<commit_before>package gox12

import (
	"bytes"
	"fmt"
	"strings"
)

type Segment struct {
	SegmentId  string
	Composites [][]string
}

type ElementValue struct {
	X12Path X12Path
	Value   string
}

func NewSegment(line string, elementTerm byte, subelementTerm byte, repTerm byte) Segment {
	fields := strings.Split(line, string(elementTerm))
	segmentId := fields[0]
	comps := make([][]string, len(fields)-1)
	for i, v := range fields[1:] {
		c := strings.Split(v, string(subelementTerm))
		comps[i] = c
	}
	return Segment{segmentId, comps}
}

// Acts like golang maps, if not found, returns default value with found==false
// X12 Path indices are 1-based
func (s *Segment) GetValue(x12path string) (val string, found bool, err error) {
	var xpath *X12Path
	if xpath, err = ParseX12Path(x12path); err != nil {
		return "", false, err
	}
	if xpath.SegmentId != "" && s.SegmentId != xpath.SegmentId {
		return "", false, fmt.Errorf("Looking for Segment ID[%s], mine is [%s]", xpath.SegmentId, s.SegmentId)
	}
	if xpath.ElementIdx == 0 {
		return "", false, fmt.Errorf("No element index specified for [%s]", x12path)
	}
	myEleIdx := xpath.ElementIdx - 1
	var mySubeleIdx int
	if xpath.SubelementIdx == 0 {
		if myEleIdx < len(s.Composites) && len(s.Composites[myEleIdx]) > 1 {
			return "", false, fmt.Errorf("This is a composite but no sub-element index was specified for [%s]", x12path)
		}
		mySubeleIdx = 0
	} else {
		mySubeleIdx = xpath.SubelementIdx - 1
	}
	if myEleIdx < len(s.Composites) {
		if mySubeleIdx < len(s.Composites[myEleIdx]) {
			return s.Composites[myEleIdx][mySubeleIdx], true, nil
		}
	}
	return "", false, nil
}

func (s *Segment) GetAllValues() <-chan ElementValue {
	ch := make(chan ElementValue)
	go func() {
		for i, comp := range s.Composites {
			for j, elem := range comp {
				x12path := X12Path{SegmentId: s.SegmentId, ElementIdx: i + 1, SubelementIdx: j + 1}
				ev := ElementValue{x12path, elem}
				//ch <- new(ElementValue{new(X12Path{SegmentId: s.SegmentId, ElementIdx: i+1, SubelementIdx: j+1}), elem})
				ch <- ev
			}
		}
		close(ch)
	}()
	return ch
}

// Default formatting
func (s *Segment) String() string {
	return s.Format('*', ':', '^')
}

func (s *Segment) Format(elementTerm byte, subelementTerm byte, repTerm byte) string {
	var buf bytes.Buffer
	buf.WriteString(s.SegmentId)
	for _, comp := range s.Composites {
		buf.WriteByte(elementTerm)
		buf.WriteString(strings.Join(comp, string(subelementTerm)))
	}
	return buf.String()
}

//func splitComposite(f2 string, term string) (ret []string) {
//	ret = strings.Split(f2, term)
//	return
//}

//type Composite []string
<commit_msg>add Composite type<commit_after>package gox12

import (
	"bytes"
	"fmt"
	"strings"
)

type Segment struct {
	SegmentId  string
	Composites []Composite
}

type Composite []string

type ElementValue struct {
	X12Path X12Path
	Value   string
}

func NewSegment(line string, elementTerm byte, subelementTerm byte, repTerm byte) Segment {
	fields := strings.Split(line, string(elementTerm))
	segmentId := fields[0]
	comps := make([]Composite, len(fields)-1)
	for i, v := range fields[1:] {
		c := strings.Split(v, string(subelementTerm))
		comps[i] = c
	}
	return Segment{segmentId, comps}
}

// Acts like golang maps, if not found, returns default value with found==false
// X12 Path indices are 1-based
func (s *Segment) GetValue(x12path string) (val string, found bool, err error) {
	var xpath *X12Path
	if xpath, err = ParseX12Path(x12path); err != nil {
		return "", false, err
	}
	if xpath.SegmentId != "" && s.SegmentId != xpath.SegmentId {
		return "", false, fmt.Errorf("Looking for Segment ID[%s], mine is [%s]", xpath.SegmentId, s.SegmentId)
	}
	if xpath.ElementIdx == 0 {
		return "", false, fmt.Errorf("No element index specified for [%s]", x12path)
	}
	myEleIdx := xpath.ElementIdx - 1
	var mySubeleIdx int
	if xpath.SubelementIdx == 0 {
		if myEleIdx < len(s.Composites) && len(s.Composites[myEleIdx]) > 1 {
			return "", false, fmt.Errorf("This is a composite but no sub-element index was specified for [%s]", x12path)
		}
		mySubeleIdx = 0
	} else {
		mySubeleIdx = xpath.SubelementIdx - 1
	}
	if myEleIdx < len(s.Composites) {
		if mySubeleIdx < len(s.Composites[myEleIdx]) {
			return s.Composites[myEleIdx][mySubeleIdx], true, nil
		}
	}
	return "", false, nil
}

func (s *Segment) GetAllValues() <-chan ElementValue {
	ch := make(chan ElementValue)
	go func() {
		for i, comp := range s.Composites {
			for j, elem := range comp {
				x12path := X12Path{SegmentId: s.SegmentId, ElementIdx: i + 1, SubelementIdx: j + 1}
				ev := ElementValue{x12path, elem}
				//ch <- new(ElementValue{new(X12Path{SegmentId: s.SegmentId, ElementIdx: i+1, SubelementIdx: j+1}), elem})
				ch <- ev
			}
		}
		close(ch)
	}()
	return ch
}

// Default formatting
func (s *Segment) String() string {
	return s.Format('*', ':', '^')
}

func (s *Segment) Format(elementTerm byte, subelementTerm byte, repTerm byte) string {
	var buf bytes.Buffer
	buf.WriteString(s.SegmentId)
	for _, comp := range s.Composites {
		buf.WriteByte(elementTerm)
		buf.WriteString(formatComposite(comp, subelementTerm, repTerm))
	}
	return buf.String()
}

func formatComposite(c Composite, subelementTerm byte, repTerm byte) string {
	return strings.Join(c, string(subelementTerm))
}

//func splitComposite(f2 string, term string) (ret []string) {
//	ret = strings.Split(f2, term)
//	return
//}
<|endoftext|>


<commit_before>/*
Package cdtime implements methods to convert from and to collectd's internal time
representation, cdtime_t.
*/
package cdtime // import "collectd.org/cdtime"

import (
	"time"
)

// Time represens a time in collectd's internal representation.
type Time uint64

// New returns a new Time representing time t.
func New(t time.Time) Time {
	return newNano(uint64(t.UnixNano()))
}

// NewDuration returns a new Time representing duration d.
func NewDuration(d time.Duration) Time {
	return newNano(uint64(d.Nanoseconds()))
}

// Time converts and returns the time as time.Time.
func (t Time) Time() time.Time {
	s := int64(t >> 30)
	ns := (int64(t&0x3fffffff) * 1000000000) >> 30

	return time.Unix(s, ns)
}

// Duration converts and returns the duration as time.Time.
func (t Time) Duration() time.Duration {
	s := int64(t >> 30)
	ns := (int64(t&0x3fffffff) * 1000000000) >> 30

	return time.Duration(1000000000*s+ns) * time.Nanosecond
}

func newNano(ns uint64) Time {
	// break into seconds and nano-seconds so the left-shift doesn't overflow.
	s := ns / 1000000000
	ns = ns % 1000000000

	return Time((s << 30) | ((ns << 30) / 1000000000))
}
<commit_msg>cdtime: Add JSON (un)marshalling.<commit_after>/*
Package cdtime implements methods to convert from and to collectd's internal time
representation, cdtime_t.
*/
package cdtime // import "collectd.org/cdtime"

import (
	"strconv"
	"time"
)

// Time represens a time in collectd's internal representation.
type Time uint64

// New returns a new Time representing time t.
func New(t time.Time) Time {
	return newNano(uint64(t.UnixNano()))
}

// NewDuration returns a new Time representing duration d.
func NewDuration(d time.Duration) Time {
	return newNano(uint64(d.Nanoseconds()))
}

// Time converts and returns the time as time.Time.
func (t Time) Time() time.Time {
	s, ns := t.decompose()
	return time.Unix(s, ns)
}

// Duration converts and returns the duration as time.Time.
func (t Time) Duration() time.Duration {
	s, ns := t.decompose()
	return time.Duration(1000000000*s+ns) * time.Nanosecond
}

// String returns the string representation of Time. The format used is seconds
// since the epoch with millisecond precision, e.g. "1426588900.328".
func (t Time) String() string {
	f := t.Float()
	return strconv.FormatFloat(f /* format */, 'f' /* precision */, 3 /* bits */, 64)
}

// Float returns the time as seocnds since epoch. This is a lossy conversion,
// which will lose up to 11 bits. This means that the returned value should be
// considered to have roughly microsecond precision.
func (t Time) Float() float64 {
	s, ns := t.decompose()
	return float64(s) + float64(ns)/1000000000.0
}

// MarshalJSON implements the "encoding/json".Marshaler interface for Time.
func (t Time) MarshalJSON() ([]byte, error) {
	return []byte(t.String()), nil
}

// UnmarshalJSON implements the "encoding/json".Unmarshaler interface for Time.
func (t *Time) UnmarshalJSON(data []byte) error {
	f, err := strconv.ParseFloat(string(data) /* bits */, 64)
	if err != nil {
		return err
	}

	s := uint64(f)
	ns := uint64((f - float64(s)) * 1000000000.0)

	*t = newNano(1000000000*s + ns)
	return nil
}

func (t Time) decompose() (s, ns int64) {
	s = int64(t >> 30)
	ns = (int64(t&0x3fffffff) * 1000000000) >> 30
	return
}

func newNano(ns uint64) Time {
	// break into seconds and nano-seconds so the left-shift doesn't overflow.
	s := ns / 1000000000
	ns = ns % 1000000000

	return Time((s << 30) | ((ns << 30) / 1000000000))
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func test_compiler(t *testing.T, input_file string, reference_file string) {
	// output_file is the name of the text-format proto file that is generated by the compiler
	output_file := strings.Replace(filepath.Base(input_file), filepath.Ext(input_file), ".text", 1)
	// remove any preexisting output_file
	var err error
	err = exec.Command("rm", "-f", output_file).Run()
	if err != nil {
		t.Logf("Failed to remove file %s", output_file)
		t.FailNow()
	}
	// run the compiler
	err = exec.Command("openapi-compiler", "-in", input_file, "-text").Run()
	if err != nil {
		t.Logf("JSON compile failed: %+v", err)
		t.FailNow()
	}
	// verify the output_file against the reference_file
	err = exec.Command("diff", output_file, reference_file).Run()
	if err != nil {
		t.Logf("JSON diff failed: %+v", err)
		t.FailNow()
	}
}

func TestPetstoreJSON(t *testing.T) {
	test_compiler(t,
		"examples/petstore.json",
		"test/petstore.text")
}

func TestPetstoreYAML(t *testing.T) {
	test_compiler(t,
		"examples/petstore.yaml",
		"test/petstore.text")
}

func TestSeparateYAML(t *testing.T) {
	test_compiler(t,
		"examples/v2.0/yaml/petstore-separate/spec/swagger.yaml",
		"test/v2.0/yaml/petstore-separate/spec/swagger.text")
}

func TestRemotePetstoreJSON(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/petstore.json",
		"test/petstore.text")
}

func TestRemotePetstoreYAML(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/petstore.yaml",
		"test/petstore.text")
}

func TestRemoteSeparateYAML(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/yaml/petstore-separate/spec/swagger.yaml",
		"test/v2.0/yaml/petstore-separate/spec/swagger.text")
}
<commit_msg>Add "separate json" tests, both local and remote.<commit_after>package main

import (
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func test_compiler(t *testing.T, input_file string, reference_file string) {
	// output_file is the name of the text-format proto file that is generated by the compiler
	output_file := strings.Replace(filepath.Base(input_file), filepath.Ext(input_file), ".text", 1)
	// remove any preexisting output_file
	var err error
	err = exec.Command("rm", "-f", output_file).Run()
	if err != nil {
		t.Logf("Failed to remove file %s", output_file)
		t.FailNow()
	}
	// run the compiler
	err = exec.Command("openapi-compiler", "-in", input_file, "-text").Run()
	if err != nil {
		t.Logf("JSON compile failed: %+v", err)
		t.FailNow()
	}
	// verify the output_file against the reference_file
	err = exec.Command("diff", output_file, reference_file).Run()
	if err != nil {
		t.Logf("JSON diff failed: %+v", err)
		t.FailNow()
	}
}

func TestPetstoreJSON(t *testing.T) {
	test_compiler(t,
		"examples/petstore.json",
		"test/petstore.text")
}

func TestPetstoreYAML(t *testing.T) {
	test_compiler(t,
		"examples/petstore.yaml",
		"test/petstore.text")
}

func TestSeparateYAML(t *testing.T) {
	test_compiler(t,
		"examples/v2.0/yaml/petstore-separate/spec/swagger.yaml",
		"test/v2.0/yaml/petstore-separate/spec/swagger.text")
}

func TestSeparateJSON(t *testing.T) {
	test_compiler(t,
		"examples/v2.0/json/petstore-separate/spec/swagger.json",
		"test/v2.0/yaml/petstore-separate/spec/swagger.text") // yaml and json results should be identical
}

func TestRemotePetstoreJSON(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/petstore.json",
		"test/petstore.text")
}

func TestRemotePetstoreYAML(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/petstore.yaml",
		"test/petstore.text")
}

func TestRemoteSeparateYAML(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/yaml/petstore-separate/spec/swagger.yaml",
		"test/v2.0/yaml/petstore-separate/spec/swagger.text")
}

func TestRemoteSeparateJSON(t *testing.T) {
	test_compiler(t,
		"https://raw.githubusercontent.com/googleapis/openapi-compiler/master/examples/v2.0/json/petstore-separate/spec/swagger.json",
		"test/v2.0/yaml/petstore-separate/spec/swagger.text")
}
<|endoftext|>


<commit_before>package sms // import "github.com/mndrix/sms-over-xmpp"
import (
	"errors"

	xco "github.com/mndrix/go-xco"
)

// StaticConfig intends to implement the Config interface
var _ Config = &StaticConfig{}

type StaticConfig struct {
	Http HttpConfig `toml:"http"`

	Xmpp StaticConfigXmpp `toml:"xmpp"`

	// Users maps the local part of an XMPP address to the
	// corresponding E.164 phone number.
	Users map[string]string `toml:"users"`

	// Twilio contains optional account details for making API calls via the
	// Twilio service.
	Twilio *TwilioConfig `toml:"twilio"`
}

type HttpConfig struct {
	Host string `toml:"host"`
	Port int    `toml:"port"`
}

type StaticConfigXmpp struct {
	Host   string `toml:"host"`
	Name   string `toml:"name"`
	Port   int    `toml:"port"`
	Secret string `toml:"secret"`
}

type TwilioConfig struct {
	AccountSid string `toml:"account-sid"`
	KeySid     string `toml:"key-sid"`
	KeySecret  string `toml:"key-secret"`
}

func (self *StaticConfig) ComponentName() string {
	return self.Xmpp.Name
}

func (self *StaticConfig) SharedSecret() string {
	return self.Xmpp.Secret
}

func (self *StaticConfig) HttpHost() string {
	return self.Http.Host
}

func (self *StaticConfig) HttpPort() int {
	port := self.Http.Port
	if port == 0 {
		port = 9677
	}
	return port
}

func (self *StaticConfig) XmppHost() string {
	return self.Xmpp.Host
}

func (self *StaticConfig) XmppPort() int {
	return self.Xmpp.Port
}

func (self *StaticConfig) AddressToPhone(addr xco.Address) (string, error) {
	e164, ok := self.Users[addr.LocalPart]
	if ok {
		return e164, nil
	}

	return addr.LocalPart, nil
}

func (self *StaticConfig) SmsProvider(from, to string) (SmsProvider, error) {
	if self.Twilio == nil {
		return nil, errors.New("Need to configure an SMS provider")
	}
	twilio := &Twilio{
		accountSid: self.Twilio.AccountSid,
		keySid:     self.Twilio.KeySid,
		keySecret:  self.Twilio.KeySecret,
	}
	return twilio, nil
}
<commit_msg>Use full addresses for "users" config<commit_after>package sms // import "github.com/mndrix/sms-over-xmpp"
import (
	"errors"

	xco "github.com/mndrix/go-xco"
)

// StaticConfig intends to implement the Config interface
var _ Config = &StaticConfig{}

type StaticConfig struct {
	Http HttpConfig `toml:"http"`

	Xmpp StaticConfigXmpp `toml:"xmpp"`

	// Users maps an XMPP address to an E.164 phone number.
	Users map[string]string `toml:"users"`

	// Twilio contains optional account details for making API calls via the
	// Twilio service.
	Twilio *TwilioConfig `toml:"twilio"`
}

type HttpConfig struct {
	Host string `toml:"host"`
	Port int    `toml:"port"`
}

type StaticConfigXmpp struct {
	Host   string `toml:"host"`
	Name   string `toml:"name"`
	Port   int    `toml:"port"`
	Secret string `toml:"secret"`
}

type TwilioConfig struct {
	AccountSid string `toml:"account-sid"`
	KeySid     string `toml:"key-sid"`
	KeySecret  string `toml:"key-secret"`
}

func (self *StaticConfig) ComponentName() string {
	return self.Xmpp.Name
}

func (self *StaticConfig) SharedSecret() string {
	return self.Xmpp.Secret
}

func (self *StaticConfig) HttpHost() string {
	return self.Http.Host
}

func (self *StaticConfig) HttpPort() int {
	port := self.Http.Port
	if port == 0 {
		port = 9677
	}
	return port
}

func (self *StaticConfig) XmppHost() string {
	return self.Xmpp.Host
}

func (self *StaticConfig) XmppPort() int {
	return self.Xmpp.Port
}

func (self *StaticConfig) AddressToPhone(addr xco.Address) (string, error) {
	e164, ok := self.Users[addr.LocalPart+"@"+addr.DomainPart]
	if ok {
		return e164, nil
	}

	// assume the name is already a phone number
	return addr.LocalPart, nil
}

func (self *StaticConfig) SmsProvider(from, to string) (SmsProvider, error) {
	if self.Twilio == nil {
		return nil, errors.New("Need to configure an SMS provider")
	}
	twilio := &Twilio{
		accountSid: self.Twilio.AccountSid,
		keySid:     self.Twilio.KeySid,
		keySecret:  self.Twilio.KeySecret,
	}
	return twilio, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"github.com/stripe/sequins/backend"
	"github.com/stripe/sequins/index"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type sequinsOptions struct {
	LocalPath           string
	CheckForSuccessFile bool
}

type sequins struct {
	options        sequinsOptions
	backend        backend.Backend
	index          *index.Index
	http           *http.Server
	currentVersion string
	started        time.Time
	updated        time.Time
	reloadLock     sync.Mutex
}

type status struct {
	Path    string `json:"path"`
	Started int64  `json:"started"`
	Updated int64  `json:"updated"`
	Count   int    `json:"count"`
}

func newSequins(backend backend.Backend, options sequinsOptions) *sequins {
	return &sequins{
		options:    options,
		backend:    backend,
		reloadLock: sync.Mutex{},
	}
}

func (s *sequins) start(address string) error {
	err := s.refresh()
	if err != nil {
		return err
	}

	now := time.Now()
	s.started = now
	s.updated = now

	defer s.index.Close()

	log.Printf("Listening on %s", address)
	return http.ListenAndServe(address, s)
}

func (s *sequins) reloadLatest() error {
	err := s.refresh()
	if err != nil {
		return err
	}

	s.updated = time.Now()

	return nil
}

func (s *sequins) refresh() error {
	s.reloadLock.Lock()
	defer s.reloadLock.Unlock()

	version, err := s.backend.LatestVersion(s.options.CheckForSuccessFile)
	if err != nil {
		return err
	}

	if version != s.currentVersion {
		path := filepath.Join(s.options.LocalPath, version)

		err := os.Mkdir(path, 0700|os.ModeDir)
		if err != nil && !os.IsExist(err) {
			return err
		}

		if os.IsExist(err) {
			log.Printf("Version %s is already downloaded.", version)
		} else {
			log.Printf("Downloading version %s from %s.", version, s.backend.DisplayPath(version))
			err = s.backend.Download(version, path)
			if err != nil {
				return err
			}
		}

		log.Printf("Building index over version %s at %s.", version, path)
		index := index.New(path)
		err = index.BuildIndex()
		if err != nil {
			return err
		}

		log.Println("Switching to new directory!")
		s.currentVersion = version
		s.index = index
	} else {
		log.Printf("%s is already the newest version, so not reloading.", version)
	}

	return nil
}

func (s *sequins) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		count, err := s.index.Count()
		if err != nil {
			log.Fatal(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		status := status{
			Path:    s.backend.DisplayPath(s.currentVersion),
			Started: s.started.Unix(),
			Updated: s.updated.Unix(),
			Count:   count,
		}

		jsonBytes, err := json.Marshal(status)
		if err != nil {
			log.Fatal(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		w.Write(jsonBytes)
		return
	}

	key := strings.TrimPrefix(r.URL.Path, "/")
	res, err := s.index.Get(key)
	if err == index.ErrNotFound {
		w.WriteHeader(http.StatusNotFound)
	} else if err != nil {
		log.Fatal(err)
		w.WriteHeader(http.StatusInternalServerError)
	} else {
		http.ServeContent(w, r, key, s.updated, bytes.NewReader(res))
	}
}
<commit_msg>improve a few error messages<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/stripe/sequins/backend"
	"github.com/stripe/sequins/index"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type sequinsOptions struct {
	LocalPath           string
	CheckForSuccessFile bool
}

type sequins struct {
	options        sequinsOptions
	backend        backend.Backend
	index          *index.Index
	http           *http.Server
	currentVersion string
	started        time.Time
	updated        time.Time
	reloadLock     sync.Mutex
}

type status struct {
	Path    string `json:"path"`
	Started int64  `json:"started"`
	Updated int64  `json:"updated"`
	Count   int    `json:"count"`
}

func newSequins(backend backend.Backend, options sequinsOptions) *sequins {
	return &sequins{
		options:    options,
		backend:    backend,
		reloadLock: sync.Mutex{},
	}
}

func (s *sequins) start(address string) error {
	err := s.refresh()
	if err != nil {
		return err
	}

	now := time.Now()
	s.started = now
	s.updated = now

	defer s.index.Close()

	log.Printf("Listening on %s", address)
	return http.ListenAndServe(address, s)
}

func (s *sequins) reloadLatest() error {
	err := s.refresh()
	if err != nil {
		return err
	}

	s.updated = time.Now()

	return nil
}

func (s *sequins) refresh() error {
	s.reloadLock.Lock()
	defer s.reloadLock.Unlock()

	version, err := s.backend.LatestVersion(s.options.CheckForSuccessFile)
	if err != nil {
		return err
	}

	if version != s.currentVersion {
		path := filepath.Join(s.options.LocalPath, version)

		err := os.Mkdir(path, 0700|os.ModeDir)
		if err != nil && !os.IsExist(err) {
			return err
		}

		if os.IsExist(err) {
			log.Printf("Version %s is already downloaded.", version)
		} else {
			log.Printf("Downloading version %s from %s.", version, s.backend.DisplayPath(version))
			err = s.backend.Download(version, path)
			if err != nil {
				return err
			}
		}

		log.Printf("Building index over version %s at %s.", version, path)
		index := index.New(path)
		err = index.BuildIndex()
		if err != nil {
			return fmt.Errorf("Error while indexing: %s", err)
		}

		log.Println("Switching to new directory!")
		s.currentVersion = version
		s.index = index
	} else {
		log.Printf("%s is already the newest version, so not reloading.", version)
	}

	return nil
}

func (s *sequins) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		count, err := s.index.Count()
		if err != nil {
			log.Fatal(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		status := status{
			Path:    s.backend.DisplayPath(s.currentVersion),
			Started: s.started.Unix(),
			Updated: s.updated.Unix(),
			Count:   count,
		}

		jsonBytes, err := json.Marshal(status)
		if err != nil {
			log.Fatal(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		w.Write(jsonBytes)
		return
	}

	key := strings.TrimPrefix(r.URL.Path, "/")
	res, err := s.index.Get(key)
	if err == index.ErrNotFound {
		w.WriteHeader(http.StatusNotFound)
	} else if err != nil {
		log.Fatal(fmt.Errorf("Error fetching value for %s: %s", key, err))
		w.WriteHeader(http.StatusInternalServerError)
	} else {
		http.ServeContent(w, r, key, s.updated, bytes.NewReader(res))
	}
}
<|endoftext|>


<commit_before>package plugins

import (
	"io/ioutil"
	"net/http"
	"strings"

	"fmt"

	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bwmarrin/discordgo"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/youtube/v3"
)

type YouTube struct {
	service        *youtube.Service
	client         *http.Client
	configFileName string
	config         *jwt.Config
}

const (
	YouTubeChannelBaseUrl string = "https://www.youtube.com/channel/%s"
	YouTubeVideoBaseUrl   string = "https://youtu.be/%s"
	YouTubeColor          string = "cd201f"

	youtubeConfigFileName string = "google.client_credentials_json_location"
)

func (yt *YouTube) Commands() []string {
	return []string{
		"youtube",
		"yt",
	}
}

func (yt *YouTube) Init(session *discordgo.Session) {
	yt.configFileName = youtubeConfigFileName

	err := yt.createConfig()
	helpers.Relax(err)

	yt.client = yt.config.Client(context.Background())

	yt.service, err = youtube.New(yt.client)
	helpers.Relax(err)
}

func (yt *YouTube) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	defer helpers.Recover()

	session.ChannelTyping(msg.ChannelID)

	args := strings.Fields(content)
	if len(args) < 1 {
		_, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
		helpers.Relax(err)
		return
	}

	var result *discordgo.MessageSend
	switch args[0] {
	default:
		// _youtube {args[0]: videoID}
		result = yt.search(args[0:])
	}

	_, err := session.ChannelMessageSendComplex(msg.ChannelID, result)
	helpers.Relax(err)
}

func (yt *YouTube) search(args []string) *discordgo.MessageSend {
	if yt.service == nil {
		return &discordgo.MessageSend{Content: "plugins.youtube.service-not-available"}
	}

	// _youtube {args[0]: videoID}
	if len(args) < 1 {
		return &discordgo.MessageSend{Content: "bot.argument.invalid"}
	}
	query := strings.Join(args, " ")

	call := yt.service.Search.List("id,snippet").
		Q(query).
		Type("channel,video").
		SafeSearch("strict").
		MaxResults(1)

	response, err := call.Do()
	if err != nil {
		return &discordgo.MessageSend{Content: err.Error()}
	}

	if len(response.Items) <= 0 {
		return &discordgo.MessageSend{Content: "plugins.youtube.video-not-found"}
	}
	item := response.Items[0]

	var id, url string
	switch item.Id.Kind {
	case "youtube#video":
		id = item.Id.VideoId
		url = fmt.Sprintf(YouTubeVideoBaseUrl, id)
	case "youtube#channel":
		id = item.Id.ChannelId
		url = fmt.Sprintf(YouTubeChannelBaseUrl, id)
	default:
		return &discordgo.MessageSend{Content: "unknown item kind: " + item.Kind}
	}

	return &discordgo.MessageSend{Content: url}
}

func (yt *YouTube) createConfig() error {
	config := yt.getConfig()

	authJSON, err := ioutil.ReadFile(config)
	if err != nil {
		return err
	}

	yt.config, err = google.JWTConfigFromJSON(authJSON, youtube.YoutubeReadonlyScope)
	return err
}

func (yt *YouTube) getConfig() string {
	return helpers.GetConfig().Path(yt.configFileName).Data().(string)
}
<commit_msg>[youtube] adds channel/video information<commit_after>package plugins

import (
	"io/ioutil"
	"net/http"
	"strings"

	"fmt"

	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bwmarrin/discordgo"
	humanize "github.com/dustin/go-humanize"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/youtube/v3"
)

type YouTube struct {
	service        *youtube.Service
	client         *http.Client
	configFileName string
	config         *jwt.Config
}

const (
	YouTubeChannelBaseUrl string = "https://www.youtube.com/channel/%s"
	YouTubeVideoBaseUrl   string = "https://youtu.be/%s"
	YouTubeColor          string = "cd201f"

	youtubeConfigFileName string = "google.client_credentials_json_location"
)

func (yt *YouTube) Commands() []string {
	return []string{
		"youtube",
		"yt",
	}
}

func (yt *YouTube) Init(session *discordgo.Session) {
	yt.configFileName = youtubeConfigFileName

	err := yt.createConfig()
	helpers.Relax(err)

	yt.client = yt.config.Client(context.Background())

	yt.service, err = youtube.New(yt.client)
	helpers.Relax(err)
}

func (yt *YouTube) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	defer helpers.Recover()

	session.ChannelTyping(msg.ChannelID)

	args := strings.Fields(content)
	if len(args) < 1 {
		_, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
		helpers.Relax(err)
		return
	}

	var result *discordgo.MessageSend
	switch args[0] {
	default:
		// _youtube {args[0]: videoID}
		result = yt.search(args[0:])
	}

	if result.Content != "" {
		_, err := session.ChannelMessageSend(msg.ChannelID, result.Content)
		helpers.Relax(err)
	}
	if result.Embed != nil {
		_, err := session.ChannelMessageSendEmbed(msg.ChannelID, result.Embed)
		helpers.Relax(err)
	}
}

func (yt *YouTube) search(args []string) *discordgo.MessageSend {
	if yt.service == nil {
		return &discordgo.MessageSend{Content: "plugins.youtube.service-not-available"}
	}

	// _youtube {args[0]: videoID}
	if len(args) < 1 {
		return &discordgo.MessageSend{Content: "bot.argument.invalid"}
	}
	query := strings.Join(args, " ")

	call := yt.service.Search.List("id,snippet").
		Q(query).
		Type("channel,video").
		MaxResults(1)

	response, err := call.Do()
	if err != nil {
		return &discordgo.MessageSend{Content: err.Error()}
	}

	if len(response.Items) <= 0 {
		return &discordgo.MessageSend{Content: "plugins.youtube.video-not-found"}
	}
	item := response.Items[0]

	data := &discordgo.MessageSend{}
	switch item.Id.Kind {
	case "youtube#video":
		id := item.Id.VideoId
		data.Content = fmt.Sprintf(YouTubeVideoBaseUrl, id)
		data.Embed = yt.getVideoInfo(id)
	case "youtube#channel":
		id := item.Id.ChannelId
		data.Content = fmt.Sprintf(YouTubeChannelBaseUrl, id)
		data.Embed = yt.getChannelInfo(id)
	default:
		data.Content = "unknown item kind: " + item.Kind
	}

	return data
}

func (yt *YouTube) getVideoInfo(videoId string) *discordgo.MessageEmbed {
	call := yt.service.Videos.List("statistics, snippet").
		Id(videoId).
		MaxResults(1)

	response, err := call.Do()
	if err != nil {
		return nil
	}

	if len(response.Items) <= 0 {
		return nil
	}
	video := response.Items[0]

	return &discordgo.MessageEmbed{
		Footer: &discordgo.MessageEmbedFooter{Text: "Video information of " + video.Snippet.Title},
		Fields: []*discordgo.MessageEmbedField{
			{Name: "Views", Value: humanize.Comma(int64(video.Statistics.ViewCount)), Inline: true},
			{Name: "Likes", Value: humanize.Comma(int64(video.Statistics.LikeCount)), Inline: true},
			{Name: "Comments", Value: humanize.Comma(int64(video.Statistics.CommentCount)), Inline: true},
			{Name: "Published at", Value: video.Snippet.PublishedAt, Inline: true},
		},
		Color: helpers.GetDiscordColorFromHex(YouTubeColor),
	}
}

func (yt *YouTube) getChannelInfo(channelId string) *discordgo.MessageEmbed {
	call := yt.service.Channels.List("statistics, snippet").
		Id(channelId).
		MaxResults(1)

	response, err := call.Do()
	if err != nil {
		return nil
	}

	if len(response.Items) <= 0 {
		return nil
	}
	channel := response.Items[0]

	return &discordgo.MessageEmbed{
		Footer: &discordgo.MessageEmbedFooter{Text: "Channel information of " + channel.Snippet.Title},
		Fields: []*discordgo.MessageEmbedField{
			{Name: "Views", Value: humanize.Comma(int64(channel.Statistics.ViewCount)), Inline: true},
			{Name: "Subscribers", Value: humanize.Comma(int64(channel.Statistics.SubscriberCount)), Inline: true},
			{Name: "Videos", Value: humanize.Comma(int64(channel.Statistics.VideoCount)), Inline: true},
			{Name: "Comments", Value: humanize.Comma(int64(channel.Statistics.CommentCount)), Inline: true},
			{Name: "Published at", Value: channel.Snippet.PublishedAt, Inline: true},
		},
		Color: helpers.GetDiscordColorFromHex(YouTubeColor),
	}
}

func (yt *YouTube) createConfig() error {
	config := yt.getConfig()

	authJSON, err := ioutil.ReadFile(config)
	if err != nil {
		return err
	}

	yt.config, err = google.JWTConfigFromJSON(authJSON, youtube.YoutubeReadonlyScope)
	return err
}

func (yt *YouTube) getConfig() string {
	return helpers.GetConfig().Path(yt.configFileName).Data().(string)
}
<|endoftext|>


<commit_before>package mackerel

// TODO: Write test and implementation for each of the following.

// Getting Notification Channels
//   GET /api/v0/channels
// Registering Notification Channels
//  POST /api/v0/channels
// Deleting Notification Channels
//  DELETE /api/v0/channels/<channelId>
// https://mackerel.io/api-docs/entry/channels
<commit_msg>start to write tests for ListChannels<commit_after>package mackerel

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

func TestListChannels(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		if req.URL.Path != "/api/v0/channels" {
			t.Error("request URL should be /api/v0/channels but: ", req.URL.Path)
		}

		if req.Method != "GET" {
			t.Error("request method should be GET but: ", req.Method)
		}

		respJSON, _ := json.Marshal(map[string][]map[string]interface{}{
			"channels": {
				{
					"id":      "abcdefabc",
					"name":    "email channel",
					"type":    "email",
					"emails":  []string{"test@example.com", "test2@example.com"},
					"userIds": []string{"1234", "2345"},
					"events":  []string{"alert"},
				},
				{
					"id":   "bcdefabcd",
					"name": "slack channel",
					"type": "slack",
					"url":  "https://hooks.slack.com/services/TAAAA/BBBB/XXXXX",
					"mentions": map[string]interface{}{
						"ok":      "ok message",
						"warning": "warning message",
					},
					"enabledGraphImage": true,
					"events":            []string{"alert"},
				},
				{
					"id":     "cdefabcde",
					"name":   "webhook channel",
					"type":   "webhook",
					"url":    "http://example.com/webhook",
					"events": []string{"alert"},
				},
				{
					"id":   "defabcdef",
					"name": "line channel",
					"type": "line",
				},
			},
		})

		res.Header()["Content-Type"] = []string{"application/json"}
		fmt.Fprint(res, string(respJSON))
	}))
	defer ts.Close()

	client, _ := NewClientWithOptions("dummy-key", ts.URL, false)
	channels, err := client.ListChannels()

	if err != nil {
		t.Error("err should be nil but: ", err)
	}

	if reflect.DeepEqual(channels[0].Emails, []string{"test@example.com", "test2@example.com"}) != true {
		t.Errorf("Wrong data for emails: %v", channels[0].Emails)
	}
	if reflect.DeepEqual(channels[0].UserIDs, []string{"1234", "2345"}) != true {
		t.Errorf("Wrong data for emails: %v", channels[0].UserIDs)
	}

	if channels[1].URL != "https://hooks.slack.com/services/TAAAA/BBBB/XXXXX" {
		t.Error("request sends json including URL but: ", channels[1])
	}
	if channels[2].URL != "http://example.com/webhook" {
		t.Error("request sends json including URL but: ", channels[2])
	}
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"

	rtl "github.com/jpoirier/gortlsdr"
	// "unsafe"
)

var send_ping bool = true

// rtlsdr_cb is used for asynchronous streaming. It's the
// user callback function passed to librtlsdr.
func rtlsdr_cb(buf []byte, userctx *rtl.UserCtx) {
	if send_ping {
		send_ping = false
		// send a ping to async_stop
		if c, ok := (*userctx).(chan bool); ok {
			c <- true // async-read done signal
		}
	}
	log.Printf("Length of async-read buffer: %d\n", len(buf))
}

// async_stop pends for a ping from the rtlsdrCb function
// callback, and when received cancel the async callback.
func async_stop(dev *rtl.Context, c chan bool) {
	log.Println("async_stop running...")
	<-c
	log.Println("Received ping from rtlsdr_cb, calling CancelAsync")
	if err := dev.CancelAsync(); err != nil {
		log.Printf("CancelAsync failed - %s\n", err)
	} else {
		log.Printf("CancelAsync successful\n")
	}

	os.Exit(0)
}

func sig_abort(dev *rtl.Context) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	<-ch
	_ = dev.CancelAsync()
	dev.Close()
	os.Exit(0)
}

func main() {
	var err error
	var dev *rtl.Context

	//---------- Device Check ----------
	if c := rtl.GetDeviceCount(); c == 0 {
		log.Fatal("No devices found, exiting.\n")
	} else {
		for i := 0; i < c; i++ {
			m, p, s, err := rtl.GetDeviceUsbStrings(i)
			log.Printf("GetDeviceUsbStrings %s - %s %s %s\n",
				err, m, p, s)
		}
	}

	log.Printf("===== Device name: %s =====\n", rtl.GetDeviceName(0))
	log.Printf("===== Running tests using device indx: 0 =====\n")

	//---------- Open Device ----------
	if dev, err = rtl.Open(0); err != nil {
		log.Fatal("\tOpen Failed, exiting\n")
	}
	defer dev.Close()
	go sig_abort(dev)

	//---------- Device Strings ----------
	m, p, s, err := dev.GetUsbStrings()
	if err != nil {
		log.Printf("\tGetUsbStrings Failed - error: %s\n", err)
	} else {
		log.Printf("\tGetUsbStrings - %s %s %s\n", m, p, s)
	}

	log.Printf("\tGetTunerType: %s\n", dev.GetTunerType())

	//---------- Get/Set Tuner Gains ----------
	g, err := dev.GetTunerGains()
	if err != nil {
		log.Printf("\tGetTunerGains Failed - error: %s\n", err)
	} else {
		gains := fmt.Sprintf("\tGains: ")
		for _, j := range g {
			gains += fmt.Sprintf("%d ", j)
		}
		log.Printf("%s\n", gains)
	}

	tgain := dev.GetTunerGain()
	log.Printf("\tGetTunerGain: %d\n", tgain)

	err = dev.SetTunerGainMode(true)
	if err != nil {
		log.Printf("\tSetTunerGainMode Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetTunerGainMode Successful\n")
	}

	err = dev.SetTunerGain(tgain)
	if err != nil {
		log.Printf("\tSetTunerGain Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetTunerGain Successful\n")
	}

	//---------- Get/Set Sample Rate ----------
	err = dev.SetSampleRate(rtl.DefaultSampleRate)
	if err != nil {
		log.Printf("\tSetSampleRate Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetSampleRate - rate: %d\n", rtl.DefaultSampleRate)
	}
	log.Printf("\tGetSampleRate: %d\n", dev.GetSampleRate())

	//---------- Get/Set Xtal Freq ----------
	rtl_freq, tuner_freq, err := dev.GetXtalFreq()
	if err != nil {
		log.Printf("\tGetXtalFreq Failed - error: %s\n", err)
	} else {
		log.Printf("\tGetXtalFreq - Rtl: %d, Tuner: %d\n", rtl_freq, tuner_freq)
	}

	err = dev.SetXtalFreq(rtl_freq, tuner_freq)
	if err != nil {
		log.Printf("\tSetXtalFreq Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetXtalFreq - Center freq: %d, Tuner freq: %d\n",
			rtl_freq, tuner_freq)
	}

	//---------- Get/Set Center Freq ----------
	err = dev.SetCenterFreq(850000000)
	if err != nil {
		log.Printf("\tSetCenterFreq 850MHz Failed, error: %s\n", err)
	} else {
		log.Printf("\tSetCenterFreq 850MHz Successful\n")
	}

	log.Printf("\tGetCenterFreq: %d\n", dev.GetCenterFreq())

	//---------- Get/Set Freq Correction ----------
	freq_corr := dev.GetFreqCorrection()
	log.Printf("\tGetFreqCorrection: %d\n", freq_corr)
	err = dev.SetFreqCorrection(freq_corr)
	if err != nil {
		log.Printf("\tGetFreqCorrection Failed, error: %s\n", err)
	} else {
		log.Printf("\tGetFreqCorrection Successful\n")
	}

	//---------- Get/Set AGC Mode ----------
	if err = dev.SetAgcMode(false); err == nil {
		log.Printf("\tSetAgcMode off Successful\n")
	} else {
		log.Printf("\tSetAgcMode Failed, error: %s\n", err)
	}

	//---------- Get/Set Direct Sampling ----------
	if mode, err := dev.GetDirectSampling(); err == nil {
		log.Printf("\tGetDirectSampling Successful, mode: %s\n",
			rtl.SamplingModes[mode])
	} else {
		log.Printf("\tSetTestMode 'On' Failed - error: %s\n", err)
	}

	if err = dev.SetDirectSampling(rtl.SamplingNone); err == nil {
		log.Printf("\tSetDirectSampling 'On' Successful\n")
	} else {
		log.Printf("\tSetDirectSampling 'On' Failed - error: %s\n", err)
	}

	//---------- Get/Set Tuner IF Gain ----------
	// if err = SetTunerIfGain(stage, gain: int); err == nil {
	// 	log.Printf("\SetTunerIfGain Successful\n")
	// } else {
	// 	log.Printf("\tSetTunerIfGain Failed - error: %s\n", err)
	// }

	//---------- Get/Set test mode ----------
	if err = dev.SetTestMode(true); err == nil {
		log.Printf("\tSetTestMode 'On' Successful\n")
	} else {
		log.Printf("\tSetTestMode 'On' Failed - error: %s\n", err)
	}

	if err = dev.SetTestMode(false); err == nil {
		log.Printf("\tSetTestMode 'Off' Successful\n")
	} else {
		log.Printf("\tSetTestMode 'Off' Fail - error: %s\n", err)
	}

	//---------- Get/Set misc. streaming ----------
	if err = dev.ResetBuffer(); err == nil {
		log.Printf("\tResetBuffer Successful\n")
	} else {
		log.Printf("\tResetBuffer Failed - error: %s\n", err)
	}

	var buffer []byte = make([]uint8, rtl.DefaultBufLength)
	n_read, err := dev.ReadSync(buffer, rtl.DefaultBufLength)
	if err != nil {
		log.Printf("\tReadSync Failed - error: %s\n", err)
	} else {
		log.Printf("\tReadSync %d\n", n_read)
	}
	if err == nil && n_read < rtl.DefaultBufLength {
		log.Printf("ReadSync short read, %d samples lost\n", rtl.DefaultBufLength-n_read)
	}

	// Note, ReadAsync blocks until CancelAsync is called, so spawn
	// a goroutine running in its own system thread that'll wait
	// for the async-read callback to signal when it's done.
	IQch := make(chan bool)
	go async_stop(dev, IQch)
	var userctx rtl.UserCtx = IQch
	err = dev.ReadAsync(rtlsdr_cb, &userctx, rtl.DefaultAsyncBufNumber, rtl.DefaultBufLength)
	if err == nil {
		log.Printf("\tReadAsync Successful\n")
	} else {
		log.Printf("\tReadAsync Fail - error: %s\n", err)
	}

	log.Printf("Exiting...\n")
}
<commit_msg>minor example tweaks<commit_after>// +build ignore

package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"

	rtl "github.com/jpoirier/gortlsdr"
	// "unsafe"
)

var send_ping bool = true

// rtlsdr_cb is used for asynchronous streaming. It's the
// user callback function passed to librtlsdr.
func rtlsdr_cb(buf []byte, userctx *rtl.UserCtx) {
	if send_ping {
		send_ping = false
		// send a ping to async_stop
		if c, ok := (*userctx).(chan bool); ok {
			c <- true // async-read done signal
		}
	}
	log.Printf("Length of async-read buffer: %d\n", len(buf))
}

// async_stop pends for a ping from the rtlsdrCb function
// callback, and when received cancel the async callback.
func async_stop(dev *rtl.Context, c chan bool) {
	log.Println("async_stop running...")
	<-c
	log.Println("Received ping from rtlsdr_cb, calling CancelAsync")
	if err := dev.CancelAsync(); err != nil {
		log.Printf("CancelAsync failed - %s\n", err)
	} else {
		log.Printf("CancelAsync successful\n")
	}

	os.Exit(0)
}

func sig_abort(dev *rtl.Context) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	<-ch
	_ = dev.CancelAsync()
	dev.Close()
	os.Exit(0)
}

func main() {
	var err error
	var dev *rtl.Context

	//---------- Device Check ----------
	if c := rtl.GetDeviceCount(); c == 0 {
		log.Fatal("No devices found, exiting.\n")
	} else {
		for i := 0; i < c; i++ {
			m, p, s, err := rtl.GetDeviceUsbStrings(i)
			log.Printf("GetDeviceUsbStrings %s - %s %s %s\n",
				err, m, p, s)
		}
	}

	log.Printf("===== Device name: %s =====\n", rtl.GetDeviceName(0))
	log.Printf("===== Running tests using device indx: 0 =====\n")

	//---------- Open Device ----------
	if dev, err = rtl.Open(0); err != nil {
		log.Fatal("\tOpen Failed, exiting\n")
	}
	defer dev.Close()
	go sig_abort(dev)

	//---------- Device Strings ----------
	m, p, s, err := dev.GetUsbStrings()
	if err != nil {
		log.Printf("\tGetUsbStrings Failed - error: %s\n", err)
	} else {
		log.Printf("\tGetUsbStrings - %s %s %s\n", m, p, s)
	}

	log.Printf("\tGetTunerType: %s\n", dev.GetTunerType())

	//---------- Get/Set Tuner Gains ----------
	g, err := dev.GetTunerGains()
	if err != nil {
		log.Printf("\tGetTunerGains Failed - error: %s\n", err)
	} else {
		gains := fmt.Sprintf("\tGains: ")
		for _, j := range g {
			gains += fmt.Sprintf("%d ", j)
		}
		log.Printf("%s\n", gains)
	}

	tgain := dev.GetTunerGain()
	log.Printf("\tGetTunerGain: %d\n", tgain)

	err = dev.SetTunerGainMode(true)
	if err != nil {
		log.Printf("\tSetTunerGainMode Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetTunerGainMode Successful\n")
	}

	err = dev.SetTunerGain(tgain)
	if err != nil {
		log.Printf("\tSetTunerGain Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetTunerGain Successful\n")
	}

	//---------- Get/Set Sample Rate ----------
	err = dev.SetSampleRate(rtl.DefaultSampleRate)
	if err != nil {
		log.Printf("\tSetSampleRate Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetSampleRate - rate: %d\n", rtl.DefaultSampleRate)
	}
	log.Printf("\tGetSampleRate: %d\n", dev.GetSampleRate())

	//---------- Get/Set Xtal Freq ----------
	rtl_freq, tuner_freq, err := dev.GetXtalFreq()
	if err != nil {
		log.Printf("\tGetXtalFreq Failed - error: %s\n", err)
	} else {
		log.Printf("\tGetXtalFreq - Rtl: %d, Tuner: %d\n", rtl_freq, tuner_freq)
	}

	err = dev.SetXtalFreq(rtl_freq, tuner_freq)
	if err != nil {
		log.Printf("\tSetXtalFreq Failed - error: %s\n", err)
	} else {
		log.Printf("\tSetXtalFreq - Center freq: %d, Tuner freq: %d\n",
			rtl_freq, tuner_freq)
	}

	//---------- Get/Set Center Freq ----------
	err = dev.SetCenterFreq(850000000)
	if err != nil {
		log.Printf("\tSetCenterFreq 850MHz Failed, error: %s\n", err)
	} else {
		log.Printf("\tSetCenterFreq 850MHz Successful\n")
	}

	log.Printf("\tGetCenterFreq: %d\n", dev.GetCenterFreq())

	//---------- Get/Set Freq Correction ----------
	freq_corr := dev.GetFreqCorrection()
	log.Printf("\tGetFreqCorrection: %d\n", freq_corr)
	err = dev.SetFreqCorrection(10) // 10ppm
	if err != nil {
		log.Printf("\tSetFreqCorrection Failed, error: %s\n", err)
	} else {
		log.Printf("\tSetFreqCorrection Successful\n")
	}

	//---------- Get/Set AGC Mode ----------
	if err = dev.SetAgcMode(false); err == nil {
		log.Printf("\tSetAgcMode off Successful\n")
	} else {
		log.Printf("\tSetAgcMode Failed, error: %s\n", err)
	}

	//---------- Get/Set Direct Sampling ----------
	if mode, err := dev.GetDirectSampling(); err == nil {
		log.Printf("\tGetDirectSampling Successful, mode: %s\n",
			rtl.SamplingModes[mode])
	} else {
		log.Printf("\tSetTestMode 'On' Failed - error: %s\n", err)
	}

	if err = dev.SetDirectSampling(rtl.SamplingNone); err == nil {
		log.Printf("\tSetDirectSampling 'On' Successful\n")
	} else {
		log.Printf("\tSetDirectSampling 'On' Failed - error: %s\n", err)
	}

	//---------- Get/Set Tuner IF Gain ----------
	// if err = SetTunerIfGain(stage, gain: int); err == nil {
	// 	log.Printf("\SetTunerIfGain Successful\n")
	// } else {
	// 	log.Printf("\tSetTunerIfGain Failed - error: %s\n", err)
	// }

	//---------- Get/Set test mode ----------
	if err = dev.SetTestMode(true); err == nil {
		log.Printf("\tSetTestMode 'On' Successful\n")
	} else {
		log.Printf("\tSetTestMode 'On' Failed - error: %s\n", err)
	}

	if err = dev.SetTestMode(false); err == nil {
		log.Printf("\tSetTestMode 'Off' Successful\n")
	} else {
		log.Printf("\tSetTestMode 'Off' Fail - error: %s\n", err)
	}

	//---------- Get/Set misc. streaming ----------
	if err = dev.ResetBuffer(); err == nil {
		log.Printf("\tResetBuffer Successful\n")
	} else {
		log.Printf("\tResetBuffer Failed - error: %s\n", err)
	}

	var buffer []byte = make([]uint8, rtl.DefaultBufLength)
	n_read, err := dev.ReadSync(buffer, rtl.DefaultBufLength)
	if err != nil {
		log.Printf("\tReadSync Failed - error: %s\n", err)
	} else {
		log.Printf("\tReadSync %d\n", n_read)
	}
	if err == nil && n_read < rtl.DefaultBufLength {
		log.Printf("ReadSync short read, %d samples lost\n", rtl.DefaultBufLength-n_read)
	}

	// Note, ReadAsync blocks until CancelAsync is called, so spawn
	// a goroutine running in its own system thread that'll wait
	// for the async-read callback to signal when it's done.
	IQch := make(chan bool)
	go async_stop(dev, IQch)
	var userctx rtl.UserCtx = IQch
	err = dev.ReadAsync(rtlsdr_cb, &userctx, rtl.DefaultAsyncBufNumber, rtl.DefaultBufLength)
	if err == nil {
		log.Printf("\tReadAsync Successful\n")
	} else {
		log.Printf("\tReadAsync Fail - error: %s\n", err)
	}

	log.Printf("Exiting...\n")
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"io/ioutil"
	"log"
	"net/http/httptest"
	"os"
	"os/signal"
	"syscall"

	"github.com/johannesboyne/gofakes3"
	"github.com/johannesboyne/gofakes3/backend/s3bolt"
)

func main() {
	var (
		flagBoltFile = flag.String("db", "", "Path to boltdb file")
	)
	flag.Parse()

	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, os.Kill)
	defer cancel()

	logger := log.New(os.Stdout, "", 0)

	var dbpath string
	if *flagBoltFile != "" {
		dbpath = *flagBoltFile
	} else {
		f, err := ioutil.TempFile("", "")
		if err != nil {
			logger.Fatalf("tmpfile: %v", err)
		}
		f.Close()

		dbpath = f.Name()
		logger.Printf("DB file: %v", f.Name())
	}

	backend, err := s3bolt.NewFile(dbpath)
	if err != nil {
		logger.Fatalf("bolt: %v", err)
	}
	faker := gofakes3.New(backend)

	ts := httptest.NewServer(faker.Server())
	defer ts.Close()

	logger.Printf("s5cmd --endpoint-url %v", ts.URL)

	<-ctx.Done()
}
<commit_msg>s3-server: print db path<commit_after>package main

import (
	"context"
	"flag"
	"io/ioutil"
	"log"
	"net/http/httptest"
	"os"
	"os/signal"
	"syscall"

	"github.com/johannesboyne/gofakes3"
	"github.com/johannesboyne/gofakes3/backend/s3bolt"
)

func main() {
	var (
		flagBoltFile = flag.String("db", "", "Path to boltdb file")
	)
	flag.Parse()

	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, os.Kill)
	defer cancel()

	logger := log.New(os.Stdout, "", 0)

	var dbpath string
	if *flagBoltFile != "" {
		dbpath = *flagBoltFile
	} else {
		f, err := ioutil.TempFile("", "")
		if err != nil {
			logger.Fatalf("tmpfile: %v", err)
		}
		f.Close()

		dbpath = f.Name()
	}

	logger.Printf("DB file: %v", dbpath)

	backend, err := s3bolt.NewFile(dbpath)
	if err != nil {
		logger.Fatalf("bolt: %v", err)
	}
	faker := gofakes3.New(backend)

	ts := httptest.NewServer(faker.Server())
	defer ts.Close()

	logger.Printf("s5cmd --endpoint-url %v", ts.URL)

	<-ctx.Done()
}
<|endoftext|>


<commit_before>// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see <http://www.gnu.org/licenses/>.

package twitter

import (
	"fmt"
	"github.com/ChimeraCoder/anaconda"
	"github.com/nmeum/marvin/irc"
	"github.com/nmeum/marvin/modules"
	"html"
	"net/url"
	"strconv"
	"strings"
)

// Maximum amount of characters allowed in a tweet.
const maxChars = 140

type Module struct {
	api               *anaconda.TwitterApi
	ReadOnly          bool   `json:"read_only"`
	ConsumerKey       string `json:"consumer_key"`
	ConsumerSecret    string `json:"consumer_secret"`
	AccessToken       string `json:"access_token"`
	AccessTokenSecret string `json:"access_token_secret"`
}

func Init(moduleSet *modules.ModuleSet) {
	moduleSet.Register(new(Module))
}

func (m *Module) Name() string {
	return "twitter"
}

func (m *Module) Help() string {
	return "USAGE: !tweet TEXT || !reply ID TEXT || !directmsg USER TEXT || !retweet ID || !favorite ID"
}

func (m *Module) Defaults() {
	m.ReadOnly = false
}

func (m *Module) Load(client *irc.Client) error {
	anaconda.SetConsumerKey(m.ConsumerKey)
	anaconda.SetConsumerSecret(m.ConsumerSecret)
	m.api = anaconda.NewTwitterApi(m.AccessToken, m.AccessTokenSecret)

	if !m.ReadOnly {
		client.CmdHook("privmsg", m.tweetCmd)
		client.CmdHook("privmsg", m.replyCmd)
		client.CmdHook("privmsg", m.retweetCmd)
		client.CmdHook("privmsg", m.favoriteCmd)
		client.CmdHook("privmsg", m.directMsgCmd)
	}

	values := url.Values{}
	values.Add("replies", "all")
	values.Add("with", "user")

	go func(c *irc.Client, v url.Values) {
		for {
			m.streamHandler(c, v)
		}
	}(client, values)

	return nil
}

func (m *Module) tweet(t string, v url.Values, c *irc.Client, p irc.Message) error {
	_, err := m.api.PostTweet(t, v)
	if err != nil && len(t) > maxChars {
		return c.Write("NOTICE %s :ERROR: Tweet is too long, remove %d characters",
			p.Receiver, len(t)-maxChars)
	} else if err != nil {
		return c.Write("NOTICE %s :ERROR: %s", p.Receiver, err.Error())
	} else {
		return nil
	}
}

func (m *Module) tweetCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 2 || splited[0] != "!tweet" || !client.Connected(msg.Receiver) {
		return nil
	}

	status := strings.Join(splited[1:], " ")
	return m.tweet(status, url.Values{}, client, msg)
}

func (m *Module) replyCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 3 || splited[0] != "!reply" || !client.Connected(msg.Receiver) {
		return nil
	}

	status := strings.Join(splited[2:], " ")
	if !strings.Contains(status, "@") {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, "A reply must contain an @mention")
	}

	values := url.Values{}
	values.Add("in_reply_to_status_id", splited[1])

	return m.tweet(status, values, client, msg)
}

func (m *Module) retweetCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 2 || splited[0] != "!retweet" || !client.Connected(msg.Receiver) {
		return nil
	}

	id, err := strconv.Atoi(splited[1])
	if err != nil {
		return err
	}

	if _, err := m.api.Retweet(int64(id), false); err != nil {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, err.Error())
	}

	return nil
}

func (m *Module) favoriteCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 2 || splited[0] != "!favorite" || !client.Connected(msg.Receiver) {
		return nil
	}

	id, err := strconv.Atoi(splited[1])
	if err != nil {
		return err
	}

	if _, err := m.api.Favorite(int64(id)); err != nil {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, err.Error())
	}

	return nil
}

func (m *Module) directMsgCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 3 || splited[0] != "!directmsg" || !client.Connected(msg.Receiver) {
		return nil
	}

	scname := splited[1]
	status := strings.Join(splited[2:], " ")

	if _, err := m.api.PostDMToScreenName(status, scname); err != nil {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, err.Error())
	}

	return nil
}

func (m *Module) streamHandler(client *irc.Client, values url.Values) {
	stream := m.api.UserStream(values)
	for {
		select {
		case event, ok := <-stream.C:
			if !ok {
				break
			}

			if t := m.formatEvent(event); len(t) > 0 {
				m.notify(client, t)
			}
		}
	}

	stream.Stop()
}

func (m *Module) formatEvent(event interface{}) string {
	var msg string
	switch t := event.(type) {
	case anaconda.ApiError:
		msg = fmt.Sprintf("Twitter API error %d: %s", t.StatusCode, t.Decoded.Error())
	case anaconda.StatusDeletionNotice:
		msg = fmt.Sprintf("Tweet %d has been deleted", t.Id)
	case anaconda.DirectMessage:
		msg = fmt.Sprintf("Direct message %d by %s: %s", t.Id,
			t.SenderScreenName, t.Text)
	case anaconda.Tweet:
		msg = fmt.Sprintf("Tweet %d by %s: %s", t.Id, t.User.ScreenName,
			html.UnescapeString(t.Text))
	case anaconda.EventTweet:
		if t.Event.Event != "favorite" {
			break
		}

		text := html.UnescapeString(t.TargetObject.Text)
		msg = fmt.Sprintf("%s favorited tweet %d by %s: %s",
			t.Source.ScreenName, t.TargetObject.Id, t.Target.ScreenName, text)
	}

	return msg
}

func (m *Module) notify(client *irc.Client, text string) {
	for _, ch := range client.Channels {
		client.Write("NOTICE %s :%s", ch, html.UnescapeString(text))
	}
}
<commit_msg>twitter: show recipient of direct message<commit_after>// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see <http://www.gnu.org/licenses/>.

package twitter

import (
	"fmt"
	"github.com/ChimeraCoder/anaconda"
	"github.com/nmeum/marvin/irc"
	"github.com/nmeum/marvin/modules"
	"html"
	"net/url"
	"strconv"
	"strings"
)

// Maximum amount of characters allowed in a tweet.
const maxChars = 140

type Module struct {
	api               *anaconda.TwitterApi
	ReadOnly          bool   `json:"read_only"`
	ConsumerKey       string `json:"consumer_key"`
	ConsumerSecret    string `json:"consumer_secret"`
	AccessToken       string `json:"access_token"`
	AccessTokenSecret string `json:"access_token_secret"`
}

func Init(moduleSet *modules.ModuleSet) {
	moduleSet.Register(new(Module))
}

func (m *Module) Name() string {
	return "twitter"
}

func (m *Module) Help() string {
	return "USAGE: !tweet TEXT || !reply ID TEXT || !directmsg USER TEXT || !retweet ID || !favorite ID"
}

func (m *Module) Defaults() {
	m.ReadOnly = false
}

func (m *Module) Load(client *irc.Client) error {
	anaconda.SetConsumerKey(m.ConsumerKey)
	anaconda.SetConsumerSecret(m.ConsumerSecret)
	m.api = anaconda.NewTwitterApi(m.AccessToken, m.AccessTokenSecret)

	if !m.ReadOnly {
		client.CmdHook("privmsg", m.tweetCmd)
		client.CmdHook("privmsg", m.replyCmd)
		client.CmdHook("privmsg", m.retweetCmd)
		client.CmdHook("privmsg", m.favoriteCmd)
		client.CmdHook("privmsg", m.directMsgCmd)
	}

	values := url.Values{}
	values.Add("replies", "all")
	values.Add("with", "user")

	go func(c *irc.Client, v url.Values) {
		for {
			m.streamHandler(c, v)
		}
	}(client, values)

	return nil
}

func (m *Module) tweet(t string, v url.Values, c *irc.Client, p irc.Message) error {
	_, err := m.api.PostTweet(t, v)
	if err != nil && len(t) > maxChars {
		return c.Write("NOTICE %s :ERROR: Tweet is too long, remove %d characters",
			p.Receiver, len(t)-maxChars)
	} else if err != nil {
		return c.Write("NOTICE %s :ERROR: %s", p.Receiver, err.Error())
	} else {
		return nil
	}
}

func (m *Module) tweetCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 2 || splited[0] != "!tweet" || !client.Connected(msg.Receiver) {
		return nil
	}

	status := strings.Join(splited[1:], " ")
	return m.tweet(status, url.Values{}, client, msg)
}

func (m *Module) replyCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 3 || splited[0] != "!reply" || !client.Connected(msg.Receiver) {
		return nil
	}

	status := strings.Join(splited[2:], " ")
	if !strings.Contains(status, "@") {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, "A reply must contain an @mention")
	}

	values := url.Values{}
	values.Add("in_reply_to_status_id", splited[1])

	return m.tweet(status, values, client, msg)
}

func (m *Module) retweetCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 2 || splited[0] != "!retweet" || !client.Connected(msg.Receiver) {
		return nil
	}

	id, err := strconv.Atoi(splited[1])
	if err != nil {
		return err
	}

	if _, err := m.api.Retweet(int64(id), false); err != nil {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, err.Error())
	}

	return nil
}

func (m *Module) favoriteCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 2 || splited[0] != "!favorite" || !client.Connected(msg.Receiver) {
		return nil
	}

	id, err := strconv.Atoi(splited[1])
	if err != nil {
		return err
	}

	if _, err := m.api.Favorite(int64(id)); err != nil {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, err.Error())
	}

	return nil
}

func (m *Module) directMsgCmd(client *irc.Client, msg irc.Message) error {
	splited := strings.Fields(msg.Data)
	if len(splited) < 3 || splited[0] != "!directmsg" || !client.Connected(msg.Receiver) {
		return nil
	}

	scname := splited[1]
	status := strings.Join(splited[2:], " ")

	if _, err := m.api.PostDMToScreenName(status, scname); err != nil {
		return client.Write("NOTICE %s :ERROR: %s",
			msg.Receiver, err.Error())
	}

	return nil
}

func (m *Module) streamHandler(client *irc.Client, values url.Values) {
	stream := m.api.UserStream(values)
	for {
		select {
		case event, ok := <-stream.C:
			if !ok {
				break
			}

			if t := m.formatEvent(event); len(t) > 0 {
				m.notify(client, t)
			}
		}
	}

	stream.Stop()
}

func (m *Module) formatEvent(event interface{}) string {
	var msg string
	switch t := event.(type) {
	case anaconda.ApiError:
		msg = fmt.Sprintf("Twitter API error %d: %s", t.StatusCode, t.Decoded.Error())
	case anaconda.StatusDeletionNotice:
		msg = fmt.Sprintf("Tweet %d has been deleted", t.Id)
	case anaconda.DirectMessage:
		msg = fmt.Sprintf("Direct message %d by %s send to %s: %s", t.Id,
			t.SenderScreenName, t.RecipientScreenName, t.Text)
	case anaconda.Tweet:
		msg = fmt.Sprintf("Tweet %d by %s: %s", t.Id, t.User.ScreenName,
			html.UnescapeString(t.Text))
	case anaconda.EventTweet:
		if t.Event.Event != "favorite" {
			break
		}

		text := html.UnescapeString(t.TargetObject.Text)
		msg = fmt.Sprintf("%s favorited tweet %d by %s: %s",
			t.Source.ScreenName, t.TargetObject.Id, t.Target.ScreenName, text)
	}

	return msg
}

func (m *Module) notify(client *irc.Client, text string) {
	for _, ch := range client.Channels {
		client.Write("NOTICE %s :%s", ch, html.UnescapeString(text))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// JobStorage is a fake Storage implementation that only provides job-relevant storage methods.
type JobStorage struct {
	NullStorage

	Submitted SubmittedJob
	Query     JobQuery
}

func (storage *JobStorage) InsertJob(job SubmittedJob) (uint64, error) {
	storage.Submitted = job

	return 42, nil
}

func (storage *JobStorage) ListJobs(query JobQuery) ([]SubmittedJob, error) {
	storage.Query = query

	j0 := SubmittedJob{
		Job: Job{Command: `echo "1"`},
		JID: 11,
	}
	j1 := SubmittedJob{
		Job: Job{Command: `echo "2"`},
		JID: 22,
	}
	j2 := SubmittedJob{
		Job: Job{Command: `echo "3"`},
		JID: 33,
	}

	return []SubmittedJob{j0, j1, j2}, nil
}

func TestJobHandlerBadRequest(t *testing.T) {
	r, err := http.NewRequest("PUT", "https://localhost/api/jobs", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	w := httptest.NewRecorder()
	c := &Context{}

	JobHandler(c, w, r)

	hasError(t, w, http.StatusMethodNotAllowed, RhoError{
		Code:    CodeMethodNotSupported,
		Message: "Method not supported",
		Retry:   false,
	})
}

func TestSubmitJob(t *testing.T) {
	body := strings.NewReader(`
	{
		"jobs": [{
			"cmd": "id",
			"name": "wat",
			"result_source": "stdout",
			"result_type": "binary"
		}]
	}
	`)
	r, err := http.NewRequest("POST", "https://localhost/api/jobs", body)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	s := &JobStorage{}
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
		Storage: s,
	}

	JobHandler(c, w, r)

	if w.Code != http.StatusOK {
		t.Errorf("Unexpected HTTP status: [%d]", w.Code)
	}

	var response struct {
		JIDs []uint `json:"jids"`
	}
	out := w.Body.Bytes()
	if err := json.Unmarshal(out, &response); err != nil {
		t.Fatalf("Unable to parse response body as JSON: [%s]", string(out))
	}
	if len(response.JIDs) != 1 {
		t.Fatalf("Expected one JID, received [%d]", len(response.JIDs))
	}
	if response.JIDs[0] != 42 {
		t.Errorf("Expected to be assigned ID 42, got [%d]", response.JIDs[0])
	}

	if s.Submitted.Account != "admin" {
		t.Errorf("Expected submitted job to belong to admin, not [%s]", s.Submitted.Account)
	}
	if s.Submitted.Status != StatusQueued {
		t.Errorf("Expected submitted job to be in state queued, not [%s]", s.Submitted.Status)
	}

	if s.Submitted.CreatedAt == 0 {
		t.Error("Expected the job's CreatedAt time to be populated.")
	}
	if s.Submitted.StartedAt != 0 {
		t.Errorf("Expected the job's StartedAt time to be zero, but was [%s]", s.Submitted.StartedAt)
	}
	if s.Submitted.FinishedAt != 0 {
		t.Errorf("Expected the job's FinishedAt time to be zero, but was [%s]", s.Submitted.FinishedAt)
	}
}

func TestSubmitJobBadResultSource(t *testing.T) {
	body := strings.NewReader(`
	{
		"jobs": [{
			"cmd": "id",
			"name": "wat",
			"result_source": "magic",
			"result_type": "binary"
		}]
	}
	`)
	r, err := http.NewRequest("POST", "https://localhost/api/jobs", body)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
	}

	JobHandler(c, w, r)

	hasError(t, w, http.StatusBadRequest, RhoError{
		Code:    CodeInvalidResultSource,
		Message: "Invalid result source [magic]",
		Retry:   false,
	})
}

func TestSubmitJobBadResultType(t *testing.T) {
	body := strings.NewReader(`
	{
		"jobs": [{
			"cmd": "id",
			"name": "wat",
			"result_source": "stdout",
			"result_type": "elsewhere"
		}]
	}
	`)
	r, err := http.NewRequest("POST", "https://localhost/api/jobs", body)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
	}

	JobHandler(c, w, r)

	hasError(t, w, http.StatusBadRequest, RhoError{
		Code:    CodeInvalidResultType,
		Message: "Invalid result type [elsewhere]",
		Retry:   false,
	})
}

func TestListJobsAll(t *testing.T) {
	r, err := http.NewRequest("GET", "https://localhost/api/jobs", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	s := &JobStorage{}
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
		Storage: s,
	}

	JobHandler(c, w, r)

	if w.Code != http.StatusOK {
		t.Errorf("Unexpected HTTP status: [%d]", w.Code)
	}

	var response struct {
		Jobs []SubmittedJob `json:"jobs"`
	}
	out := w.Body.Bytes()
	if err := json.Unmarshal(out, &response); err != nil {
		t.Fatalf("Unable to parse response body as JSON: [%s]", string(out))
	}
	t.Logf("Response body:\n%s", out)

	if len(response.Jobs) != 3 {
		t.Fatalf("Unexpected number of jobs returned: [%d]", len(response.Jobs))
	}
	if cmd0 := response.Jobs[0].Command; cmd0 != `echo "1"` {
		t.Errorf(`Expected first job to have command 'echo "1"', had [%s]`, cmd0)
	}
	if cmd1 := response.Jobs[1].Command; cmd1 != `echo "2"` {
		t.Errorf(`Expected second job to have command 'echo "2"', had [%s]`, cmd1)
	}
	if cmd2 := response.Jobs[2].Command; cmd2 != `echo "3"` {
		t.Errorf(`Expected third job to have command 'echo "3"', had [%s]`, cmd2)
	}
}
<commit_msg>Test querying by JID.<commit_after>package main

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// JobStorage is a fake Storage implementation that only provides job-relevant storage methods.
type JobStorage struct {
	NullStorage

	Submitted SubmittedJob
	Query     JobQuery
}

func (storage *JobStorage) InsertJob(job SubmittedJob) (uint64, error) {
	storage.Submitted = job

	return 42, nil
}

func (storage *JobStorage) ListJobs(query JobQuery) ([]SubmittedJob, error) {
	storage.Query = query

	j0 := SubmittedJob{
		Job: Job{Command: `echo "1"`},
		JID: 11,
	}
	j1 := SubmittedJob{
		Job: Job{Command: `echo "2"`},
		JID: 22,
	}
	j2 := SubmittedJob{
		Job: Job{Command: `echo "3"`},
		JID: 33,
	}

	return []SubmittedJob{j0, j1, j2}, nil
}

func TestJobHandlerBadRequest(t *testing.T) {
	r, err := http.NewRequest("PUT", "https://localhost/api/jobs", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	w := httptest.NewRecorder()
	c := &Context{}

	JobHandler(c, w, r)

	hasError(t, w, http.StatusMethodNotAllowed, RhoError{
		Code:    CodeMethodNotSupported,
		Message: "Method not supported",
		Retry:   false,
	})
}

func TestSubmitJob(t *testing.T) {
	body := strings.NewReader(`
	{
		"jobs": [{
			"cmd": "id",
			"name": "wat",
			"result_source": "stdout",
			"result_type": "binary"
		}]
	}
	`)
	r, err := http.NewRequest("POST", "https://localhost/api/jobs", body)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	s := &JobStorage{}
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
		Storage: s,
	}

	JobHandler(c, w, r)

	if w.Code != http.StatusOK {
		t.Errorf("Unexpected HTTP status: [%d]", w.Code)
	}

	var response struct {
		JIDs []uint `json:"jids"`
	}
	out := w.Body.Bytes()
	if err := json.Unmarshal(out, &response); err != nil {
		t.Fatalf("Unable to parse response body as JSON: [%s]", string(out))
	}
	if len(response.JIDs) != 1 {
		t.Fatalf("Expected one JID, received [%d]", len(response.JIDs))
	}
	if response.JIDs[0] != 42 {
		t.Errorf("Expected to be assigned ID 42, got [%d]", response.JIDs[0])
	}

	if s.Submitted.Account != "admin" {
		t.Errorf("Expected submitted job to belong to admin, not [%s]", s.Submitted.Account)
	}
	if s.Submitted.Status != StatusQueued {
		t.Errorf("Expected submitted job to be in state queued, not [%s]", s.Submitted.Status)
	}

	if s.Submitted.CreatedAt == 0 {
		t.Error("Expected the job's CreatedAt time to be populated.")
	}
	if s.Submitted.StartedAt != 0 {
		t.Errorf("Expected the job's StartedAt time to be zero, but was [%s]", s.Submitted.StartedAt)
	}
	if s.Submitted.FinishedAt != 0 {
		t.Errorf("Expected the job's FinishedAt time to be zero, but was [%s]", s.Submitted.FinishedAt)
	}
}

func TestSubmitJobBadResultSource(t *testing.T) {
	body := strings.NewReader(`
	{
		"jobs": [{
			"cmd": "id",
			"name": "wat",
			"result_source": "magic",
			"result_type": "binary"
		}]
	}
	`)
	r, err := http.NewRequest("POST", "https://localhost/api/jobs", body)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
	}

	JobHandler(c, w, r)

	hasError(t, w, http.StatusBadRequest, RhoError{
		Code:    CodeInvalidResultSource,
		Message: "Invalid result source [magic]",
		Retry:   false,
	})
}

func TestSubmitJobBadResultType(t *testing.T) {
	body := strings.NewReader(`
	{
		"jobs": [{
			"cmd": "id",
			"name": "wat",
			"result_source": "stdout",
			"result_type": "elsewhere"
		}]
	}
	`)
	r, err := http.NewRequest("POST", "https://localhost/api/jobs", body)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
	}

	JobHandler(c, w, r)

	hasError(t, w, http.StatusBadRequest, RhoError{
		Code:    CodeInvalidResultType,
		Message: "Invalid result type [elsewhere]",
		Retry:   false,
	})
}

func TestListJobsAll(t *testing.T) {
	r, err := http.NewRequest("GET", "https://localhost/api/jobs", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	s := &JobStorage{}
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
		Storage: s,
	}

	JobHandler(c, w, r)

	if w.Code != http.StatusOK {
		t.Errorf("Unexpected HTTP status: [%d]", w.Code)
	}

	var response struct {
		Jobs []SubmittedJob `json:"jobs"`
	}
	out := w.Body.Bytes()
	if err := json.Unmarshal(out, &response); err != nil {
		t.Fatalf("Unable to parse response body as JSON: [%s]", string(out))
	}
	t.Logf("Response body:\n%s", out)

	if len(response.Jobs) != 3 {
		t.Fatalf("Unexpected number of jobs returned: [%d]", len(response.Jobs))
	}
	if cmd0 := response.Jobs[0].Command; cmd0 != `echo "1"` {
		t.Errorf(`Expected first job to have command 'echo "1"', had [%s]`, cmd0)
	}
	if cmd1 := response.Jobs[1].Command; cmd1 != `echo "2"` {
		t.Errorf(`Expected second job to have command 'echo "2"', had [%s]`, cmd1)
	}
	if cmd2 := response.Jobs[2].Command; cmd2 != `echo "3"` {
		t.Errorf(`Expected third job to have command 'echo "3"', had [%s]`, cmd2)
	}
}

func jobListQuery(t *testing.T, url string) JobQuery {
	r, err := http.NewRequest("GET", url, nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}
	r.SetBasicAuth("admin", "12345")
	w := httptest.NewRecorder()
	s := &JobStorage{}
	c := &Context{
		Settings: Settings{
			AdminName: "admin",
			AdminKey:  "12345",
		},
		Storage: s,
	}

	JobHandler(c, w, r)

	return s.Query
}

func TestListJobsBySingleID(t *testing.T) {
	q := jobListQuery(t, "https://localhost/api/jobs?jid=123")

	if len(q.JIDs) != 1 {
		t.Errorf("Expected a single JID, got [%v]", q.JIDs)
	}
	if q.JIDs[0] != 123 {
		t.Errorf("Expected JID to be 123, got [%d]", q.JIDs[0])
	}

	if q.Limit != 1000 {
		t.Errorf("Expected limit to default to 1000, got [%d]", q.Limit)
	}
}

func TestListJobsByMultipleIDs(t *testing.T) {
	q := jobListQuery(t, "https://localhost/api/jobs?jid=123&jid=456&jid=789")

	if len(q.JIDs) != 3 {
		t.Errorf("Expected three JIDs, got [%v]", q.JIDs)
	}
	for i, expected := range []uint64{123, 456, 789} {
		if q.JIDs[i] != expected {
			t.Errorf("Expected [%d] for element %d, got [%d]", expected, i, q.JIDs[i])
		}
	}
}

func TestListJobsByName(t *testing.T) {
	t.Fatalf("pending")
}

func TestListJobsClampLimit(t *testing.T) {
	t.Fatalf("pending")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/madebymany/tilly/Godeps/_workspace/src/github.com/abourget/slack"
	"sync"
	"time"
)

type Standup struct {
	Questions         []string
	Finished          bool
	Channel           slack.Channel
	Duration          time.Duration
	client            *AuthedSlack
	userIds           []string
	userManager       *UserManager
	userReplies       map[*User]userReply
	userRepliesMutex  sync.Mutex
	finishedChan      chan struct{}
	reportedWaitGroup *sync.WaitGroup
}

type userReply interface {
	isUserReply()
}

type userAbsentReply struct{}
type userAnswersReply []string
type userSkippedReply struct{}
type userErrorReply struct{}

func (r userAbsentReply) isUserReply() {
}

func (r userAnswersReply) isUserReply() {
}

func (r userAnswersReply) isCompleted() bool {
	for _, a := range r {
		if a == "" {
			return false
		}
	}
	return true
}

func (r userSkippedReply) isUserReply() {
}

func (r userErrorReply) isUserReply() {
}

func NewStandup(client *AuthedSlack, channel slack.Channel, userManager *UserManager, reportedWaitGroup *sync.WaitGroup) (s *Standup) {

	reportedWaitGroup.Add(1)

	s = &Standup{
		client:            client,
		Channel:           channel,
		userManager:       userManager,
		userReplies:       make(map[*User]userReply),
		Questions:         Questions,
		finishedChan:      make(chan struct{}, 1),
		Duration:          StandupTimeMinutes * time.Minute,
		reportedWaitGroup: reportedWaitGroup,
	}

	s.userIds = make([]string, 0, len(s.Channel.Members))
	for _, userId := range s.Channel.Members {
		if userId != s.client.UserId {
			s.userIds = append(s.userIds, userId)
		}
	}

	return s
}

func (self *Standup) Run() {
	for _, userId := range self.userIds {
		self.userManager.StartStandup(self, userId)
	}
	go self.startTheClock()

	_ = <-self.finishedChan
	self.Finished = true

	var msg bytes.Buffer

	msg.WriteString("<!everyone>: *BARKBARKBARK Stand-up done!*\nQuestions were:\n")
	for _, q := range self.Questions {
		msg.WriteString("• ")
		msg.WriteString(q)
		msg.WriteString("\n")
	}
	msg.WriteString("\n")

	for user, anyReply := range self.userReplies {
		userName := fmt.Sprintf("<@%s|%s>", user.Info.Id, user.Info.Name)
		switch reply := anyReply.(type) {
		case userAnswersReply:
			msg.WriteString(userName)
			msg.WriteString(" answered:\n")
			for _, a := range reply {
				msg.WriteString("• ")
				msg.WriteString(a)
				msg.WriteString("\n")
			}
		case userAbsentReply:
			msg.WriteString(userName)
			msg.WriteString(" never replied to me :disappointed:")
		case userSkippedReply:
			msg.WriteString(userName)
			msg.WriteString(" skipped this stand-up.")
		case userErrorReply:
			msg.WriteString("There was an error when trying to chat with ")
			msg.WriteString(userName)
		default:
			msg.WriteString("I don't know what ")
			msg.WriteString(userName)
			msg.WriteString(" did. It is a mystery to me. :no_mouth:")
		}
		msg.WriteString("\n")
	}

	var params slack.PostMessageParameters
	params = DefaultMessageParameters
	params.Parse = "none"
	params.LinkNames = 0
	params.EscapeText = false
	self.client.PostMessage(self.Channel.Id, msg.String(), params)

	self.reportedWaitGroup.Done()
}

func (self *Standup) ReportUserAcknowledged(u *User) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	self.userReplies[u] = userAbsentReply{}
	// don't check for completion, we're only just starting
}

func (self *Standup) ReportUserAnswer(u *User, qidx int, answer string) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	DebugLog.Printf("got answer from user %s: %s", u.Info.Name, answer)
	reply, replyExists := self.userReplies[u]
	if _, isAbsent := reply.(userAbsentReply); !replyExists || isAbsent {
		reply = make(userAnswersReply, len(self.Questions))
		self.userReplies[u] = reply
	}
	if answers, ok := reply.(userAnswersReply); ok {
		answers[qidx] = answer
	}

	self.checkFinished()
}

func (self *Standup) ReportUserError(u *User) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	self.userReplies[u] = userErrorReply{}
	self.checkFinished()
}

func (self *Standup) ReportUserSkip(u *User) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	self.userReplies[u] = userSkippedReply{}
	self.checkFinished()
}

func (self *Standup) IsLastQuestion(i int) bool {
	return i >= len(self.Questions)-1
}

func (self *Standup) startTheClock() {
	time.Sleep(self.Duration)

	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	for user, _ := range self.userReplies {
		user.StandupTimeUp(self)
	}

	self.finish()
}

func (self *Standup) finish() {
	self.finishedChan <- struct{}{}
}

func (self *Standup) isFinished() bool {
	if len(self.userIds) != len(self.userReplies) {
		return false
	}
	for _, reply := range self.userReplies {
		switch r := reply.(type) {
		case userAnswersReply:
			if !r.isCompleted() {
				return false
			}
		case userAbsentReply:
			return false
		}
	}
	return true
}

func (self *Standup) checkFinished() {
	if self.isFinished() {
		self.finish()
	}
}
<commit_msg>Format missed questions nicer<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/madebymany/tilly/Godeps/_workspace/src/github.com/abourget/slack"
	"sync"
	"time"
)

type Standup struct {
	Questions         []string
	Finished          bool
	Channel           slack.Channel
	Duration          time.Duration
	client            *AuthedSlack
	userIds           []string
	userManager       *UserManager
	userReplies       map[*User]userReply
	userRepliesMutex  sync.Mutex
	finishedChan      chan struct{}
	reportedWaitGroup *sync.WaitGroup
}

type userReply interface {
	isUserReply()
}

type userAbsentReply struct{}
type userAnswersReply []string
type userSkippedReply struct{}
type userErrorReply struct{}

func (r userAbsentReply) isUserReply() {
}

func (r userAnswersReply) isUserReply() {
}

func (r userAnswersReply) isCompleted() bool {
	for _, a := range r {
		if a == "" {
			return false
		}
	}
	return true
}

func (r userSkippedReply) isUserReply() {
}

func (r userErrorReply) isUserReply() {
}

func NewStandup(client *AuthedSlack, channel slack.Channel, userManager *UserManager, reportedWaitGroup *sync.WaitGroup) (s *Standup) {

	reportedWaitGroup.Add(1)

	s = &Standup{
		client:            client,
		Channel:           channel,
		userManager:       userManager,
		userReplies:       make(map[*User]userReply),
		Questions:         Questions,
		finishedChan:      make(chan struct{}, 1),
		Duration:          StandupTimeMinutes * time.Minute,
		reportedWaitGroup: reportedWaitGroup,
	}

	s.userIds = make([]string, 0, len(s.Channel.Members))
	for _, userId := range s.Channel.Members {
		if userId != s.client.UserId {
			s.userIds = append(s.userIds, userId)
		}
	}

	return s
}

func (self *Standup) Run() {
	for _, userId := range self.userIds {
		self.userManager.StartStandup(self, userId)
	}
	go self.startTheClock()

	_ = <-self.finishedChan
	self.Finished = true

	var msg bytes.Buffer

	msg.WriteString("<!everyone>: *BARKBARKBARK Stand-up done!*\nQuestions were:\n")
	for _, q := range self.Questions {
		msg.WriteString("• ")
		msg.WriteString(q)
		msg.WriteString("\n")
	}
	msg.WriteString("\n")

	for user, anyReply := range self.userReplies {
		userName := fmt.Sprintf("<@%s|%s>", user.Info.Id, user.Info.Name)
		switch reply := anyReply.(type) {
		case userAnswersReply:
			msg.WriteString(userName)
			msg.WriteString(" answered:\n")
			for _, a := range reply {
				if a == "" {
					msg.WriteString("but didn't respond to the rest.\n")
					break
				}
				msg.WriteString("• ")
				msg.WriteString(a)
				msg.WriteString("\n")
			}
		case userAbsentReply:
			msg.WriteString(userName)
			msg.WriteString(" never replied to me :disappointed:")
		case userSkippedReply:
			msg.WriteString(userName)
			msg.WriteString(" skipped this stand-up.")
		case userErrorReply:
			msg.WriteString("There was an error when trying to chat with ")
			msg.WriteString(userName)
		default:
			msg.WriteString("I don't know what ")
			msg.WriteString(userName)
			msg.WriteString(" did. It is a mystery to me. :no_mouth:")
		}
		msg.WriteString("\n")
	}

	var params slack.PostMessageParameters
	params = DefaultMessageParameters
	params.Parse = "none"
	params.LinkNames = 0
	params.EscapeText = false
	self.client.PostMessage(self.Channel.Id, msg.String(), params)

	self.reportedWaitGroup.Done()
}

func (self *Standup) ReportUserAcknowledged(u *User) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	self.userReplies[u] = userAbsentReply{}
	// don't check for completion, we're only just starting
}

func (self *Standup) ReportUserAnswer(u *User, qidx int, answer string) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	DebugLog.Printf("got answer from user %s: %s", u.Info.Name, answer)
	reply, replyExists := self.userReplies[u]
	if _, isAbsent := reply.(userAbsentReply); !replyExists || isAbsent {
		reply = make(userAnswersReply, len(self.Questions))
		self.userReplies[u] = reply
	}
	if answers, ok := reply.(userAnswersReply); ok {
		answers[qidx] = answer
	}

	self.checkFinished()
}

func (self *Standup) ReportUserError(u *User) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	self.userReplies[u] = userErrorReply{}
	self.checkFinished()
}

func (self *Standup) ReportUserSkip(u *User) {
	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	self.userReplies[u] = userSkippedReply{}
	self.checkFinished()
}

func (self *Standup) IsLastQuestion(i int) bool {
	return i >= len(self.Questions)-1
}

func (self *Standup) startTheClock() {
	time.Sleep(self.Duration)

	self.userRepliesMutex.Lock()
	defer self.userRepliesMutex.Unlock()

	for user, _ := range self.userReplies {
		user.StandupTimeUp(self)
	}

	self.finish()
}

func (self *Standup) finish() {
	self.finishedChan <- struct{}{}
}

func (self *Standup) isFinished() bool {
	if len(self.userIds) != len(self.userReplies) {
		return false
	}
	for _, reply := range self.userReplies {
		switch r := reply.(type) {
		case userAnswersReply:
			if !r.isCompleted() {
				return false
			}
		case userAbsentReply:
			return false
		}
	}
	return true
}

func (self *Standup) checkFinished() {
	if self.isFinished() {
		self.finish()
	}
}
<|endoftext|>


<commit_before>package filesys

import (
	"context"
	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"path/filepath"
	"os"
	"time"
	"github.com/chrislusf/seaweedfs/weed/filer2"
)

var _ = fs.Node(&File{})
var _ = fs.NodeOpener(&File{})
var _ = fs.NodeFsyncer(&File{})
var _ = fs.NodeSetattrer(&File{})

type File struct {
	Chunks     []*filer_pb.FileChunk
	Name       string
	dir        *Dir
	wfs        *WFS
	attributes *filer_pb.FuseAttributes
}

func (file *File) Attr(context context.Context, attr *fuse.Attr) error {

	fullPath := filepath.Join(file.dir.Path, file.Name)
	item := file.wfs.listDirectoryEntriesCache.Get(fullPath)
	if item != nil {
		entry := item.Value().(*filer_pb.Entry)
		file.Chunks = entry.Chunks
		file.attributes = entry.Attributes
		glog.V(1).Infof("file attr read cached %v attributes", file.Name)
	} else {
		err := file.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

			request := &filer_pb.GetEntryAttributesRequest{
				Name:      file.Name,
				ParentDir: file.dir.Path,
			}

			resp, err := client.GetEntryAttributes(context, request)
			if err != nil {
				glog.V(0).Infof("file attr read file %v: %v", request, err)
				return err
			}

			file.attributes = resp.Attributes
			file.Chunks = resp.Chunks

			glog.V(1).Infof("file attr %v %+v: %d", fullPath, file.attributes, filer2.TotalSize(file.Chunks))

			return nil
		})

		if err != nil {
			return err
		}
	}

	attr.Mode = os.FileMode(file.attributes.FileMode)
	attr.Size = filer2.TotalSize(file.Chunks)
	attr.Mtime = time.Unix(file.attributes.Mtime, 0)
	attr.Gid = file.attributes.Gid
	attr.Uid = file.attributes.Uid

	return nil

}

func (file *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {

	fullPath := filepath.Join(file.dir.Path, file.Name)

	glog.V(3).Infof("%v file open %+v", fullPath, req)

	return &FileHandle{
		f:         file,
		RequestId: req.Header.ID,
		NodeId:    req.Header.Node,
		Uid:       req.Uid,
		Gid:       req.Gid,
	}, nil

}

func (file *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
	fullPath := filepath.Join(file.dir.Path, file.Name)

	glog.V(3).Infof("%v file setattr %+v", fullPath, req)
	if req.Valid.Size() {

		glog.V(3).Infof("%v file setattr set size=%v", fullPath, req.Size)
		if req.Size == 0 {
			// fmt.Printf("truncate %v \n", fullPath)
			file.Chunks = nil
		}
		file.attributes.FileSize = req.Size
	}
	if req.Valid.Mode() {
		file.attributes.FileMode = uint32(req.Mode)
	}

	if req.Valid.Uid() {
		file.attributes.Uid = req.Uid
	}

	if req.Valid.Gid() {
		file.attributes.Gid = req.Gid
	}

	if req.Valid.Mtime() {
		file.attributes.Mtime = req.Mtime.Unix()
	}

	return nil

}

func (file *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
	// fsync works at OS level
	// write the file chunks to the filer
	glog.V(3).Infof("%s/%s fsync file %+v", file.dir.Path, file.Name, req)

	return nil
}
<commit_msg>use existing attributes instead of fetching from filer<commit_after>package filesys

import (
	"context"
	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"path/filepath"
	"os"
	"time"
	"github.com/chrislusf/seaweedfs/weed/filer2"
)

var _ = fs.Node(&File{})
var _ = fs.NodeOpener(&File{})
var _ = fs.NodeFsyncer(&File{})
var _ = fs.NodeSetattrer(&File{})

type File struct {
	Chunks     []*filer_pb.FileChunk
	Name       string
	dir        *Dir
	wfs        *WFS
	attributes *filer_pb.FuseAttributes
}

func (file *File) Attr(context context.Context, attr *fuse.Attr) error {

	fullPath := filepath.Join(file.dir.Path, file.Name)

	if file.attributes == nil {
		item := file.wfs.listDirectoryEntriesCache.Get(fullPath)
		if item != nil {
			entry := item.Value().(*filer_pb.Entry)
			file.Chunks = entry.Chunks
			file.attributes = entry.Attributes
			glog.V(1).Infof("file attr read cached %v attributes", file.Name)
		} else {
			err := file.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

				request := &filer_pb.GetEntryAttributesRequest{
					Name:      file.Name,
					ParentDir: file.dir.Path,
				}

				resp, err := client.GetEntryAttributes(context, request)
				if err != nil {
					glog.V(0).Infof("file attr read file %v: %v", request, err)
					return err
				}

				file.attributes = resp.Attributes
				file.Chunks = resp.Chunks

				glog.V(1).Infof("file attr %v %+v: %d", fullPath, file.attributes, filer2.TotalSize(file.Chunks))

				return nil
			})

			if err != nil {
				return err
			}
		}
	}

	attr.Mode = os.FileMode(file.attributes.FileMode)
	attr.Size = filer2.TotalSize(file.Chunks)
	attr.Mtime = time.Unix(file.attributes.Mtime, 0)
	attr.Gid = file.attributes.Gid
	attr.Uid = file.attributes.Uid

	return nil

}

func (file *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {

	fullPath := filepath.Join(file.dir.Path, file.Name)

	glog.V(3).Infof("%v file open %+v", fullPath, req)

	return &FileHandle{
		f:         file,
		RequestId: req.Header.ID,
		NodeId:    req.Header.Node,
		Uid:       req.Uid,
		Gid:       req.Gid,
	}, nil

}

func (file *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
	fullPath := filepath.Join(file.dir.Path, file.Name)

	glog.V(3).Infof("%v file setattr %+v", fullPath, req)
	if req.Valid.Size() {

		glog.V(3).Infof("%v file setattr set size=%v", fullPath, req.Size)
		if req.Size == 0 {
			// fmt.Printf("truncate %v \n", fullPath)
			file.Chunks = nil
		}
		file.attributes.FileSize = req.Size
	}
	if req.Valid.Mode() {
		file.attributes.FileMode = uint32(req.Mode)
	}

	if req.Valid.Uid() {
		file.attributes.Uid = req.Uid
	}

	if req.Valid.Gid() {
		file.attributes.Gid = req.Gid
	}

	if req.Valid.Mtime() {
		file.attributes.Mtime = req.Mtime.Unix()
	}

	return nil

}

func (file *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
	// fsync works at OS level
	// write the file chunks to the filer
	glog.V(3).Infof("%s/%s fsync file %+v", file.dir.Path, file.Name, req)

	return nil
}
<|endoftext|>