<commit_before>package shh

import (
	"fmt"
	"strings"
	"time"
)

var (
	MetricNameNormalizer = strings.NewReplacer("#", "_", "-", "_")
)

type MeasurementType int

const (
	CounterType MeasurementType = iota
	GaugeType
	FloatGaugeType
)

type CounterMeasurement struct {
	time   time.Time
	poller string
	what   []string
	value  uint64
	unit   Unit
}

type GaugeMeasurement struct {
	time   time.Time
	poller string
	what   []string
	value  uint64
	unit   Unit
}

type FloatGaugeMeasurement struct {
	time   time.Time
	poller string
	what   []string
	value  float64
	unit   Unit
}

type Measurement interface {
	Name(prefix string) string // Metric name
	Value() interface{}
	StrValue() string // String representation of the value
	Time() time.Time  // the underlying time object.
	Type() MeasurementType
	Unit() Unit
}

func combinedName(prefix, poller string, what []string) string {
	v := fmt.Sprintf("%s.%s", poller, strings.Join(what, "."))
	if prefix != "" {
		v = fmt.Sprintf("%s.%s", prefix, v)
	}
	return MetricNameNormalizer.Replace(v)
}

func (c CounterMeasurement) Name(prefix string) string {
	return combinedName(prefix, c.poller, c.what)
}

func (c CounterMeasurement) StrValue() string {
	return fmt.Sprintf("%d", c.value)
}

func (c CounterMeasurement) Value() interface{} {
	return c.value
}

func (c CounterMeasurement) Time() time.Time {
	return c.time
}

func (c CounterMeasurement) Type() MeasurementType {
	return CounterType
}

func (c CounterMeasurement) Unit() Unit {
	return c.unit
}

func (c CounterMeasurement) Difference(l CounterMeasurement) uint64 {
	// This is a crappy way to handle wraps and resets when we don't know
	// what the max value is (32, 64 or 128 bit)
	// Leads to a little, loss, but should be minimal overall
	cv := c.value
	lv := l.value

	if cv < lv {
		return cv
	}
	return cv - lv
}

func (g GaugeMeasurement) Name(prefix string) string {
	return combinedName(prefix, g.poller, g.what)
}

func (g GaugeMeasurement) StrValue() string {
	return fmt.Sprintf("%d", g.value)
}

func (g GaugeMeasurement) Value() interface{} {
	return g.value
}

func (g GaugeMeasurement) Time() time.Time {
	return g.time
}

func (c GaugeMeasurement) Type() MeasurementType {
	return GaugeType
}

func (c GaugeMeasurement) Unit() Unit {
	return c.unit
}

func (g FloatGaugeMeasurement) Name(prefix string) string {
	return combinedName(prefix, g.poller, g.what)
}

func (g FloatGaugeMeasurement) StrValue() string {
	return fmt.Sprintf("%f", g.value)
}

func (g FloatGaugeMeasurement) Value() interface{} {
	return g.value
}

func (g FloatGaugeMeasurement) Time() time.Time {
	return g.time
}

func (c FloatGaugeMeasurement) Type() MeasurementType {
	return FloatGaugeType
}

func (c FloatGaugeMeasurement) Unit() Unit {
	return c.unit
}

// func (m Measurement) Timestamp() string {
// 	return m.When.Format(time.RFC3339)
// }
<commit_msg>This sucks, but we agree with it<commit_after>package shh

import (
	"fmt"
	"strings"
	"time"
)

var (
	MetricNameNormalizer = strings.NewReplacer("#", ".", "_", "-")
)

type MeasurementType int

const (
	CounterType MeasurementType = iota
	GaugeType
	FloatGaugeType
)

type CounterMeasurement struct {
	time   time.Time
	poller string
	what   []string
	value  uint64
	unit   Unit
}

type GaugeMeasurement struct {
	time   time.Time
	poller string
	what   []string
	value  uint64
	unit   Unit
}

type FloatGaugeMeasurement struct {
	time   time.Time
	poller string
	what   []string
	value  float64
	unit   Unit
}

type Measurement interface {
	Name(prefix string) string // Metric name
	Value() interface{}
	StrValue() string // String representation of the value
	Time() time.Time  // the underlying time object.
	Type() MeasurementType
	Unit() Unit
}

func combinedName(prefix, poller string, what []string) string {
	v := fmt.Sprintf("%s.%s", poller, strings.Join(what, "."))
	if prefix != "" {
		v = fmt.Sprintf("%s.%s", prefix, v)
	}
	return MetricNameNormalizer.Replace(v)
}

func (c CounterMeasurement) Name(prefix string) string {
	return combinedName(prefix, c.poller, c.what)
}

func (c CounterMeasurement) StrValue() string {
	return fmt.Sprintf("%d", c.value)
}

func (c CounterMeasurement) Value() interface{} {
	return c.value
}

func (c CounterMeasurement) Time() time.Time {
	return c.time
}

func (c CounterMeasurement) Type() MeasurementType {
	return CounterType
}

func (c CounterMeasurement) Unit() Unit {
	return c.unit
}

func (c CounterMeasurement) Difference(l CounterMeasurement) uint64 {
	// This is a crappy way to handle wraps and resets when we don't know
	// what the max value is (32, 64 or 128 bit)
	// Leads to a little, loss, but should be minimal overall
	cv := c.value
	lv := l.value

	if cv < lv {
		return cv
	}
	return cv - lv
}

func (g GaugeMeasurement) Name(prefix string) string {
	return combinedName(prefix, g.poller, g.what)
}

func (g GaugeMeasurement) StrValue() string {
	return fmt.Sprintf("%d", g.value)
}

func (g GaugeMeasurement) Value() interface{} {
	return g.value
}

func (g GaugeMeasurement) Time() time.Time {
	return g.time
}

func (c GaugeMeasurement) Type() MeasurementType {
	return GaugeType
}

func (c GaugeMeasurement) Unit() Unit {
	return c.unit
}

func (g FloatGaugeMeasurement) Name(prefix string) string {
	return combinedName(prefix, g.poller, g.what)
}

func (g FloatGaugeMeasurement) StrValue() string {
	return fmt.Sprintf("%f", g.value)
}

func (g FloatGaugeMeasurement) Value() interface{} {
	return g.value
}

func (g FloatGaugeMeasurement) Time() time.Time {
	return g.time
}

func (c FloatGaugeMeasurement) Type() MeasurementType {
	return FloatGaugeType
}

func (c FloatGaugeMeasurement) Unit() Unit {
	return c.unit
}

// func (m Measurement) Timestamp() string {
// 	return m.When.Format(time.RFC3339)
// }
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

const VERSION = `0.1`

// Flags
var (
	version = flag.Bool("V", false, "Display version information and exit")
	useTLS = flag.Bool("tls", true, "Use TLS")
)

func main() {
	flag.Parse()

	if *version {
		fmt.Printf("netcat-sec %s\n", VERSION)
	}

	args := flag.Args()
	if len(args) != 2 {
		fmt.Fprintln(os.Stderr, "Need host and port")
		return
	}
	host, port := args[0], args[1]

	var (
		conn net.Conn
		err  error
	)

	if *useTLS {
		conn, err = tls.Dial("tcp", host+":"+port, nil)
	} else {
		conn, err = net.Dial("tcp", host+":"+port)
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}

	go func() {
		r := bufio.NewReader(os.Stdin)
		for {
			l, _, err := r.ReadLine()
			if err != nil {
				os.Exit(0)
			}
			_, err = conn.Write([]byte(string(l)+"\n"))
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
		}
	}()

	go func() {
		r := bufio.NewReader(conn)
		for {
			l, _, err := r.ReadLine()
			if err != nil {
				if err == io.EOF {
					os.Exit(0)
				}
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			fmt.Println(string(l))
		}
	}()

	<-make(chan interface{})
}
<commit_msg>Fixing -h output<commit_after>package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

const VERSION = `0.1`

// Flags
var (
	version = flag.Bool("V", false, "Display version information and exit")
	useTLS = flag.Bool("tls", true, "Use TLS")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s [-tls=false] hostname port\n", os.Args[0])
	}
	flag.Parse()

	if *version {
		fmt.Printf("netcat-sec %s\n", VERSION)
	}

	args := flag.Args()
	if len(args) != 2 {
		flag.Usage()
		return
	}
	host, port := args[0], args[1]

	var (
		conn net.Conn
		err  error
	)

	if *useTLS {
		conn, err = tls.Dial("tcp", host+":"+port, nil)
	} else {
		conn, err = net.Dial("tcp", host+":"+port)
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}

	go func() {
		r := bufio.NewReader(os.Stdin)
		for {
			l, _, err := r.ReadLine()
			if err != nil {
				os.Exit(0)
			}
			_, err = conn.Write([]byte(string(l)+"\n"))
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
		}
	}()

	go func() {
		r := bufio.NewReader(conn)
		for {
			l, _, err := r.ReadLine()
			if err != nil {
				if err == io.EOF {
					os.Exit(0)
				}
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			fmt.Println(string(l))
		}
	}()

	<-make(chan interface{})
}
<|endoftext|>


<commit_before>package pg

import (
	"log"
	"os"
)

var Logger = log.New(os.Stdout, "pg: ", log.Ldate|log.Ltime)

type Fabric interface {
	New() interface{}
}

type fabricWrapper struct {
	model interface{}
}

func (f *fabricWrapper) New() interface{} {
	return f.model
}

type Appender interface {
	Append([]byte) []byte
}

// Raw query.
type Q string

func (q Q) Append(dst []byte) []byte {
	dst = append(dst, string(q)...)
	return dst
}

// SQL field.
type F string

func (f F) Append(dst []byte) []byte {
	dst = append(dst, '"')
	for _, c := range []byte(f) {
		if c == '"' {
			dst = append(dst, '"', '"')
		} else {
			dst = append(dst, c)
		}
	}
	dst = append(dst, '"')
	return dst
}
<commit_msg>Remove logger.<commit_after>package pg

type Fabric interface {
	New() interface{}
}

type fabricWrapper struct {
	model interface{}
}

func (f *fabricWrapper) New() interface{} {
	return f.model
}

type Appender interface {
	Append([]byte) []byte
}

// Raw query.
type Q string

func (q Q) Append(dst []byte) []byte {
	dst = append(dst, string(q)...)
	return dst
}

// SQL field.
type F string

func (f F) Append(dst []byte) []byte {
	dst = append(dst, '"')
	for _, c := range []byte(f) {
		if c == '"' {
			dst = append(dst, '"', '"')
		} else {
			dst = append(dst, c)
		}
	}
	dst = append(dst, '"')
	return dst
}
<|endoftext|>


<commit_before>package ql

import (
	"database/sql"
)

// Connection is a connection to the database with an EventReceiver to send events,
// errors, and timings to.
type Connection struct {
	DB *sql.DB
	EventReceiver
}

// NewConnection instantiates a Connection for a given database/sql connection
// and event receiver.
func NewConnection(db *sql.DB, log EventReceiver) *Connection {
	if log == nil {
		log = nullReceiver
	}

	return &Connection{DB: db, EventReceiver: log}
}

// Close closes the database, releasing any open resources.
func (c *Connection) Close() error {
	return c.DB.Close()
}

type runner interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
}

// Open opens a database by calling sql.Open. It returns new Connection with
// nil EventReceiver.
func Open(driverName, dataSourceName string) (*Connection, error) {
	db, err := sql.Open(driverName, dataSourceName)
	if err != nil {
		return nil, err
	}
	return NewConnection(db, nil), nil
}

// MustOpen is like Open but panics on error.
func MustOpen(driverName, dataSourceName string) *Connection {
	conn, err := Open(driverName, dataSourceName)
	if err != nil {
		panic(err)
	}
	return conn
}
<commit_msg>add MustOpenAndVerify and Ping methods<commit_after>package ql

import (
	"database/sql"
)

// Connection is a connection to the database with an EventReceiver to send events,
// errors, and timings to.
type Connection struct {
	DB *sql.DB
	EventReceiver
}

// NewConnection instantiates a Connection for a given database/sql connection
// and event receiver.
func NewConnection(db *sql.DB, log EventReceiver) *Connection {
	if log == nil {
		log = nullReceiver
	}

	return &Connection{DB: db, EventReceiver: log}
}

// Open opens a database by calling sql.Open. It returns new Connection with
// nil EventReceiver.
func Open(driverName, dataSourceName string) (*Connection, error) {
	db, err := sql.Open(driverName, dataSourceName)
	if err != nil {
		return nil, err
	}
	return NewConnection(db, nil), nil
}

// MustOpen is like Open but panics on error.
func MustOpen(driverName, dataSourceName string) *Connection {
	conn, err := Open(driverName, dataSourceName)
	if err != nil {
		panic(err)
	}
	return conn
}

// MustOpenAndVerify is like MustOpen but it verifies the connection and panics
// on error.
func MustOpenAndVerify(driverName, dataSourceName string) *Connection {
	conn := MustOpen(driverName, dataSourceName)
	if err := conn.Ping(); err != nil {
		panic(err)
	}
	return conn
}

// Close closes the database, releasing any open resources.
func (c *Connection) Close() error {
	return c.DB.Close()
}

// Ping verifies a connection to the database is still alive, establishing a connection if necessary.
func (c *Connection) Ping() error {
	return c.DB.Ping()
}

type runner interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
}
<|endoftext|>


<commit_before>package bolster

import (
	"errors"
	"fmt"
	"reflect"

	"github.com/boltdb/bolt"
	"github.com/nochso/bolster/bytesort"
	"github.com/nochso/bolster/errlist"
)

// Tx is a read-only or read-write transaction.
type Tx struct {
	store *Store
	btx   *bolt.Tx
	errs  errlist.Errors
}

type txAction int

const (
	insert txAction = iota
	update
	upsert
	delete
	get
)

var (
	// ErrNotFound is returned when a specific item could not be found.
	ErrNotFound = errors.New("item not found")
)

func (tx *Tx) validateStruct(v interface{}, action txAction) (typeInfo, reflect.Value, error) {
	rv := reflect.ValueOf(v)
	rt := rv.Type()
	if action == insert || action == update || action == upsert || action == get {
		if rt.Kind() != reflect.Ptr {
			return typeInfo{}, rv, fmt.Errorf("expected pointer to struct, got %v", rt.Kind())
		}
		rv = rv.Elem()
		rt = rv.Type()
	}
	if rt.Kind() != reflect.Struct {
		return typeInfo{}, rv, fmt.Errorf("expected pointer to struct, got pointer to %v", rt.Kind())
	}
	ti, ok := tx.store.types[rt]
	if !ok {
		return ti, rv, fmt.Errorf("unregistered struct: %v", rt)
	}
	return ti, rv, nil
}

// Truncate deletes all items of v's type.
func (tx *Tx) Truncate(v interface{}) error {
	if tx.errs.ErrorOrNil() != nil {
		return tx.errs
	}
	ti, _, err := tx.validateStruct(v, delete)
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}
	tx.errs = tx.errs.Append(tx.btx.DeleteBucket(ti.FullName))
	_, err = tx.btx.CreateBucket(ti.FullName)
	tx.errs = tx.errs.Append(err)
	return tx.errs.ErrorOrNil()
}

// Insert saves a new item.
func (tx *Tx) Insert(v interface{}) error {
	ti, rv, err := tx.validateStruct(v, insert)
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}

	bkt := tx.btx.Bucket(ti.FullName)
	if ti.AutoIncrement {
		idType := rv.Field(ti.IDField).Type()
		zero := reflect.Zero(idType).Interface()
		if zero == rv.Field(ti.IDField).Interface() {
			seq, err := bkt.NextSequence()
			if err != nil {
				tx.errs = tx.errs.Append(err)
				return err
			}
			seqRV := reflect.ValueOf(seq)
			if !seqRV.Type().ConvertibleTo(idType) {
				err = fmt.Errorf("Insert: %s: unable to convert autoincremented ID of type %s to %s", ti, seqRV.Type(), idType)
				tx.errs = tx.errs.Append(err)
				return err
			}
			var overflows bool
			switch idType.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				signedSeq := int64(seq)
				overflows = rv.Field(ti.IDField).OverflowInt(signedSeq)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				overflows = rv.Field(ti.IDField).OverflowUint(seq)
			}
			if overflows {
				err = fmt.Errorf("Insert: %s: next bucket sequence %d overflows ID field of type %s", ti, seq, idType)
				tx.errs = tx.errs.Append(err)
				return err
			}
			rv.Field(ti.IDField).Set(seqRV.Convert(idType))
		}
	}

	idBytes, err := bytesort.Encode(rv.Field(ti.IDField).Interface())
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}

	if bkt.Get(idBytes) != nil {
		err = fmt.Errorf("Insert: %s: item with ID %q already exists", ti, fmt.Sprintf("%v", rv.Field(ti.IDField).Interface()))
		tx.errs = tx.errs.Append(err)
		return err
	}

	structBytes, err := tx.store.codec.Marshal(v)
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}
	err = bkt.Put(idBytes, structBytes)
	tx.errs = tx.errs.Append(err)
	return err
}

// Get fetches v by ID.
func (tx *Tx) Get(v interface{}, id interface{}) error {
	ti, _, err := tx.validateStruct(v, get)
	if err != nil {
		return err
	}
	actTypeID := reflect.TypeOf(id)
	expTypeID := ti.Type.Field(ti.IDField).Type
	if actTypeID != expTypeID {
		return fmt.Errorf("Get: %s: incompatible type of ID: expected %v, got %v", ti, expTypeID, actTypeID)
	}
	idBytes, err := bytesort.Encode(id)
	if err != nil {
		return err
	}
	bkt := tx.btx.Bucket(ti.FullName)
	b := bkt.Get(idBytes)
	if b == nil {
		return ErrNotFound
	}
	return tx.store.codec.Unmarshal(b, v)
}
<commit_msg>Extract Tx.autoincrement<commit_after>package bolster

import (
	"errors"
	"fmt"
	"reflect"

	"github.com/boltdb/bolt"
	"github.com/nochso/bolster/bytesort"
	"github.com/nochso/bolster/errlist"
)

// Tx is a read-only or read-write transaction.
type Tx struct {
	store *Store
	btx   *bolt.Tx
	errs  errlist.Errors
}

type txAction int

const (
	insert txAction = iota
	update
	upsert
	delete
	get
)

var (
	// ErrNotFound is returned when a specific item could not be found.
	ErrNotFound = errors.New("item not found")
)

func (tx *Tx) validateStruct(v interface{}, action txAction) (typeInfo, reflect.Value, error) {
	rv := reflect.ValueOf(v)
	rt := rv.Type()
	if action == insert || action == update || action == upsert || action == get {
		if rt.Kind() != reflect.Ptr {
			return typeInfo{}, rv, fmt.Errorf("expected pointer to struct, got %v", rt.Kind())
		}
		rv = rv.Elem()
		rt = rv.Type()
	}
	if rt.Kind() != reflect.Struct {
		return typeInfo{}, rv, fmt.Errorf("expected pointer to struct, got pointer to %v", rt.Kind())
	}
	ti, ok := tx.store.types[rt]
	if !ok {
		return ti, rv, fmt.Errorf("unregistered struct: %v", rt)
	}
	return ti, rv, nil
}

// Truncate deletes all items of v's type.
func (tx *Tx) Truncate(v interface{}) error {
	if tx.errs.ErrorOrNil() != nil {
		return tx.errs
	}
	ti, _, err := tx.validateStruct(v, delete)
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}
	tx.errs = tx.errs.Append(tx.btx.DeleteBucket(ti.FullName))
	_, err = tx.btx.CreateBucket(ti.FullName)
	tx.errs = tx.errs.Append(err)
	return tx.errs.ErrorOrNil()
}

// Insert saves a new item.
func (tx *Tx) Insert(v interface{}) error {
	ti, rv, err := tx.validateStruct(v, insert)
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}

	bkt := tx.btx.Bucket(ti.FullName)
	id := rv.Field(ti.IDField)
	if ti.AutoIncrement {
		err = tx.autoincrement(id, bkt, ti)
		if err != nil {
			tx.errs = tx.errs.Append(err)
			return err
		}
	}
	idBytes, err := bytesort.Encode(id.Interface())
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}

	if bkt.Get(idBytes) != nil {
		err = fmt.Errorf("Insert: %s: item with ID %q already exists", ti, fmt.Sprintf("%v", id.Interface()))
		tx.errs = tx.errs.Append(err)
		return err
	}

	structBytes, err := tx.store.codec.Marshal(v)
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}
	err = bkt.Put(idBytes, structBytes)
	tx.errs = tx.errs.Append(err)
	return err
}

func (tx *Tx) autoincrement(id reflect.Value, bkt *bolt.Bucket, ti typeInfo) error {
	idType := id.Type()
	zero := reflect.Zero(idType).Interface()
	if id.Interface() != zero {
		return nil
	}
	seq, err := bkt.NextSequence()
	if err != nil {
		tx.errs = tx.errs.Append(err)
		return err
	}
	seqRV := reflect.ValueOf(seq)
	if !seqRV.Type().ConvertibleTo(idType) {
		err = fmt.Errorf("Insert: %s: unable to convert autoincremented ID of type %s to %s", ti, seqRV.Type(), idType)
		tx.errs = tx.errs.Append(err)
		return err
	}
	var overflows bool
	if idType.Kind() >= reflect.Int && idType.Kind() <= reflect.Int64 {
		signedSeq := int64(seq)
		overflows = id.OverflowInt(signedSeq)
	} else if idType.Kind() >= reflect.Uint && idType.Kind() <= reflect.Uint64 {
		overflows = id.OverflowUint(seq)
	}
	if overflows {
		err = fmt.Errorf("Insert: %s: next bucket sequence %d overflows ID field of type %s", ti, seq, idType)
		tx.errs = tx.errs.Append(err)
		return err
	}
	id.Set(seqRV.Convert(idType))
	return nil
}

// Get fetches v by ID.
func (tx *Tx) Get(v interface{}, id interface{}) error {
	ti, _, err := tx.validateStruct(v, get)
	if err != nil {
		return err
	}
	actTypeID := reflect.TypeOf(id)
	expTypeID := ti.Type.Field(ti.IDField).Type
	if actTypeID != expTypeID {
		return fmt.Errorf("Get: %s: incompatible type of ID: expected %v, got %v", ti, expTypeID, actTypeID)
	}
	idBytes, err := bytesort.Encode(id)
	if err != nil {
		return err
	}
	bkt := tx.btx.Bucket(ti.FullName)
	b := bkt.Get(idBytes)
	if b == nil {
		return ErrNotFound
	}
	return tx.store.codec.Unmarshal(b, v)
}
<|endoftext|>


<commit_before>package termui

import (
	"github.com/nsf/termbox-go"
	"sync"
)

var (
	quitChan   chan struct{}
	updateChan chan struct{}
	newBody    chan Element
	wgexit     *sync.WaitGroup
	// Events can be used to recieve unhandled termbox events.
	Events <-chan termbox.Event
)

// Start the UI with the given element as root.
func Start(body Element) {
	wgexit = new(sync.WaitGroup)
	wgexit.Add(1)
	termbox.Init()
	eventChan := make(chan termbox.Event)
	updateChan = make(chan struct{})
	quitChan = make(chan struct{})
	newBody = make(chan Element)
	go func() {
	loop:
		for {
			select {
			case <-quitChan:
				break loop
			default:
				eventChan <- termbox.PollEvent()
			}
		}

		close(eventChan)
	}()

	events := make(chan termbox.Event)
	Events = events

	input := newInputManager(body)

	go func() {
		defer func() {
			close(events)
			wgexit.Done()
			termbox.Close()

			close(eventChan)
			close(updateChan)
			close(newBody)
		}()

		body.Arrange(body.Measure(termbox.Size()))
		for {
			termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
			w, h := termbox.Size()
			rootrenderer.RenderChild(body, w, h, 0, 0)

			err := termbox.Flush()
			if err != nil {
				panic(err)
			}

			select {
			case <-quitChan:
				return
			case ev := <-eventChan:
				if ev.Type == termbox.EventResize {
					body.Arrange(body.Measure(termbox.Size()))
				}
				if !input.DispatchEvent(ev) {
					events <- ev
				}
			case <-updateChan:
				body.Arrange(body.Measure(termbox.Size()))
			case nb := <-newBody:
				body = nb
				body.Arrange(body.Measure(termbox.Size()))
			}

		}
	}()
}

// SetBody replaces the root element with the new body.
func SetBody(e Element) {
	go func() {
		newBody <- e
	}()
}

// Update tells the UI to update measurement and render again.
func Update() {
	go func() {
		updateChan <- struct{}{}
	}()
}

// Wait for the UI to finish.
func Wait() {
	wgexit.Wait()
}

// Stop shutsdown the UI.
func Stop() {
	go func() {
		close(quitChan)
	}()
}
<commit_msg>respect body fg / bg settings<commit_after>package termui

import (
	"github.com/nsf/termbox-go"
	"sync"
)

var (
	quitChan   chan struct{}
	updateChan chan struct{}
	newBody    chan Element
	wgexit     *sync.WaitGroup
	// Events can be used to recieve unhandled termbox events.
	Events <-chan termbox.Event
)

// Start the UI with the given element as root.
func Start(body Element) {
	wgexit = new(sync.WaitGroup)
	wgexit.Add(1)
	termbox.Init()
	eventChan := make(chan termbox.Event)
	updateChan = make(chan struct{})
	quitChan = make(chan struct{})
	newBody = make(chan Element)
	go func() {
	loop:
		for {
			select {
			case <-quitChan:
				break loop
			default:
				eventChan <- termbox.PollEvent()
			}
		}

		close(eventChan)
	}()

	events := make(chan termbox.Event)
	Events = events

	input := newInputManager(body)

	go func() {
		defer func() {
			close(events)
			wgexit.Done()
			termbox.Close()

			close(eventChan)
			close(updateChan)
			close(newBody)
		}()

		body.Arrange(body.Measure(termbox.Size()))
		for {
			termbox.Clear(ForegroundProperty.Get(nil), BackgroundProperty.Get(nil))
			w, h := termbox.Size()
			rootrenderer.RenderChild(body, w, h, 0, 0)

			err := termbox.Flush()
			if err != nil {
				panic(err)
			}

			select {
			case <-quitChan:
				return
			case ev := <-eventChan:
				if ev.Type == termbox.EventResize {
					body.Arrange(body.Measure(termbox.Size()))
				}
				if !input.DispatchEvent(ev) {
					events <- ev
				}
			case <-updateChan:
				body.Arrange(body.Measure(termbox.Size()))
			case nb := <-newBody:
				body = nb
				body.Arrange(body.Measure(termbox.Size()))
			}

		}
	}()
}

// SetBody replaces the root element with the new body.
func SetBody(e Element) {
	go func() {
		newBody <- e
	}()
}

// Update tells the UI to update measurement and render again.
func Update() {
	go func() {
		updateChan <- struct{}{}
	}()
}

// Wait for the UI to finish.
func Wait() {
	wgexit.Wait()
}

// Stop shutsdown the UI.
func Stop() {
	go func() {
		close(quitChan)
	}()
}
<|endoftext|>


<commit_before>package cache

import (
	"context"
	"encoding/json"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/weaveworks/flux/image"
	"github.com/weaveworks/flux/registry"
)

const refreshWhenExpiryWithin = time.Minute
const askForNewImagesInterval = time.Minute

// Warmer refreshes the information kept in the cache from remote
// registries.
type Warmer struct {
	clientFactory registry.ClientFactory
	cache         Client
	burst         int
	Priority      chan image.Name
	Notify        func()
}

// NewWarmer creates cache warmer that (when Loop is invoked) will
// periodically refresh the values kept in the cache.
func NewWarmer(cf registry.ClientFactory, cacheClient Client, burst int) (*Warmer, error) {
	if cf == nil || cacheClient == nil || burst <= 0 {
		return nil, errors.New("arguments must be non-nil (or > 0 in the case of burst)")
	}
	return &Warmer{
		clientFactory: cf,
		cache:         cacheClient,
		burst:         burst,
	}, nil
}

// .. and this is what we keep in the backlog
type backlogItem struct {
	image.Name
	registry.Credentials
}

// Continuously get the images to populate the cache with, and
// populate the cache with them.
func (w *Warmer) Loop(logger log.Logger, stop <-chan struct{}, wg *sync.WaitGroup, imagesToFetchFunc func() registry.ImageCreds) {
	defer wg.Done()

	refresh := time.Tick(askForNewImagesInterval)
	imageCreds := imagesToFetchFunc()
	backlog := imageCredsToBacklog(imageCreds)

	// We have some fine control over how long to spend on each fetch
	// operation, since they are given a `context`. For now though,
	// just rattle through them one by one, however long they take.
	ctx := context.Background()

	// This loop acts keeps a kind of priority queue, whereby image
	// names coming in on the `Priority` channel are looked up first.
	// If there are none, images used in the cluster are refreshed;
	// but no more often than once every `askForNewImagesInterval`,
	// since there is no effective back-pressure on cache refreshes
	// and it would spin freely otherwise).
	for {
		select {
		case <-stop:
			logger.Log("stopping", "true")
			return
		case name := <-w.Priority:
			logger.Log("priority", name.String())
			// NB the implicit contract here is that the prioritised
			// image has to have been running the last time we
			// requested the credentials.
			if creds, ok := imageCreds[name]; ok {
				w.warm(ctx, logger, name, creds)
			} else {
				logger.Log("priority", name.String(), "err", "no creds available")
			}
			continue
		default:
		}

		if len(backlog) > 0 {
			im := backlog[0]
			backlog = backlog[1:]
			w.warm(ctx, logger, im.Name, im.Credentials)
		} else {
			select {
			case <-refresh:
				imageCreds = imagesToFetchFunc()
				backlog = imageCredsToBacklog(imageCreds)
			default:
			}
		}
	}
}

func imageCredsToBacklog(imageCreds registry.ImageCreds) []backlogItem {
	backlog := make([]backlogItem, len(imageCreds))
	var i int
	for name, cred := range imageCreds {
		backlog[i] = backlogItem{name, cred}
		i++
	}
	return backlog
}

func (w *Warmer) warm(ctx context.Context, logger log.Logger, id image.Name, creds registry.Credentials) {
	client, err := w.clientFactory.ClientFor(id.CanonicalName(), creds)
	if err != nil {
		logger.Log("err", err.Error())
		return
	}

	// This is what we're going to write back to the cache
	var repo ImageRepository
	repoKey := NewRepositoryKey(id.CanonicalName())
	bytes, _, err := w.cache.GetKey(repoKey)
	if err == nil {
		err = json.Unmarshal(bytes, &repo)
	} else if err == ErrNotCached {
		err = nil
	}

	if err != nil {
		logger.Log("err", errors.Wrap(err, "fetching previous result from cache"))
		return
	}

	// Save for comparison later
	oldImages := repo.Images

	// Now we have the previous result; everything after will be
	// attempting to refresh that value. Whatever happens, at the end
	// we'll write something back.
	defer func() {
		bytes, err := json.Marshal(repo)
		if err == nil {
			err = w.cache.SetKey(repoKey, bytes)
		}
		if err != nil {
			logger.Log("err", errors.Wrap(err, "writing result to cache"))
		}
	}()

	tags, err := client.Tags(ctx)
	if err != nil {
		if !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) && !strings.Contains(err.Error(), "net/http: request canceled") {
			logger.Log("err", errors.Wrap(err, "requesting tags"))
			repo.LastError = err.Error()
		}
		return
	}

	newImages := map[string]image.Info{}

	// Create a list of manifests that need updating
	var toUpdate []image.Ref
	var missing, expired int
	for _, tag := range tags {
		// See if we have the manifest already cached
		newID := id.ToRef(tag)
		key := NewManifestKey(newID.CanonicalRef())
		bytes, expiry, err := w.cache.GetKey(key)
		// If err, then we don't have it yet. Update.
		switch {
		case err != nil:
			missing++
		case time.Until(expiry) < refreshWhenExpiryWithin:
			expired++
		default:
			var image image.Info
			if err := json.Unmarshal(bytes, &image); err == nil {
				newImages[tag] = image
				continue
			}
			missing++
		}
		toUpdate = append(toUpdate, newID)
	}

	var successCount int

	if len(toUpdate) > 0 {
		logger.Log("fetching", id.String(), "total", len(toUpdate), "expired", expired, "missing", missing)
		var successMx sync.Mutex

		// The upper bound for concurrent fetches against a single host is
		// w.Burst, so limit the number of fetching goroutines to that.
		fetchers := make(chan struct{}, w.burst)
		awaitFetchers := &sync.WaitGroup{}
	updates:
		for _, imID := range toUpdate {
			select {
			case <-ctx.Done():
				break updates
			case fetchers <- struct{}{}:
			}

			awaitFetchers.Add(1)
			go func(imageID image.Ref) {
				defer func() { awaitFetchers.Done(); <-fetchers }()
				// Get the image from the remote
				img, err := client.Manifest(ctx, imageID.Tag)
				if err != nil {
					if err, ok := errors.Cause(err).(net.Error); ok && err.Timeout() {
						// This was due to a context timeout, don't bother logging
						return
					}
					logger.Log("err", errors.Wrap(err, "requesting manifests"))
					return
				}

				key := NewManifestKey(img.ID.CanonicalRef())
				// Write back to memcached
				val, err := json.Marshal(img)
				if err != nil {
					logger.Log("err", errors.Wrap(err, "serializing tag to store in cache"))
					return
				}
				err = w.cache.SetKey(key, val)
				if err != nil {
					logger.Log("err", errors.Wrap(err, "storing manifests in cache"))
					return
				}
				successMx.Lock()
				successCount++
				newImages[imageID.Tag] = img
				successMx.Unlock()
			}(imID)
		}
		awaitFetchers.Wait()
		logger.Log("updated", id.String(), "count", successCount)
	}

	// We managed to fetch new metadata for everything we were missing
	// (if anything). Ratchet the result forward.
	if successCount == len(toUpdate) {
		repo = ImageRepository{
			LastUpdate: time.Now(),
			Images:     newImages,
		}
	}

	if w.Notify != nil {
		cacheTags := StringSet{}
		for t := range oldImages {
			cacheTags[t] = struct{}{}
		}

		// If there's more tags than there used to be, there must be
		// at least one new tag.
		if len(cacheTags) < len(tags) {
			w.Notify()
			return
		}
		// Otherwise, check whether there are any entries in the
		// fetched tags that aren't in the cached tags.
		tagSet := NewStringSet(tags)
		if !tagSet.Subset(cacheTags) {
			w.Notify()
		}
	}
}

// StringSet is a set of strings.
type StringSet map[string]struct{}

// NewStringSet returns a StringSet containing exactly the strings
// given as arguments.
func NewStringSet(ss []string) StringSet {
	res := StringSet{}
	for _, s := range ss {
		res[s] = struct{}{}
	}
	return res
}

// Subset returns true if `s` is a subset of `t` (including the case
// of having the same members).
func (s StringSet) Subset(t StringSet) bool {
	for k := range s {
		if _, ok := t[k]; !ok {
			return false
		}
	}
	return true
}
<commit_msg>Please Go's linter about comments on exported methods.<commit_after>package cache

import (
	"context"
	"encoding/json"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/weaveworks/flux/image"
	"github.com/weaveworks/flux/registry"
)

const refreshWhenExpiryWithin = time.Minute
const askForNewImagesInterval = time.Minute

// Warmer refreshes the information kept in the cache from remote
// registries.
type Warmer struct {
	clientFactory registry.ClientFactory
	cache         Client
	burst         int
	Priority      chan image.Name
	Notify        func()
}

// NewWarmer creates cache warmer that (when Loop is invoked) will
// periodically refresh the values kept in the cache.
func NewWarmer(cf registry.ClientFactory, cacheClient Client, burst int) (*Warmer, error) {
	if cf == nil || cacheClient == nil || burst <= 0 {
		return nil, errors.New("arguments must be non-nil (or > 0 in the case of burst)")
	}
	return &Warmer{
		clientFactory: cf,
		cache:         cacheClient,
		burst:         burst,
	}, nil
}

// .. and this is what we keep in the backlog
type backlogItem struct {
	image.Name
	registry.Credentials
}

// Loop continuously gets the images to populate the cache with,
// and populate the cache with them.
func (w *Warmer) Loop(logger log.Logger, stop <-chan struct{}, wg *sync.WaitGroup, imagesToFetchFunc func() registry.ImageCreds) {
	defer wg.Done()

	refresh := time.Tick(askForNewImagesInterval)
	imageCreds := imagesToFetchFunc()
	backlog := imageCredsToBacklog(imageCreds)

	// We have some fine control over how long to spend on each fetch
	// operation, since they are given a `context`. For now though,
	// just rattle through them one by one, however long they take.
	ctx := context.Background()

	// This loop acts keeps a kind of priority queue, whereby image
	// names coming in on the `Priority` channel are looked up first.
	// If there are none, images used in the cluster are refreshed;
	// but no more often than once every `askForNewImagesInterval`,
	// since there is no effective back-pressure on cache refreshes
	// and it would spin freely otherwise).
	for {
		select {
		case <-stop:
			logger.Log("stopping", "true")
			return
		case name := <-w.Priority:
			logger.Log("priority", name.String())
			// NB the implicit contract here is that the prioritised
			// image has to have been running the last time we
			// requested the credentials.
			if creds, ok := imageCreds[name]; ok {
				w.warm(ctx, logger, name, creds)
			} else {
				logger.Log("priority", name.String(), "err", "no creds available")
			}
			continue
		default:
		}

		if len(backlog) > 0 {
			im := backlog[0]
			backlog = backlog[1:]
			w.warm(ctx, logger, im.Name, im.Credentials)
		} else {
			select {
			case <-refresh:
				imageCreds = imagesToFetchFunc()
				backlog = imageCredsToBacklog(imageCreds)
			default:
			}
		}
	}
}

func imageCredsToBacklog(imageCreds registry.ImageCreds) []backlogItem {
	backlog := make([]backlogItem, len(imageCreds))
	var i int
	for name, cred := range imageCreds {
		backlog[i] = backlogItem{name, cred}
		i++
	}
	return backlog
}

func (w *Warmer) warm(ctx context.Context, logger log.Logger, id image.Name, creds registry.Credentials) {
	client, err := w.clientFactory.ClientFor(id.CanonicalName(), creds)
	if err != nil {
		logger.Log("err", err.Error())
		return
	}

	// This is what we're going to write back to the cache
	var repo ImageRepository
	repoKey := NewRepositoryKey(id.CanonicalName())
	bytes, _, err := w.cache.GetKey(repoKey)
	if err == nil {
		err = json.Unmarshal(bytes, &repo)
	} else if err == ErrNotCached {
		err = nil
	}

	if err != nil {
		logger.Log("err", errors.Wrap(err, "fetching previous result from cache"))
		return
	}

	// Save for comparison later
	oldImages := repo.Images

	// Now we have the previous result; everything after will be
	// attempting to refresh that value. Whatever happens, at the end
	// we'll write something back.
	defer func() {
		bytes, err := json.Marshal(repo)
		if err == nil {
			err = w.cache.SetKey(repoKey, bytes)
		}
		if err != nil {
			logger.Log("err", errors.Wrap(err, "writing result to cache"))
		}
	}()

	tags, err := client.Tags(ctx)
	if err != nil {
		if !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) && !strings.Contains(err.Error(), "net/http: request canceled") {
			logger.Log("err", errors.Wrap(err, "requesting tags"))
			repo.LastError = err.Error()
		}
		return
	}

	newImages := map[string]image.Info{}

	// Create a list of manifests that need updating
	var toUpdate []image.Ref
	var missing, expired int
	for _, tag := range tags {
		// See if we have the manifest already cached
		newID := id.ToRef(tag)
		key := NewManifestKey(newID.CanonicalRef())
		bytes, expiry, err := w.cache.GetKey(key)
		// If err, then we don't have it yet. Update.
		switch {
		case err != nil:
			missing++
		case time.Until(expiry) < refreshWhenExpiryWithin:
			expired++
		default:
			var image image.Info
			if err := json.Unmarshal(bytes, &image); err == nil {
				newImages[tag] = image
				continue
			}
			missing++
		}
		toUpdate = append(toUpdate, newID)
	}

	var successCount int

	if len(toUpdate) > 0 {
		logger.Log("fetching", id.String(), "total", len(toUpdate), "expired", expired, "missing", missing)
		var successMx sync.Mutex

		// The upper bound for concurrent fetches against a single host is
		// w.Burst, so limit the number of fetching goroutines to that.
		fetchers := make(chan struct{}, w.burst)
		awaitFetchers := &sync.WaitGroup{}
	updates:
		for _, imID := range toUpdate {
			select {
			case <-ctx.Done():
				break updates
			case fetchers <- struct{}{}:
			}

			awaitFetchers.Add(1)
			go func(imageID image.Ref) {
				defer func() { awaitFetchers.Done(); <-fetchers }()
				// Get the image from the remote
				img, err := client.Manifest(ctx, imageID.Tag)
				if err != nil {
					if err, ok := errors.Cause(err).(net.Error); ok && err.Timeout() {
						// This was due to a context timeout, don't bother logging
						return
					}
					logger.Log("err", errors.Wrap(err, "requesting manifests"))
					return
				}

				key := NewManifestKey(img.ID.CanonicalRef())
				// Write back to memcached
				val, err := json.Marshal(img)
				if err != nil {
					logger.Log("err", errors.Wrap(err, "serializing tag to store in cache"))
					return
				}
				err = w.cache.SetKey(key, val)
				if err != nil {
					logger.Log("err", errors.Wrap(err, "storing manifests in cache"))
					return
				}
				successMx.Lock()
				successCount++
				newImages[imageID.Tag] = img
				successMx.Unlock()
			}(imID)
		}
		awaitFetchers.Wait()
		logger.Log("updated", id.String(), "count", successCount)
	}

	// We managed to fetch new metadata for everything we were missing
	// (if anything). Ratchet the result forward.
	if successCount == len(toUpdate) {
		repo = ImageRepository{
			LastUpdate: time.Now(),
			Images:     newImages,
		}
	}

	if w.Notify != nil {
		cacheTags := StringSet{}
		for t := range oldImages {
			cacheTags[t] = struct{}{}
		}

		// If there's more tags than there used to be, there must be
		// at least one new tag.
		if len(cacheTags) < len(tags) {
			w.Notify()
			return
		}
		// Otherwise, check whether there are any entries in the
		// fetched tags that aren't in the cached tags.
		tagSet := NewStringSet(tags)
		if !tagSet.Subset(cacheTags) {
			w.Notify()
		}
	}
}

// StringSet is a set of strings.
type StringSet map[string]struct{}

// NewStringSet returns a StringSet containing exactly the strings
// given as arguments.
func NewStringSet(ss []string) StringSet {
	res := StringSet{}
	for _, s := range ss {
		res[s] = struct{}{}
	}
	return res
}

// Subset returns true if `s` is a subset of `t` (including the case
// of having the same members).
func (s StringSet) Subset(t StringSet) bool {
	for k := range s {
		if _, ok := t[k]; !ok {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/docker/distribution/registry/api/v2"
)

// UnexpectedHTTPStatusError is returned when an unexpected HTTP status is
// returned when making a registry api call.
type UnexpectedHTTPStatusError struct {
	Status string
}

func (e *UnexpectedHTTPStatusError) Error() string {
	return fmt.Sprintf("Received unexpected HTTP status: %s", e.Status)
}

// UnexpectedHTTPResponseError is returned when an expected HTTP status code
// is returned, but the content was unexpected and failed to be parsed.
type UnexpectedHTTPResponseError struct {
	ParseErr error
	Response []byte
}

func (e *UnexpectedHTTPResponseError) Error() string {
	shortenedResponse := string(e.Response)
	if len(shortenedResponse) > 15 {
		shortenedResponse = shortenedResponse[:12] + "..."
	}
	return fmt.Sprintf("Error parsing HTTP response: %s: %q", e.ParseErr.Error(), shortenedResponse)
}

func parseHTTPErrorResponse(r io.Reader) error {
	var errors v2.Errors
	body, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(body, &errors); err != nil {
		return &UnexpectedHTTPResponseError{
			ParseErr: err,
			Response: body,
		}
	}
	return &errors
}

func handleErrorResponse(resp *http.Response) error {
	if resp.StatusCode == 401 {
		err := parseHTTPErrorResponse(resp.Body)
		if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
			return &v2.Error{
				Code:    v2.ErrorCodeUnauthorized,
				Message: "401 Unauthorized",
				Detail:  uErr.Response,
			}
		}
		return err
	}
	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		return parseHTTPErrorResponse(resp.Body)
	}
	return &UnexpectedHTTPStatusError{Status: resp.Status}
}
<commit_msg>Remove error message shortening<commit_after>package client

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/docker/distribution/registry/api/v2"
)

// UnexpectedHTTPStatusError is returned when an unexpected HTTP status is
// returned when making a registry api call.
type UnexpectedHTTPStatusError struct {
	Status string
}

func (e *UnexpectedHTTPStatusError) Error() string {
	return fmt.Sprintf("Received unexpected HTTP status: %s", e.Status)
}

// UnexpectedHTTPResponseError is returned when an expected HTTP status code
// is returned, but the content was unexpected and failed to be parsed.
type UnexpectedHTTPResponseError struct {
	ParseErr error
	Response []byte
}

func (e *UnexpectedHTTPResponseError) Error() string {
	return fmt.Sprintf("Error parsing HTTP response: %s: %q", e.ParseErr.Error(), string(e.Response))
}

func parseHTTPErrorResponse(r io.Reader) error {
	var errors v2.Errors
	body, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(body, &errors); err != nil {
		return &UnexpectedHTTPResponseError{
			ParseErr: err,
			Response: body,
		}
	}
	return &errors
}

func handleErrorResponse(resp *http.Response) error {
	if resp.StatusCode == 401 {
		err := parseHTTPErrorResponse(resp.Body)
		if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
			return &v2.Error{
				Code:    v2.ErrorCodeUnauthorized,
				Message: "401 Unauthorized",
				Detail:  uErr.Response,
			}
		}
		return err
	}
	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		return parseHTTPErrorResponse(resp.Body)
	}
	return &UnexpectedHTTPStatusError{Status: resp.Status}
}
<|endoftext|>


<commit_before>package alu

import (
    "log"
    "runtime"
	"os"
	"path/filepath"
)

func Caller() string {
    pc, _, _, ok := runtime.Caller(1)
    if !ok {
	    return "Unknown"
    } else {
	    return runtime.FuncForPC(pc).Name()
    }
}

func InitLog(name string) {
    log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)

	if len(name) == 0 {
		_, file := filepath.Split(os.Args[0])
		name = file + ".log"
	}

	f, err := os.OpenFile(name, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0600)
	if err != nil {
		log.Printf("%s has error, %s", Caller(), err.Error())
		return
	}

	log.SetOutput(f)
}

func ResetLog() {
	log.SetOutput(os.Stdout)
    log.SetFlags(log.LstdFlags)
}

func ToDateString(d *time.Time) {
    return fmt.Sprintf("%d-%02d-%02d", date.Year(), date.Month(), date.Day())
}

func ToDateTimeString(d *time.Time) {
    return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second())
}
<commit_msg>Fixed statement bug.<commit_after>package alu

import (
    "log"
    "runtime"
    "os"
    "path/filepath"
    "time"
    "fmt"
)

func Caller() string {
    pc, _, _, ok := runtime.Caller(1)
    if !ok {
	    return "Unknown"
    } else {
	    return runtime.FuncForPC(pc).Name()
    }
}

func InitLog(name string) {
    log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)

	if len(name) == 0 {
		_, file := filepath.Split(os.Args[0])
		name = file + ".log"
	}

	f, err := os.OpenFile(name, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0600)
	if err != nil {
		log.Printf("%s has error, %s", Caller(), err.Error())
		return
	}

	log.SetOutput(f)
}

func ResetLog() {
	log.SetOutput(os.Stdout)
    log.SetFlags(log.LstdFlags)
}

func ToDateString(date *time.Time) string {
    return fmt.Sprintf("%d-%02d-%02d", date.Year(), date.Month(), date.Day())
}

func ToDateTimeString(date *time.Time) string {
    return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second())
}
<|endoftext|>


<commit_before>// Package stockfighter provides a simple wrapper for the Stockfighter API:
//
// https://www.stockfighter.io/
//
// https://starfighter.readme.io/
package stockfighter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httputil"

	"github.com/gorilla/websocket"
)

func apiUrl(path string, args ...interface{}) string {
	return fmt.Sprintf("https://api.stockfighter.io/ob/api/"+path, args...)
}

func gmUrl(path string, args ...interface{}) string {
	return fmt.Sprintf("https://api.stockfighter.io/gm/"+path, args...)
}

func wsUrl(path string, args ...interface{}) string {
	return fmt.Sprintf("wss://api.stockfighter.io/ob/ws/"+path, args...)
}

type apiCall interface {
	Err() error
}

type response struct {
	Ok    bool
	Error string
}

func (r response) Err() error {
	if len(r.Error) > 0 {
		return fmt.Errorf(r.Error)
	}
	return nil
}

type venueResponse struct {
	response
	Venue string
}

type stocksResponse struct {
	response
	Symbols []Symbol
}

type orderBookResponse struct {
	response
	OrderBook
}

type quoteResponse struct {
	response
	Quote
}

type orderResponse struct {
	response
	OrderState
}

type bulkOrderResponse struct {
	response
	Venue  string
	Orders []OrderState
}

type quoteMessage struct {
	Ok bool
	Quote
}

type executionMessage struct {
	Ok bool
	Execution
}

type gameResponse struct {
	response
	Game
}

type gameStateResponse struct {
	response
	GameState
}

type Stockfighter struct {
	apiKey string
	debug  bool
}

// Create new Stockfighter API instance.
// If debug is true, log all HTTP requests and responses.
func NewStockfighter(apiKey string, debug bool) *Stockfighter {
	return &Stockfighter{
		apiKey: apiKey,
		debug:  debug,
	}
}

// Check the API Is Up. If venue is a non-empty string, then check that venue.
// Returns nil if ok, otherwise the error indicates the problem.
func (sf *Stockfighter) Heartbeat(venue string) error {
	var resp response
	url := apiUrl("heartbeat")
	if len(venue) > 0 {
		url = apiUrl("venues/%s/heartbeat", venue)
	}
	return sf.do("GET", url, nil, &resp)
}

// Get the stocks available for trading on a venue.
func (sf *Stockfighter) Stocks(venue string) ([]Symbol, error) {
	var resp stocksResponse
	url := apiUrl("venues/%s/stocks", venue)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return resp.Symbols, nil
}

// Get the orderbook for a particular stock.
func (sf *Stockfighter) OrderBook(venue, stock string) (*OrderBook, error) {
	var resp orderBookResponse
	url := apiUrl("venues/%s/stocks/%s", venue, stock)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.OrderBook, nil
}

// Get a quick look at the most recent trade information for a stock.
func (sf *Stockfighter) Quote(venue, stock string) (*Quote, error) {
	var resp quoteResponse
	url := apiUrl("venues/%s/stocks/%s/quote", venue, stock)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.Quote, nil
}

// Place an order
func (sf *Stockfighter) Place(order *Order) (*OrderState, error) {
	body, err := encodeJson(order)
	if err != nil {
		return nil, err
	}
	var resp orderResponse
	url := apiUrl("venues/%s/stocks/%s/orders", order.Venue, order.Stock)
	if err := sf.do("POST", url, body, &resp); err != nil {
		return nil, err
	}
	return &resp.OrderState, nil
}

// Get the status for an existing order.
func (sf *Stockfighter) Status(venue, stock string, id uint64) (*OrderState, error) {
	var resp orderResponse
	url := apiUrl("venues/%s/stocks/%s/orders/%d", venue, stock, id)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.OrderState, nil
}

// Get the statuses for all an account's orders of a stock on a venue.
// If stock is a non-empty string, only statuses for that stock are returned
func (sf *Stockfighter) StockStatus(account, venue, stock string) ([]OrderState, error) {
	url := apiUrl("venues/%s/accounts/%s/orders", venue, account)
	if len(stock) > 0 {
		url = apiUrl("venues/%s/accounts/%s/stocks/%s/orders", venue, account, stock)
	}
	var resp bulkOrderResponse
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return resp.Orders, nil
}

// Cancel an existing order
func (sf *Stockfighter) Cancel(venue, stock string, id uint64) error {
	var resp response
	url := apiUrl("venues/%s/stocks/%s/orders/%d", venue, stock, id)
	return sf.do("DELETE", url, nil, &resp)
}

// Subscribe to a stream of quotes for a venue.
// If stock is a non-empy string, only quotes for that stock are returned.
func (sf *Stockfighter) Quotes(account, venue, stock string) (chan *Quote, error) {
	url := wsUrl("%s/venues/%s/tickertape", account, venue)
	if len(stock) > 0 {
		url = wsUrl("%s/venues/%s/stocks/%s/tickertape", account, venue, stock)
	}
	c := make(chan *Quote)
	return c, sf.pump(url, func(conn *websocket.Conn) error {
		var quote quoteMessage
		if err := conn.ReadJSON(&quote); err != nil {
			close(c)
			return err
		}
		if quote.Ok {
			c <- &quote.Quote
		}
		return nil
	})
}

// Subscribe to a stream of executions for a venue.
// If stock is a non-empy string, only executions for that stock are returned.
func (sf *Stockfighter) Executions(account, venue, stock string) (chan *Execution, error) {
	url := wsUrl("%s/venues/%s/executions", account, venue)
	if len(stock) > 0 {
		url = wsUrl("%s/venues/%s/stocks/%s/executions", account, venue, stock)
	}
	c := make(chan *Execution)
	return c, sf.pump(url, func(conn *websocket.Conn) error {
		var execution executionMessage
		if err := conn.ReadJSON(&execution); err != nil {
			close(c)
			return err
		}
		if execution.Ok {
			c <- &execution.Execution
		}
		return nil
	})
}

// Start a new level.
func (sf *Stockfighter) Start(level string) (*Game, error) {
	var resp gameResponse
	url := gmUrl("levels/%s", level)
	if err := sf.do("POST", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.Game, nil
}

// Restart a level using the instance id from a previously started Game.
func (sf *Stockfighter) Restart(id uint64) error {
	var resp response
	url := gmUrl("instances/%d/restart", id)
	return sf.do("POST", url, nil, &resp)
}

// Resume a level using the instance id from a previously started Game.
func (sf *Stockfighter) Resume(id uint64) error {
	var resp response
	url := gmUrl("instances/%d/resume", id)
	return sf.do("POST", url, nil, &resp)
}

// Stop a level using the instance id from a previously started Game.
func (sf *Stockfighter) Stop(id uint64) error {
	var resp response
	url := gmUrl("instances/%d/stop", id)
	return sf.do("POST", url, nil, &resp)
}

// Get the GameState using the instance id from a previously started Game.
func (sf *Stockfighter) GameStatus(id uint64) (*GameState, error) {
	var resp gameStateResponse
	url := gmUrl("instances/%d", id)
	if err := sf.do("Get", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.GameState, nil
}

func encodeJson(v interface{}) (io.Reader, error) {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(v); err != nil {
		return nil, err
	}
	return &buf, nil
}

func (sf *Stockfighter) do(method, url string, body io.Reader, value apiCall) error {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}
	req.Header.Add("X-Starfighter-Authorization", sf.apiKey)
	if sf.debug {
		out, _ := httputil.DumpRequest(req, true)
		log.Println(string(out))
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if sf.debug {
		out, _ := httputil.DumpResponse(resp, true)
		log.Println(string(out))
	}
	if err := json.NewDecoder(resp.Body).Decode(value); err != nil {
		return err
	}
	if err := value.Err(); err != nil {
		return err
	}
	return nil
}

func (sf *Stockfighter) pump(url string, f func(*websocket.Conn) error) error {
	conn, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		return err
	}
	go func() {
		defer conn.Close()
		for err := f(conn); err == nil; err = f(conn) {
		}
	}()
	return nil
}
<commit_msg>avoid nil error check<commit_after>// Package stockfighter provides a simple wrapper for the Stockfighter API:
//
// https://www.stockfighter.io/
//
// https://starfighter.readme.io/
package stockfighter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httputil"

	"github.com/gorilla/websocket"
)

func apiUrl(path string, args ...interface{}) string {
	return fmt.Sprintf("https://api.stockfighter.io/ob/api/"+path, args...)
}

func gmUrl(path string, args ...interface{}) string {
	return fmt.Sprintf("https://api.stockfighter.io/gm/"+path, args...)
}

func wsUrl(path string, args ...interface{}) string {
	return fmt.Sprintf("wss://api.stockfighter.io/ob/ws/"+path, args...)
}

type apiCall interface {
	Err() error
}

type response struct {
	Ok    bool
	Error string
}

func (r response) Err() error {
	if len(r.Error) > 0 {
		return fmt.Errorf(r.Error)
	}
	return nil
}

type venueResponse struct {
	response
	Venue string
}

type stocksResponse struct {
	response
	Symbols []Symbol
}

type orderBookResponse struct {
	response
	OrderBook
}

type quoteResponse struct {
	response
	Quote
}

type orderResponse struct {
	response
	OrderState
}

type bulkOrderResponse struct {
	response
	Venue  string
	Orders []OrderState
}

type quoteMessage struct {
	Ok bool
	Quote
}

type executionMessage struct {
	Ok bool
	Execution
}

type gameResponse struct {
	response
	Game
}

type gameStateResponse struct {
	response
	GameState
}

type Stockfighter struct {
	apiKey string
	debug  bool
}

// Create new Stockfighter API instance.
// If debug is true, log all HTTP requests and responses.
func NewStockfighter(apiKey string, debug bool) *Stockfighter {
	return &Stockfighter{
		apiKey: apiKey,
		debug:  debug,
	}
}

// Check the API Is Up. If venue is a non-empty string, then check that venue.
// Returns nil if ok, otherwise the error indicates the problem.
func (sf *Stockfighter) Heartbeat(venue string) error {
	var resp response
	url := apiUrl("heartbeat")
	if len(venue) > 0 {
		url = apiUrl("venues/%s/heartbeat", venue)
	}
	return sf.do("GET", url, nil, &resp)
}

// Get the stocks available for trading on a venue.
func (sf *Stockfighter) Stocks(venue string) ([]Symbol, error) {
	var resp stocksResponse
	url := apiUrl("venues/%s/stocks", venue)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return resp.Symbols, nil
}

// Get the orderbook for a particular stock.
func (sf *Stockfighter) OrderBook(venue, stock string) (*OrderBook, error) {
	var resp orderBookResponse
	url := apiUrl("venues/%s/stocks/%s", venue, stock)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.OrderBook, nil
}

// Get a quick look at the most recent trade information for a stock.
func (sf *Stockfighter) Quote(venue, stock string) (*Quote, error) {
	var resp quoteResponse
	url := apiUrl("venues/%s/stocks/%s/quote", venue, stock)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.Quote, nil
}

// Place an order
func (sf *Stockfighter) Place(order *Order) (*OrderState, error) {
	body, err := encodeJson(order)
	if err != nil {
		return nil, err
	}
	var resp orderResponse
	url := apiUrl("venues/%s/stocks/%s/orders", order.Venue, order.Stock)
	if err := sf.do("POST", url, body, &resp); err != nil {
		return nil, err
	}
	return &resp.OrderState, nil
}

// Get the status for an existing order.
func (sf *Stockfighter) Status(venue, stock string, id uint64) (*OrderState, error) {
	var resp orderResponse
	url := apiUrl("venues/%s/stocks/%s/orders/%d", venue, stock, id)
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.OrderState, nil
}

// Get the statuses for all an account's orders of a stock on a venue.
// If stock is a non-empty string, only statuses for that stock are returned
func (sf *Stockfighter) StockStatus(account, venue, stock string) ([]OrderState, error) {
	url := apiUrl("venues/%s/accounts/%s/orders", venue, account)
	if len(stock) > 0 {
		url = apiUrl("venues/%s/accounts/%s/stocks/%s/orders", venue, account, stock)
	}
	var resp bulkOrderResponse
	if err := sf.do("GET", url, nil, &resp); err != nil {
		return nil, err
	}
	return resp.Orders, nil
}

// Cancel an existing order
func (sf *Stockfighter) Cancel(venue, stock string, id uint64) error {
	var resp response
	url := apiUrl("venues/%s/stocks/%s/orders/%d", venue, stock, id)
	return sf.do("DELETE", url, nil, &resp)
}

// Subscribe to a stream of quotes for a venue.
// If stock is a non-empy string, only quotes for that stock are returned.
func (sf *Stockfighter) Quotes(account, venue, stock string) (chan *Quote, error) {
	url := wsUrl("%s/venues/%s/tickertape", account, venue)
	if len(stock) > 0 {
		url = wsUrl("%s/venues/%s/stocks/%s/tickertape", account, venue, stock)
	}
	c := make(chan *Quote)
	return c, sf.pump(url, func(conn *websocket.Conn) error {
		var quote quoteMessage
		if err := conn.ReadJSON(&quote); err != nil {
			close(c)
			return err
		}
		if quote.Ok {
			c <- &quote.Quote
		}
		return nil
	})
}

// Subscribe to a stream of executions for a venue.
// If stock is a non-empy string, only executions for that stock are returned.
func (sf *Stockfighter) Executions(account, venue, stock string) (chan *Execution, error) {
	url := wsUrl("%s/venues/%s/executions", account, venue)
	if len(stock) > 0 {
		url = wsUrl("%s/venues/%s/stocks/%s/executions", account, venue, stock)
	}
	c := make(chan *Execution)
	return c, sf.pump(url, func(conn *websocket.Conn) error {
		var execution executionMessage
		if err := conn.ReadJSON(&execution); err != nil {
			close(c)
			return err
		}
		if execution.Ok {
			c <- &execution.Execution
		}
		return nil
	})
}

// Start a new level.
func (sf *Stockfighter) Start(level string) (*Game, error) {
	var resp gameResponse
	url := gmUrl("levels/%s", level)
	if err := sf.do("POST", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.Game, nil
}

// Restart a level using the instance id from a previously started Game.
func (sf *Stockfighter) Restart(id uint64) error {
	var resp response
	url := gmUrl("instances/%d/restart", id)
	return sf.do("POST", url, nil, &resp)
}

// Resume a level using the instance id from a previously started Game.
func (sf *Stockfighter) Resume(id uint64) error {
	var resp response
	url := gmUrl("instances/%d/resume", id)
	return sf.do("POST", url, nil, &resp)
}

// Stop a level using the instance id from a previously started Game.
func (sf *Stockfighter) Stop(id uint64) error {
	var resp response
	url := gmUrl("instances/%d/stop", id)
	return sf.do("POST", url, nil, &resp)
}

// Get the GameState using the instance id from a previously started Game.
func (sf *Stockfighter) GameStatus(id uint64) (*GameState, error) {
	var resp gameStateResponse
	url := gmUrl("instances/%d", id)
	if err := sf.do("Get", url, nil, &resp); err != nil {
		return nil, err
	}
	return &resp.GameState, nil
}

func encodeJson(v interface{}) (io.Reader, error) {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(v); err != nil {
		return nil, err
	}
	return &buf, nil
}

func (sf *Stockfighter) do(method, url string, body io.Reader, value apiCall) error {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}
	req.Header.Add("X-Starfighter-Authorization", sf.apiKey)
	if sf.debug {
		out, _ := httputil.DumpRequest(req, true)
		log.Println(string(out))
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if sf.debug {
		out, _ := httputil.DumpResponse(resp, true)
		log.Println(string(out))
	}
	if err := json.NewDecoder(resp.Body).Decode(value); err != nil {
		return err
	}
	return value.Err()
}

func (sf *Stockfighter) pump(url string, f func(*websocket.Conn) error) error {
	conn, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		return err
	}
	go func() {
		defer conn.Close()
		for err := f(conn); err == nil; err = f(conn) {
		}
	}()
	return nil
}
<|endoftext|>


<commit_before>package apiGatewayDeploy

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

const (
	RESPONSE_STATUS_SUCCESS = "SUCCESS"
	RESPONSE_STATUS_FAIL    = "FAIL"

	// todo: add error codes where this is used
	ERROR_CODE_TODO = 0
)

var (
	deploymentsChanged = make(chan string)
	addSubscriber      = make(chan chan string)
	eTag               int64
)

type errorResponse struct {
	ErrorCode int    `json:"errorCode"`
	Reason    string `json:"reason"`
}

type ApiDeployment struct {
	ID               string          `json:"id"`
	ScopeId          string          `json:"scopeId"`
	Created          string          `json:"created"`
	CreatedBy        string          `json:"createdBy"`
	Updated          string          `json:"updated"`
	UpdatedBy        string          `json:"updatedBy"`
	ConfigJson       json.RawMessage `json:"configuration"`
	BundleConfigJson json.RawMessage `json:"bundleConfiguration"`
	DisplayName      string          `json:"displayName"`
	URI              string          `json:"uri"`
}

// sent to client
type ApiDeploymentResponse []ApiDeployment

type apiDeploymentResult struct {
	ID        string `json:"id"`
	Status    string `json:"status"`
	ErrorCode int    `json:"errorCode"`
	Message   string `json:"message"`
}

// received from client
type apiDeploymentResults []apiDeploymentResult

const deploymentsEndpoint = "/deployments"

func InitAPI() {
	services.API().HandleFunc(deploymentsEndpoint, apiGetCurrentDeployments).Methods("GET")
	services.API().HandleFunc(deploymentsEndpoint, apiSetDeploymentResults).Methods("PUT")
}

func writeError(w http.ResponseWriter, status int, code int, reason string) {
	w.WriteHeader(status)
	e := errorResponse{
		ErrorCode: code,
		Reason:    reason,
	}
	bytes, err := json.Marshal(e)
	if err != nil {
		log.Errorf("unable to marshal errorResponse: %v", err)
	} else {
		w.Write(bytes)
	}
	log.Debugf("sending %d error to client: %s", status, reason)
}

func writeDatabaseError(w http.ResponseWriter) {
	writeError(w, http.StatusInternalServerError, ERROR_CODE_TODO, "database error")
}

func distributeEvents() {
	subscribers := make(map[chan string]struct{})
	mut := sync.Mutex{}
	msg := ""
	debouncer := func() {
		select {
		case <-time.After(debounceDuration):
			mut.Lock()
			subs := subscribers
			subscribers = make(map[chan string]struct{})
			m := msg
			msg = ""
			incrementETag()
			mut.Unlock()
			log.Debugf("Delivering deployment change %s to %d subscribers", m, len(subs))
			for subscriber := range subs {
				select {
				case subscriber <- m:
					log.Debugf("Handling deploy response for: %s", m)
					log.Debugf("delivering TO: %v", subscriber)
				default:
					log.Debugf("listener too far behind, message dropped")
				}
			}
		}
	}
	for {
		select {
		case newMsg := <-deploymentsChanged:
			mut.Lock()
			log.Debug("deploymentsChanged")
			if msg == "" {
				go debouncer()
			}
			msg = newMsg
			mut.Unlock()
		case subscriber := <-addSubscriber:
			log.Debugf("Add subscriber: %v", subscriber)
			mut.Lock()
			subscribers[subscriber] = struct{}{}
			mut.Unlock()
		}
	}
}

func apiGetCurrentDeployments(w http.ResponseWriter, r *http.Request) {

	// If returning without a bundle (immediately or after timeout), status = 404
	// If returning If-None-Match value is equal to current deployment, status = 304
	// If returning a new value, status = 200

	// If timeout > 0 AND there is no deployment (or new deployment) available (per If-None-Match), then
	// block for up to the specified number of seconds until a new deployment becomes available.
	b := r.URL.Query().Get("block")
	var timeout int
	if b != "" {
		var err error
		timeout, err = strconv.Atoi(b)
		if err != nil {
			writeError(w, http.StatusBadRequest, ERROR_CODE_TODO, "bad block value, must be number of seconds")
			return
		}
	}
	log.Debugf("api timeout: %d", timeout)

	// If If-None-Match header matches the ETag of current bundle list AND if the request does NOT have a 'block'
	// query param > 0, the server returns a 304 Not Modified response indicating that the client already has the
	// most recent bundle list.
	ifNoneMatch := r.Header.Get("If-None-Match")
	log.Debugf("if-none-match: %s", ifNoneMatch)

	// send unmodified if matches prior eTag and no timeout
	eTag := getETag()
	if eTag == ifNoneMatch && timeout == 0 {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	// subscribe to new deployments in case we need it
	var gotNewDeployment chan string
	if timeout > 0 && ifNoneMatch != "" {
		gotNewDeployment = make(chan string)
		addSubscriber <- gotNewDeployment
	}

	deployments, err := getReadyDeployments()
	if err != nil {
		writeDatabaseError(w)
		return
	}

	// send not found if no timeout
	if len(deployments) == 0 && timeout == 0 {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	// send results if different eTag
	if eTag != ifNoneMatch {
		sendDeployments(w, deployments, eTag)
		return
	}

	log.Debug("Blocking request... Waiting for new Deployments.")

	select {
	case <-gotNewDeployment:
		apiGetCurrentDeployments(w, r) // recurse

	case <-time.After(time.Duration(timeout) * time.Second):
		log.Debug("Blocking deployment request timed out.")
		if ifNoneMatch != "" {
			w.WriteHeader(http.StatusNotModified)
		} else {
			w.WriteHeader(http.StatusNotFound)
		}
	}
}

func sendDeployments(w http.ResponseWriter, dataDeps []DataDeployment, eTag string) {

	var apiDeps ApiDeploymentResponse

	for _, d := range dataDeps {
		apiDeps = append(apiDeps, ApiDeployment{
			ID:               d.ID,
			ScopeId:          d.DataScopeID,
			Created:          d.Created,
			CreatedBy:        d.CreatedBy,
			Updated:          d.Updated,
			UpdatedBy:        d.UpdatedBy,
			BundleConfigJson: []byte(d.BundleConfigJSON),
			ConfigJson:       []byte(d.ConfigJSON),
			DisplayName:      d.BundleName,
			URI:              d.LocalBundleURI,
		})
	}

	b, err := json.Marshal(apiDeps)
	if err != nil {
		log.Errorf("unable to marshal deployments: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	log.Debugf("sending deployment %s: %s", eTag, b)
	w.Header().Set("ETag", eTag)
	w.Write(b)
}

func apiSetDeploymentResults(w http.ResponseWriter, r *http.Request) {

	var results apiDeploymentResults
	buf, _ := ioutil.ReadAll(r.Body)
	err := json.Unmarshal(buf, &results)
	if err != nil {
		log.Errorf("Resp Handler Json Unmarshal err: ", err)
		writeError(w, http.StatusBadRequest, ERROR_CODE_TODO, "Malformed JSON")
		return
	}

	// validate the results
	// todo: these errors to the client should be standardized
	var errs bytes.Buffer
	var validResults apiDeploymentResults
	for i, result := range results {
		valid := true
		if result.ID == "" {
			errs.WriteString(fmt.Sprintf("Missing id at %d\n", i))
		}

		if result.Status != RESPONSE_STATUS_SUCCESS && result.Status != RESPONSE_STATUS_FAIL {
			errs.WriteString(fmt.Sprintf("status must be '%s' or '%s' at %d\n",
				RESPONSE_STATUS_SUCCESS, RESPONSE_STATUS_FAIL, i))
		}

		if result.Status == RESPONSE_STATUS_FAIL {
			if result.ErrorCode == 0 {
				errs.WriteString(fmt.Sprintf("errorCode is required for status == fail at %d\n", i))
			}
			if result.Message == "" {
				errs.WriteString(fmt.Sprintf("message are required for status == fail at %d\n", i))
			}
		}

		if valid {
			validResults = append(validResults, result)
		}
	}

	if errs.Len() > 0 {
		writeError(w, http.StatusBadRequest, ERROR_CODE_TODO, errs.String())
		return
	}

	if len(validResults) > 0 {
		go transmitDeploymentResultsToServer(validResults)
		setDeploymentResults(validResults)
	}

	w.Write([]byte("OK"))
}

func addHeaders(req *http.Request) {
	var token = services.Config().GetString("apigeesync_bearer_token")
	req.Header.Add("Authorization", "Bearer "+token)
}

func transmitDeploymentResultsToServer(validResults apiDeploymentResults) error {

	retryIn := bundleRetryDelay
	maxBackOff := 5 * time.Minute
	backOffFunc := createBackoff(retryIn, maxBackOff)

	uri, err := url.Parse(apiServerBaseURI.String())
	if err != nil {
		log.Errorf("unable to parse apiServerBaseURI %s: %v", apiServerBaseURI.String(), err)
		return err
	}
	uri.Path = fmt.Sprintf("/apidtracker/clusters/%s/apids/%s/deployments", apidClusterID, apidInstanceID)

	resultJSON, err := json.Marshal(validResults)
	if err != nil {
		log.Errorf("unable to marshal deployment results %v: %v", validResults, err)
		return err
	}

	for {
		log.Debugf("transmitting deployment results to tracker by URL=%s data=%s",uri.String(), string(resultJSON))
		req, err := http.NewRequest("PUT", uri.String(), bytes.NewReader(resultJSON))
		req.Header.Add("Content-Type", "application/json")
		addHeaders(req)

		resp, err := http.DefaultClient.Do(req)
		defer resp.Body.Close();
		if err != nil || resp.StatusCode != http.StatusOK {
			if err != nil {
				log.Errorf("failed to communicate with tracking service: %v", err)
			} else {
				b, _ := ioutil.ReadAll(resp.Body)
				log.Errorf("tracking service call failed to %s , code: %d, body: %s", uri.String(), resp.StatusCode, string(b))
			}
			backOffFunc()
			continue
		}
		b, _ := ioutil.ReadAll(resp.Body)
		log.Debugf("tracking service returned %s , code: %d, body: %s", uri.String(), resp.StatusCode, string(b))
		return nil
	}
}

// call whenever the list of deployments changes
func incrementETag() {
	atomic.AddInt64(&eTag, 1)
}

func getETag() string {
	e := atomic.LoadInt64(&eTag)
	return strconv.FormatInt(e, 10)
}
<commit_msg>added more logging<commit_after>package apiGatewayDeploy

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

const (
	RESPONSE_STATUS_SUCCESS = "SUCCESS"
	RESPONSE_STATUS_FAIL    = "FAIL"

	// todo: add error codes where this is used
	ERROR_CODE_TODO = 0
)

var (
	deploymentsChanged = make(chan string)
	addSubscriber      = make(chan chan string)
	eTag               int64
)

type errorResponse struct {
	ErrorCode int    `json:"errorCode"`
	Reason    string `json:"reason"`
}

type ApiDeployment struct {
	ID               string          `json:"id"`
	ScopeId          string          `json:"scopeId"`
	Created          string          `json:"created"`
	CreatedBy        string          `json:"createdBy"`
	Updated          string          `json:"updated"`
	UpdatedBy        string          `json:"updatedBy"`
	ConfigJson       json.RawMessage `json:"configuration"`
	BundleConfigJson json.RawMessage `json:"bundleConfiguration"`
	DisplayName      string          `json:"displayName"`
	URI              string          `json:"uri"`
}

// sent to client
type ApiDeploymentResponse []ApiDeployment

type apiDeploymentResult struct {
	ID        string `json:"id"`
	Status    string `json:"status"`
	ErrorCode int    `json:"errorCode"`
	Message   string `json:"message"`
}

// received from client
type apiDeploymentResults []apiDeploymentResult

const deploymentsEndpoint = "/deployments"

func InitAPI() {
	services.API().HandleFunc(deploymentsEndpoint, apiGetCurrentDeployments).Methods("GET")
	services.API().HandleFunc(deploymentsEndpoint, apiSetDeploymentResults).Methods("PUT")
}

func writeError(w http.ResponseWriter, status int, code int, reason string) {
	w.WriteHeader(status)
	e := errorResponse{
		ErrorCode: code,
		Reason:    reason,
	}
	bytes, err := json.Marshal(e)
	if err != nil {
		log.Errorf("unable to marshal errorResponse: %v", err)
	} else {
		w.Write(bytes)
	}
	log.Debugf("sending %d error to client: %s", status, reason)
}

func writeDatabaseError(w http.ResponseWriter) {
	writeError(w, http.StatusInternalServerError, ERROR_CODE_TODO, "database error")
}

func distributeEvents() {
	subscribers := make(map[chan string]struct{})
	mut := sync.Mutex{}
	msg := ""
	debouncer := func() {
		select {
		case <-time.After(debounceDuration):
			mut.Lock()
			subs := subscribers
			subscribers = make(map[chan string]struct{})
			m := msg
			msg = ""
			incrementETag()
			mut.Unlock()
			log.Debugf("Delivering deployment change %s to %d subscribers", m, len(subs))
			for subscriber := range subs {
				select {
				case subscriber <- m:
					log.Debugf("Handling deploy response for: %s", m)
					log.Debugf("delivering TO: %v", subscriber)
				default:
					log.Debugf("listener too far behind, message dropped")
				}
			}
		}
	}
	for {
		select {
		case newMsg := <-deploymentsChanged:
			mut.Lock()
			log.Debug("deploymentsChanged")
			if msg == "" {
				go debouncer()
			}
			msg = newMsg
			mut.Unlock()
		case subscriber := <-addSubscriber:
			log.Debugf("Add subscriber: %v", subscriber)
			mut.Lock()
			subscribers[subscriber] = struct{}{}
			mut.Unlock()
		}
	}
}

func apiGetCurrentDeployments(w http.ResponseWriter, r *http.Request) {

	// If returning without a bundle (immediately or after timeout), status = 404
	// If returning If-None-Match value is equal to current deployment, status = 304
	// If returning a new value, status = 200

	// If timeout > 0 AND there is no deployment (or new deployment) available (per If-None-Match), then
	// block for up to the specified number of seconds until a new deployment becomes available.
	b := r.URL.Query().Get("block")
	var timeout int
	if b != "" {
		var err error
		timeout, err = strconv.Atoi(b)
		if err != nil {
			writeError(w, http.StatusBadRequest, ERROR_CODE_TODO, "bad block value, must be number of seconds")
			return
		}
	}
	log.Debugf("api timeout: %d", timeout)

	// If If-None-Match header matches the ETag of current bundle list AND if the request does NOT have a 'block'
	// query param > 0, the server returns a 304 Not Modified response indicating that the client already has the
	// most recent bundle list.
	ifNoneMatch := r.Header.Get("If-None-Match")
	log.Debugf("if-none-match: %s", ifNoneMatch)

	// send unmodified if matches prior eTag and no timeout
	eTag := getETag()
	if eTag == ifNoneMatch && timeout == 0 {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	// subscribe to new deployments in case we need it
	var gotNewDeployment chan string
	if timeout > 0 && ifNoneMatch != "" {
		gotNewDeployment = make(chan string)
		addSubscriber <- gotNewDeployment
	}

	deployments, err := getReadyDeployments()
	if err != nil {
		writeDatabaseError(w)
		return
	}

	// send not found if no timeout
	if len(deployments) == 0 && timeout == 0 {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	// send results if different eTag
	if eTag != ifNoneMatch {
		sendDeployments(w, deployments, eTag)
		return
	}

	log.Debug("Blocking request... Waiting for new Deployments.")

	select {
	case <-gotNewDeployment:
		apiGetCurrentDeployments(w, r) // recurse

	case <-time.After(time.Duration(timeout) * time.Second):
		log.Debug("Blocking deployment request timed out.")
		if ifNoneMatch != "" {
			w.WriteHeader(http.StatusNotModified)
		} else {
			w.WriteHeader(http.StatusNotFound)
		}
	}
}

func sendDeployments(w http.ResponseWriter, dataDeps []DataDeployment, eTag string) {

	var apiDeps ApiDeploymentResponse

	for _, d := range dataDeps {
		apiDeps = append(apiDeps, ApiDeployment{
			ID:               d.ID,
			ScopeId:          d.DataScopeID,
			Created:          d.Created,
			CreatedBy:        d.CreatedBy,
			Updated:          d.Updated,
			UpdatedBy:        d.UpdatedBy,
			BundleConfigJson: []byte(d.BundleConfigJSON),
			ConfigJson:       []byte(d.ConfigJSON),
			DisplayName:      d.BundleName,
			URI:              d.LocalBundleURI,
		})
	}

	b, err := json.Marshal(apiDeps)
	if err != nil {
		log.Errorf("unable to marshal deployments: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	log.Debugf("sending deployment %s: %s", eTag, b)
	w.Header().Set("ETag", eTag)
	w.Write(b)
}

func apiSetDeploymentResults(w http.ResponseWriter, r *http.Request) {

	var results apiDeploymentResults
	buf, _ := ioutil.ReadAll(r.Body)
	err := json.Unmarshal(buf, &results)
	if err != nil {
		log.Errorf("Resp Handler Json Unmarshal err: ", err)
		writeError(w, http.StatusBadRequest, ERROR_CODE_TODO, "Malformed JSON")
		return
	}

	// validate the results
	// todo: these errors to the client should be standardized
	var errs bytes.Buffer
	var validResults apiDeploymentResults
	for i, result := range results {
		valid := true
		if result.ID == "" {
			errs.WriteString(fmt.Sprintf("Missing id at %d\n", i))
		}

		if result.Status != RESPONSE_STATUS_SUCCESS && result.Status != RESPONSE_STATUS_FAIL {
			errs.WriteString(fmt.Sprintf("status must be '%s' or '%s' at %d\n",
				RESPONSE_STATUS_SUCCESS, RESPONSE_STATUS_FAIL, i))
		}

		if result.Status == RESPONSE_STATUS_FAIL {
			if result.ErrorCode == 0 {
				errs.WriteString(fmt.Sprintf("errorCode is required for status == fail at %d\n", i))
			}
			if result.Message == "" {
				errs.WriteString(fmt.Sprintf("message are required for status == fail at %d\n", i))
			}
		}

		if valid {
			validResults = append(validResults, result)
		}
	}

	if errs.Len() > 0 {
		writeError(w, http.StatusBadRequest, ERROR_CODE_TODO, errs.String())
		return
	}

	if len(validResults) > 0 {
		go transmitDeploymentResultsToServer(validResults)
		setDeploymentResults(validResults)
	}

	w.Write([]byte("OK"))
}

func addHeaders(req *http.Request) {
	var token = services.Config().GetString("apigeesync_bearer_token")
	req.Header.Add("Authorization", "Bearer "+token)
}

func transmitDeploymentResultsToServer(validResults apiDeploymentResults) error {

	retryIn := bundleRetryDelay
	maxBackOff := 5 * time.Minute
	backOffFunc := createBackoff(retryIn, maxBackOff)

	_, err := url.Parse(apiServerBaseURI.String())
	if err != nil {
		log.Errorf("unable to parse apiServerBaseURI %s: %v", apiServerBaseURI.String(), err)
		return err
	}
	apiPath := fmt.Sprintf("%s/apidtracker/clusters/%s/apids/%s/deployments",apiServerBaseURI.String(), apidClusterID, apidInstanceID)

	resultJSON, err := json.Marshal(validResults)
	if err != nil {
		log.Errorf("unable to marshal deployment results %v: %v", validResults, err)
		return err
	}

	for {
		log.Debugf("transmitting deployment results to tracker by URL=%s data=%s",apiPath, string(resultJSON))
		req, err := http.NewRequest("PUT", apiPath, bytes.NewReader(resultJSON))
		req.Header.Add("Content-Type", "application/json")
		addHeaders(req)

		resp, err := http.DefaultClient.Do(req)
		defer resp.Body.Close();
		if err != nil || resp.StatusCode != http.StatusOK {
			if err != nil {
				log.Errorf("failed to communicate with tracking service: %v", err)
			} else {
				b, _ := ioutil.ReadAll(resp.Body)
				log.Errorf("tracking service call failed to %s , code: %d, body: %s", apiPath, resp.StatusCode, string(b))
			}
			backOffFunc()
			continue
		}
		b, _ := ioutil.ReadAll(resp.Body)
		log.Debugf("tracking service returned %s , code: %d, body: %s", apiPath, resp.StatusCode, string(b))
		return nil
	}
}

// call whenever the list of deployments changes
func incrementETag() {
	atomic.AddInt64(&eTag, 1)
}

func getETag() string {
	e := atomic.LoadInt64(&eTag)
	return strconv.FormatInt(e, 10)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/csv"
	"fmt"
	"html/template"
	"io"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

type Api struct {
	db *gorm.DB
}

func InitApi(engine *gin.Engine, db *gorm.DB) *Api {
	a := &Api{}
	a.db = db

	engine.LoadHTMLGlob("templates/*")

	engine.GET("/", a.WUI)
	engine.POST("/sms", a.AddSMS)
	engine.GET("/sms", a.GetSMS)
	engine.GET("/search", a.SearchSMS)
	engine.GET("/export", a.ExportUI)
	engine.GET("/export/do", a.DoExport)

	return a
}

func (a *Api) WUI(c *gin.Context) {
	smss, errcode := a.getSms(c)
	if errcode > 0 {
		if errcode == 404 {
			c.HTML(404, "index.tmpl", gin.H{
				"NotFound": true,
			})
			return
		} else {
			c.AbortWithStatus(errcode)
		}
	}

	var err error
	var start, limit int64
	var sstart, slimit, q string
	q = c.Query("q")

	smscount := a.countSms(q)

	sstart = c.Query("start")
	slimit = c.Query("limit")

	if sstart == "" {
		start = 0
	} else {
		start, err = strconv.ParseInt(sstart, 10, 64)
		if err != nil {
			stringWebError(c, 400, "start must be an integer")
			return
		}
	}

	if slimit == "" {
		limit = 50
	} else {
		limit, err = strconv.ParseInt(slimit, 10, 64)
		if err != nil {
			stringWebError(c, 400, "limit must be an integer")
			return
		}
	}

	morePrev := start > 0
	moreNext := (start + limit) < int64(smscount)

	if q != "" {
		q = fmt.Sprintf("&q=%s", template.URLQueryEscaper(q))
	}

	log.Debugf("start: %v, limit %v, moreNext: %v, morePrev: %v, smsCount: %d", start, limit, moreNext, morePrev, smscount)

	c.HTML(200, "index.tmpl", gin.H{
		"SMS":       smss,
		"Count":     smscount,
		"MorePrev":  morePrev,
		"MoreNext":  moreNext,
		"Limit":     limit,
		"StartNext": start + limit,
		"StartPrev": start - limit,
		"Query":     template.URL(q),
	})
	return
}

func (a *Api) ExportUI(c *gin.Context) {
	c.HTML(200, "export.tmpl", gin.H{"ExportPage": true})
	return
}

func (a *Api) DoExport(c *gin.Context) {
	sto := c.Query("to")
	sfrom := c.Query("from")
	q := c.Query("q")

	log.Debugf("Export request: From: %s, To: %s, Query: %s", sfrom, sto, q)

	var err1, err2 error
	var to, from time.Time
	if sto == "" {
		to = time.Time{}
	} else {
		to, err1 = time.Parse("2006-01-02", sto)
	}

	if sfrom != "" {
		from, err2 = time.Parse("2006-01-02", sfrom)
	} else {
		from = time.Time{}
	}

	if err1 != nil || err2 != nil {
		stringWebError(c, 400, "Incorrect date format, must be YYYY-MM-DD. %s %s", err1, err2)
		return
	}

	buf := bytes.Buffer{}
	a.export(&buf, to, from, q)

	var qs, tos, froms string
	if q != "" {
		qs = fmt.Sprintf("_%s", strings.Replace(q, " ", "_", -1))
	}
	if to.IsZero() {
		tos = time.Now().Format("2006-01-02")
	} else {
		tos = sto
	}
	if !from.IsZero() {
		froms = fmt.Sprintf("%s_", sfrom)
	}

	c.Header("Content-Disposition", fmt.Sprintf("inline; filename=\"sms-%s%s%s.csv\"", froms, tos, qs))
	c.Data(200, "text/csv; charset=utf-8", buf.Bytes())
}

func (a *Api) AddSMS(c *gin.Context) {
	log.Debug("Adding entry...")

	message := c.PostForm("message")
	mtime := c.PostForm("time")
	mdate := c.PostForm("date")
	from := c.PostForm("from")

	if message == "" {
		stringWebError(c, 400, "Message data missing")
		return
	}

	log.Infof("SMS Received: Time: %s, Date: %s, From: %s, Message: %s", mtime, mdate, from, message)

	timestamp, err := time.Parse("02-01-2006 15.04", fmt.Sprintf("%s %s", mdate, mtime))
	if err != nil {
		stringWebError(c, 400, "Failed to parse time")
		return
	}

	hasher := sha256.New()
	hasher.Write([]byte(timestamp.Format("2006-01-02 15:04:05")))
	hasher.Write([]byte(from))
	hasher.Write([]byte(message))

	sms := &Sms{
		Timestamp: timestamp,
		From:      from,
		Message:   message,
		Hash:      fmt.Sprintf("%x", hasher.Sum(nil)),
	}

	tx := a.db.Begin()

	if err := tx.Create(sms).Error; err != nil {
		tx.Rollback()
		stringWebError(c, 500, "Error creating entry: %s", err)
		return
	}

	tx.Commit()

	log.Debugf("Saved to database")
	c.String(201, "Stored")
	return
}

func (a *Api) GetSMS(c *gin.Context) {
	log.Debug("Getting SMS's...")

	smss, errcode := a.getSms(c)
	if errcode > 0 {
		if errcode == 404 {
			jsonWebError(c, errcode, "Record not found.")
		} else {
			jsonWebError(c, errcode, "Error, please see logs.")
		}
		return
	}

	c.JSON(200, smss)
	return
}

func (a *Api) SearchSMS(c *gin.Context) {
	log.Debug("Searching...")

	query := c.Query("q")
	start := c.Query("start")
	limit := c.Query("limit")

	if query == "" {
		jsonWebError(c, 400, "Bad request, missing query")
		return
	}

	if start == "" {
		start = "0"
	}

	if limit == "" {
		limit = "50"
	}

	smss := make([]*Sms, 0)
	a.db.Offset(start).Limit(limit).Where("message LIKE ?", fmt.Sprintf("%% %s %%", query)).Find(&smss)

	log.Infof("Search for '%s' returned %d results, returning up to %s to client from offset %s", query, len(smss), limit, start)

	c.JSON(200, smss)
	return
}

func (a *Api) countSms(q string) int {
	var count int
	if q == "" {
		a.db.Table("sms").Count(&count)
	} else {
		a.db.Table("sms").Where("message LIKE ?", fmt.Sprintf("%% %s %%", q)).Count(&count)
	}
	return count
}

func (a *Api) getSms(c *gin.Context) ([]*Sms, int) {
	start := c.Query("start")
	limit := c.Query("limit")
	id := c.Query("id")
	q := c.Query("q")
	hash := c.Query("hash")

	var smss []*Sms

	if id != "" || hash != "" {
		sms := Sms{}

		if id != "" {
			if a.db.Find(&sms, "id = ?", id).RecordNotFound() {
				return nil, 404
			}
		}

		if hash != "" {
			if a.db.Find(&sms, "hash = ?", hash).RecordNotFound() {
				return nil, 404
			}
		}

		log.Infof("Retrieved SMS with ID %s", id)
		smss = make([]*Sms, 1)
		smss[0] = &sms
		return smss, 0
	}

	if start == "" {
		start = "0"
	}

	if limit == "" {
		limit = "50"
	}

	if q != "" {
		if a.db.Offset(start).Limit(limit).Order("timestamp desc").Where("message LIKE ?", fmt.Sprintf("%% %s %%", q)).Find(&smss).RecordNotFound() {
			return nil, 404
		}
	} else {
		if a.db.Offset(start).Limit(limit).Order("timestamp desc").Find(&smss).RecordNotFound() {
			return nil, 404
		}
	}

	if len(smss) == 0 {
		return nil, 404
	}

	log.Infof("Retrieved %d results, returning up to %s to client from offset %s", len(smss), limit, start)

	return smss, 0
}

func (a *Api) export(out io.Writer, from, to time.Time, query string) error {
	w := csv.NewWriter(out)

	if to.IsZero() && !from.IsZero() {
		to = time.Now()
	}

	var smss []*Sms

	if query != "" {
		if !from.IsZero() {
			a.db.Where("timestamp < ? AND timestamp > ? AND message LIKE ?", from, to, fmt.Sprintf("%% %s %%", query)).Find(&smss)
		} else {
			a.db.Where("message LIKE ?", fmt.Sprintf("%% %s %%", query)).Find(&smss)
		}
	} else {
		if !from.IsZero() {
			a.db.Where("timestamp < ? AND timestamp > ?", from, to).Find(&smss)
		} else {
			a.db.Find(&smss)
		}
	}

	record := make([]string, 3)
	for i := range smss {
		record[0] = smss[i].Timestamp.Format("2006-01-02 15:04")
		record[1] = smss[i].From
		record[2] = smss[i].Message
		err := w.Write(record)
		if err != nil {
			return err
		}
	}
	w.Flush()
	return nil
}
<commit_msg>Use ILIKE instead of LIKE for searching<commit_after>package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/csv"
	"fmt"
	"html/template"
	"io"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

type Api struct {
	db *gorm.DB
}

func InitApi(engine *gin.Engine, db *gorm.DB) *Api {
	a := &Api{}
	a.db = db

	engine.LoadHTMLGlob("templates/*")

	engine.GET("/", a.WUI)
	engine.POST("/sms", a.AddSMS)
	engine.GET("/sms", a.GetSMS)
	engine.GET("/search", a.SearchSMS)
	engine.GET("/export", a.ExportUI)
	engine.GET("/export/do", a.DoExport)

	return a
}

func (a *Api) WUI(c *gin.Context) {
	smss, errcode := a.getSms(c)
	if errcode > 0 {
		if errcode == 404 {
			c.HTML(404, "index.tmpl", gin.H{
				"NotFound": true,
			})
			return
		} else {
			c.AbortWithStatus(errcode)
		}
	}

	var err error
	var start, limit int64
	var sstart, slimit, q string
	q = c.Query("q")

	smscount := a.countSms(q)

	sstart = c.Query("start")
	slimit = c.Query("limit")

	if sstart == "" {
		start = 0
	} else {
		start, err = strconv.ParseInt(sstart, 10, 64)
		if err != nil {
			stringWebError(c, 400, "start must be an integer")
			return
		}
	}

	if slimit == "" {
		limit = 50
	} else {
		limit, err = strconv.ParseInt(slimit, 10, 64)
		if err != nil {
			stringWebError(c, 400, "limit must be an integer")
			return
		}
	}

	morePrev := start > 0
	moreNext := (start + limit) < int64(smscount)

	if q != "" {
		q = fmt.Sprintf("&q=%s", template.URLQueryEscaper(q))
	}

	log.Debugf("start: %v, limit %v, moreNext: %v, morePrev: %v, smsCount: %d", start, limit, moreNext, morePrev, smscount)

	c.HTML(200, "index.tmpl", gin.H{
		"SMS":       smss,
		"Count":     smscount,
		"MorePrev":  morePrev,
		"MoreNext":  moreNext,
		"Limit":     limit,
		"StartNext": start + limit,
		"StartPrev": start - limit,
		"Query":     template.URL(q),
	})
	return
}

func (a *Api) ExportUI(c *gin.Context) {
	c.HTML(200, "export.tmpl", gin.H{"ExportPage": true})
	return
}

func (a *Api) DoExport(c *gin.Context) {
	sto := c.Query("to")
	sfrom := c.Query("from")
	q := c.Query("q")

	log.Debugf("Export request: From: %s, To: %s, Query: %s", sfrom, sto, q)

	var err1, err2 error
	var to, from time.Time
	if sto == "" {
		to = time.Time{}
	} else {
		to, err1 = time.Parse("2006-01-02", sto)
	}

	if sfrom != "" {
		from, err2 = time.Parse("2006-01-02", sfrom)
	} else {
		from = time.Time{}
	}

	if err1 != nil || err2 != nil {
		stringWebError(c, 400, "Incorrect date format, must be YYYY-MM-DD. %s %s", err1, err2)
		return
	}

	buf := bytes.Buffer{}
	a.export(&buf, to, from, q)

	var qs, tos, froms string
	if q != "" {
		qs = fmt.Sprintf("_%s", strings.Replace(q, " ", "_", -1))
	}
	if to.IsZero() {
		tos = time.Now().Format("2006-01-02")
	} else {
		tos = sto
	}
	if !from.IsZero() {
		froms = fmt.Sprintf("%s_", sfrom)
	}

	c.Header("Content-Disposition", fmt.Sprintf("inline; filename=\"sms-%s%s%s.csv\"", froms, tos, qs))
	c.Data(200, "text/csv; charset=utf-8", buf.Bytes())
}

func (a *Api) AddSMS(c *gin.Context) {
	log.Debug("Adding entry...")

	message := c.PostForm("message")
	mtime := c.PostForm("time")
	mdate := c.PostForm("date")
	from := c.PostForm("from")

	if message == "" {
		stringWebError(c, 400, "Message data missing")
		return
	}

	log.Infof("SMS Received: Time: %s, Date: %s, From: %s, Message: %s", mtime, mdate, from, message)

	timestamp, err := time.Parse("02-01-2006 15.04", fmt.Sprintf("%s %s", mdate, mtime))
	if err != nil {
		stringWebError(c, 400, "Failed to parse time")
		return
	}

	hasher := sha256.New()
	hasher.Write([]byte(timestamp.Format("2006-01-02 15:04:05")))
	hasher.Write([]byte(from))
	hasher.Write([]byte(message))

	sms := &Sms{
		Timestamp: timestamp,
		From:      from,
		Message:   message,
		Hash:      fmt.Sprintf("%x", hasher.Sum(nil)),
	}

	tx := a.db.Begin()

	if err := tx.Create(sms).Error; err != nil {
		tx.Rollback()
		stringWebError(c, 500, "Error creating entry: %s", err)
		return
	}

	tx.Commit()

	log.Debugf("Saved to database")
	c.String(201, "Stored")
	return
}

func (a *Api) GetSMS(c *gin.Context) {
	log.Debug("Getting SMS's...")

	smss, errcode := a.getSms(c)
	if errcode > 0 {
		if errcode == 404 {
			jsonWebError(c, errcode, "Record not found.")
		} else {
			jsonWebError(c, errcode, "Error, please see logs.")
		}
		return
	}

	c.JSON(200, smss)
	return
}

func (a *Api) SearchSMS(c *gin.Context) {
	log.Debug("Searching...")

	query := c.Query("q")
	start := c.Query("start")
	limit := c.Query("limit")

	if query == "" {
		jsonWebError(c, 400, "Bad request, missing query")
		return
	}

	if start == "" {
		start = "0"
	}

	if limit == "" {
		limit = "50"
	}

	smss := make([]*Sms, 0)
	a.db.Offset(start).Limit(limit).Where("message ILIKE ?", fmt.Sprintf("%% %s %%", query)).Find(&smss)

	log.Infof("Search for '%s' returned %d results, returning up to %s to client from offset %s", query, len(smss), limit, start)

	c.JSON(200, smss)
	return
}

func (a *Api) countSms(q string) int {
	var count int
	if q == "" {
		a.db.Table("sms").Count(&count)
	} else {
		a.db.Table("sms").Where("message ILIKE ?", fmt.Sprintf("%% %s %%", q)).Count(&count)
	}
	return count
}

func (a *Api) getSms(c *gin.Context) ([]*Sms, int) {
	start := c.Query("start")
	limit := c.Query("limit")
	id := c.Query("id")
	q := c.Query("q")
	hash := c.Query("hash")

	var smss []*Sms

	if id != "" || hash != "" {
		sms := Sms{}

		if id != "" {
			if a.db.Find(&sms, "id = ?", id).RecordNotFound() {
				return nil, 404
			}
		}

		if hash != "" {
			if a.db.Find(&sms, "hash = ?", hash).RecordNotFound() {
				return nil, 404
			}
		}

		log.Infof("Retrieved SMS with ID %s", id)
		smss = make([]*Sms, 1)
		smss[0] = &sms
		return smss, 0
	}

	if start == "" {
		start = "0"
	}

	if limit == "" {
		limit = "50"
	}

	if q != "" {
		if a.db.Offset(start).Limit(limit).Order("timestamp desc").Where("message ILIKE ?", fmt.Sprintf("%% %s %%", q)).Find(&smss).RecordNotFound() {
			return nil, 404
		}
	} else {
		if a.db.Offset(start).Limit(limit).Order("timestamp desc").Find(&smss).RecordNotFound() {
			return nil, 404
		}
	}

	if len(smss) == 0 {
		return nil, 404
	}

	log.Infof("Retrieved %d results, returning up to %s to client from offset %s", len(smss), limit, start)

	return smss, 0
}

func (a *Api) export(out io.Writer, from, to time.Time, query string) error {
	w := csv.NewWriter(out)

	if to.IsZero() && !from.IsZero() {
		to = time.Now()
	}

	var smss []*Sms

	if query != "" {
		if !from.IsZero() {
			a.db.Where("timestamp < ? AND timestamp > ? AND message ILIKE ?", from, to, fmt.Sprintf("%% %s %%", query)).Find(&smss)
		} else {
			a.db.Where("message ILIKE ?", fmt.Sprintf("%% %s %%", query)).Find(&smss)
		}
	} else {
		if !from.IsZero() {
			a.db.Where("timestamp < ? AND timestamp > ?", from, to).Find(&smss)
		} else {
			a.db.Find(&smss)
		}
	}

	record := make([]string, 3)
	for i := range smss {
		record[0] = smss[i].Timestamp.Format("2006-01-02 15:04")
		record[1] = smss[i].From
		record[2] = smss[i].Message
		err := w.Write(record)
		if err != nil {
			return err
		}
	}
	w.Flush()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

type Program struct {
    Title    string `json:"title"`
    Id       int    `json:"id"`
    ImageUrl string `json:"image_url"`
}

type ProgramCollection []Program

type Workout struct {
    ImageUrl           string `json:"image_url"`
    WorkoutDescription string `json:"workout_description"`
    Title              string `json:"title"`
    ProgramIDs         []int  `json:"program_ids"`
    TrainerName        string `json:"trainer_name"`
}

type WorkoutCollection []Workout

func getPrograms() ProgramCollection {
    program_url := "http://dbios.herokuapp.com/programs"
    res, err := http.Get(program_url)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }

    /* result := ProgramCollection */
    var result ProgramCollection
    if err := json.Unmarshal(body, &result); err != nil {
        panic(err)
    }
    return result
}

func main() {
    bleh := getPrograms()
    fmt.Println(bleh[0].Title)
}
<commit_msg>we dont like comments<commit_after>package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

type Program struct {
    Title    string `json:"title"`
    Id       int    `json:"id"`
    ImageUrl string `json:"image_url"`
}

type ProgramCollection []Program

type Workout struct {
    ImageUrl           string `json:"image_url"`
    WorkoutDescription string `json:"workout_description"`
    Title              string `json:"title"`
    ProgramIDs         []int  `json:"program_ids"`
    TrainerName        string `json:"trainer_name"`
}

type WorkoutCollection []Workout

func getPrograms() ProgramCollection {
    program_url := "http://dbios.herokuapp.com/programs"
    res, err := http.Get(program_url)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }

    var result ProgramCollection
    if err := json.Unmarshal(body, &result); err != nil {
        panic(err)
    }
    return result
}

func main() {
    bleh := getPrograms()
    fmt.Println(bleh[0].Title)
}
<|endoftext|>