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

package docker

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"net/http"
	"net/http/httputil"
	"sync"
	"sync/atomic"
	"time"
)

// APIEvents represents events coming from the Docker API
// The fields in the Docker API changed in API version 1.22, and
// events for more than images and containers are now fired off.
// To maintain forward and backward compatibility, go-dockerclient
// replicates the event in both the new and old format as faithfully as possible.
//
// For events that only exist in 1.22 in later, `Status` is filled in as
// `"Type:Action"` instead of just `Action` to allow for older clients to
// differentiate and not break if they rely on the pre-1.22 Status types.
//
// The transformEvent method can be consulted for more information about how
// events are translated from new/old API formats
type APIEvents struct {
	// New API Fields in 1.22
	Action string   `json:"action,omitempty"`
	Type   string   `json:"type,omitempty"`
	Actor  APIActor `json:"actor,omitempty"`

	// Old API fields for < 1.22
	Status string `json:"status,omitempty"`
	ID     string `json:"id,omitempty"`
	From   string `json:"from,omitempty"`

	// Fields in both
	Time     int64 `json:"time,omitempty"`
	TimeNano int64 `json:"timeNano,omitempty"`
}

// APIActor represents an actor that accomplishes something for an event
type APIActor struct {
	ID         string            `json:"id,omitempty"`
	Attributes map[string]string `json:"attributes,omitempty"`
}

type eventMonitoringState struct {
	sync.RWMutex
	sync.WaitGroup
	enabled   bool
	lastSeen  *int64
	C         chan *APIEvents
	errC      chan error
	listeners []chan<- *APIEvents
}

const (
	maxMonitorConnRetries = 5
	retryInitialWaitTime  = 10.
)

var (
	// ErrNoListeners is the error returned when no listeners are available
	// to receive an event.
	ErrNoListeners = errors.New("no listeners present to receive event")

	// ErrListenerAlreadyExists is the error returned when the listerner already
	// exists.
	ErrListenerAlreadyExists = errors.New("listener already exists for docker events")

	// EOFEvent is sent when the event listener receives an EOF error.
	EOFEvent = &APIEvents{
		Type:   "EOF",
		Status: "EOF",
	}
)

// AddEventListener adds a new listener to container events in the Docker API.
//
// The parameter is a channel through which events will be sent.
func (c *Client) AddEventListener(listener chan<- *APIEvents) error {
	var err error
	if !c.eventMonitor.isEnabled() {
		err = c.eventMonitor.enableEventMonitoring(c)
		if err != nil {
			return err
		}
	}
	err = c.eventMonitor.addListener(listener)
	if err != nil {
		return err
	}
	return nil
}

// RemoveEventListener removes a listener from the monitor.
func (c *Client) RemoveEventListener(listener chan *APIEvents) error {
	err := c.eventMonitor.removeListener(listener)
	if err != nil {
		return err
	}
	if len(c.eventMonitor.listeners) == 0 {
		c.eventMonitor.disableEventMonitoring()
	}
	return nil
}

func (eventState *eventMonitoringState) addListener(listener chan<- *APIEvents) error {
	eventState.Lock()
	defer eventState.Unlock()
	if listenerExists(listener, &eventState.listeners) {
		return ErrListenerAlreadyExists
	}
	eventState.Add(1)
	eventState.listeners = append(eventState.listeners, listener)
	return nil
}

func (eventState *eventMonitoringState) removeListener(listener chan<- *APIEvents) error {
	eventState.Lock()
	defer eventState.Unlock()
	if listenerExists(listener, &eventState.listeners) {
		var newListeners []chan<- *APIEvents
		for _, l := range eventState.listeners {
			if l != listener {
				newListeners = append(newListeners, l)
			}
		}
		eventState.listeners = newListeners
		eventState.Add(-1)
	}
	return nil
}

func (eventState *eventMonitoringState) closeListeners() {
	for _, l := range eventState.listeners {
		close(l)
		eventState.Add(-1)
	}
	eventState.listeners = nil
}

func listenerExists(a chan<- *APIEvents, list *[]chan<- *APIEvents) bool {
	for _, b := range *list {
		if b == a {
			return true
		}
	}
	return false
}

func (eventState *eventMonitoringState) enableEventMonitoring(c *Client) error {
	eventState.Lock()
	defer eventState.Unlock()
	if !eventState.enabled {
		eventState.enabled = true
		var lastSeenDefault = int64(0)
		eventState.lastSeen = &lastSeenDefault
		eventState.C = make(chan *APIEvents, 100)
		eventState.errC = make(chan error, 1)
		go eventState.monitorEvents(c)
	}
	return nil
}

func (eventState *eventMonitoringState) disableEventMonitoring() error {
	eventState.Lock()
	defer eventState.Unlock()

	eventState.closeListeners()

	eventState.Wait()

	if eventState.enabled {
		eventState.enabled = false
		close(eventState.C)
		close(eventState.errC)
	}
	return nil
}

func (eventState *eventMonitoringState) monitorEvents(c *Client) {
	var err error
	for eventState.noListeners() {
		time.Sleep(10 * time.Millisecond)
	}
	if err = eventState.connectWithRetry(c); err != nil {
		// terminate if connect failed
		eventState.disableEventMonitoring()
		return
	}
	for eventState.isEnabled() {
		timeout := time.After(100 * time.Millisecond)
		select {
		case ev, ok := <-eventState.C:
			if !ok {
				return
			}
			if ev == EOFEvent {
				eventState.disableEventMonitoring()
				return
			}
			eventState.updateLastSeen(ev)
			go eventState.sendEvent(ev)
		case err = <-eventState.errC:
			if err == ErrNoListeners {
				eventState.disableEventMonitoring()
				return
			} else if err != nil {
				defer func() { go eventState.monitorEvents(c) }()
				return
			}
		case <-timeout:
			continue
		}
	}
}

func (eventState *eventMonitoringState) connectWithRetry(c *Client) error {
	var retries int
	var err error
	for err = c.eventHijack(atomic.LoadInt64(eventState.lastSeen), eventState.C, eventState.errC); err != nil && retries < maxMonitorConnRetries; retries++ {
		waitTime := int64(retryInitialWaitTime * math.Pow(2, float64(retries)))
		time.Sleep(time.Duration(waitTime) * time.Millisecond)
		err = c.eventHijack(atomic.LoadInt64(eventState.lastSeen), eventState.C, eventState.errC)
	}
	return err
}

func (eventState *eventMonitoringState) noListeners() bool {
	eventState.RLock()
	defer eventState.RUnlock()
	return len(eventState.listeners) == 0
}

func (eventState *eventMonitoringState) isEnabled() bool {
	eventState.RLock()
	defer eventState.RUnlock()
	return eventState.enabled
}

func (eventState *eventMonitoringState) sendEvent(event *APIEvents) {
	eventState.RLock()
	defer eventState.RUnlock()
	eventState.Add(1)
	defer eventState.Done()
	if eventState.enabled {
		if len(eventState.listeners) == 0 {
			eventState.errC <- ErrNoListeners
			return
		}

		for _, listener := range eventState.listeners {
			listener <- event
		}
	}
}

func (eventState *eventMonitoringState) updateLastSeen(e *APIEvents) {
	eventState.Lock()
	defer eventState.Unlock()
	if atomic.LoadInt64(eventState.lastSeen) < e.Time {
		atomic.StoreInt64(eventState.lastSeen, e.Time)
	}
}

func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan chan error) error {
	uri := "/events"
	if startTime != 0 {
		uri += fmt.Sprintf("?since=%d", startTime)
	}
	protocol := c.endpointURL.Scheme
	address := c.endpointURL.Path
	if protocol != "unix" {
		protocol = "tcp"
		address = c.endpointURL.Host
	}
	var dial net.Conn
	var err error
	if c.TLSConfig == nil {
		dial, err = c.Dialer.Dial(protocol, address)
	} else {
		dial, err = tlsDialWithDialer(c.Dialer, protocol, address, c.TLSConfig)
	}
	if err != nil {
		return err
	}
	conn := httputil.NewClientConn(dial, nil)
	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return err
	}
	res, err := conn.Do(req)
	if err != nil {
		return err
	}
	go func(res *http.Response, conn *httputil.ClientConn) {
		defer conn.Close()
		defer res.Body.Close()
		decoder := json.NewDecoder(res.Body)
		for {
			var event APIEvents
			if err = decoder.Decode(&event); err != nil {
				if err == io.EOF || err == io.ErrUnexpectedEOF {
					if c.eventMonitor.isEnabled() && c.eventMonitor.C == eventChan {
						// Signal that we're exiting.
						eventChan <- EOFEvent
					}
					break
				}
				errChan <- err
			}
			if event.Time == 0 {
				continue
			}
			if !c.eventMonitor.isEnabled() || c.eventMonitor.C != eventChan {
				return
			}
			transformEvent(&event)
			eventChan <- &event
		}
	}(res, conn)
	return nil
}

// transformEvent takes an event and determines what version it is from
// then populates both versions of the event
func transformEvent(event *APIEvents) {
	// if event version is <= 1.21 there will be no Action and no Type
	if event.Action == "" && event.Type == "" {
		event.Action = event.Status
		event.Actor.ID = event.ID
		event.Actor.Attributes = map[string]string{}
		switch event.Status {
		case "delete", "import", "pull", "push", "tag", "untag":
			event.Type = "image"
		default:
			event.Type = "container"
			if event.From != "" {
				event.Actor.Attributes["image"] = event.From
			}
		}
	} else {
		if event.Status == "" {
			if event.Type == "image" || event.Type == "container" {
				event.Status = event.Action
			} else {
				// Because just the Status has been overloaded with different Types
				// if an event is not for an image or a container, we prepend the type
				// to avoid problems for people relying on actions being only for
				// images and containers
				event.Status = event.Type + ":" + event.Action
			}
		}
		if event.ID == "" {
			event.ID = event.Actor.ID
		}
		if event.From == "" {
			event.From = event.Actor.Attributes["image"]
		}
	}
}
<commit_msg>event: simplify code and fix data race<commit_after>// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"net/http"
	"net/http/httputil"
	"sync"
	"sync/atomic"
	"time"
)

// APIEvents represents events coming from the Docker API
// The fields in the Docker API changed in API version 1.22, and
// events for more than images and containers are now fired off.
// To maintain forward and backward compatibility, go-dockerclient
// replicates the event in both the new and old format as faithfully as possible.
//
// For events that only exist in 1.22 in later, `Status` is filled in as
// `"Type:Action"` instead of just `Action` to allow for older clients to
// differentiate and not break if they rely on the pre-1.22 Status types.
//
// The transformEvent method can be consulted for more information about how
// events are translated from new/old API formats
type APIEvents struct {
	// New API Fields in 1.22
	Action string   `json:"action,omitempty"`
	Type   string   `json:"type,omitempty"`
	Actor  APIActor `json:"actor,omitempty"`

	// Old API fields for < 1.22
	Status string `json:"status,omitempty"`
	ID     string `json:"id,omitempty"`
	From   string `json:"from,omitempty"`

	// Fields in both
	Time     int64 `json:"time,omitempty"`
	TimeNano int64 `json:"timeNano,omitempty"`
}

// APIActor represents an actor that accomplishes something for an event
type APIActor struct {
	ID         string            `json:"id,omitempty"`
	Attributes map[string]string `json:"attributes,omitempty"`
}

type eventMonitoringState struct {
	sync.RWMutex
	sync.WaitGroup
	enabled   bool
	lastSeen  int64
	C         chan *APIEvents
	errC      chan error
	listeners []chan<- *APIEvents
}

const (
	maxMonitorConnRetries = 5
	retryInitialWaitTime  = 10.
)

var (
	// ErrNoListeners is the error returned when no listeners are available
	// to receive an event.
	ErrNoListeners = errors.New("no listeners present to receive event")

	// ErrListenerAlreadyExists is the error returned when the listerner already
	// exists.
	ErrListenerAlreadyExists = errors.New("listener already exists for docker events")

	// EOFEvent is sent when the event listener receives an EOF error.
	EOFEvent = &APIEvents{
		Type:   "EOF",
		Status: "EOF",
	}
)

// AddEventListener adds a new listener to container events in the Docker API.
//
// The parameter is a channel through which events will be sent.
func (c *Client) AddEventListener(listener chan<- *APIEvents) error {
	var err error
	if !c.eventMonitor.isEnabled() {
		err = c.eventMonitor.enableEventMonitoring(c)
		if err != nil {
			return err
		}
	}
	err = c.eventMonitor.addListener(listener)
	if err != nil {
		return err
	}
	return nil
}

// RemoveEventListener removes a listener from the monitor.
func (c *Client) RemoveEventListener(listener chan *APIEvents) error {
	err := c.eventMonitor.removeListener(listener)
	if err != nil {
		return err
	}
	if len(c.eventMonitor.listeners) == 0 {
		c.eventMonitor.disableEventMonitoring()
	}
	return nil
}

func (eventState *eventMonitoringState) addListener(listener chan<- *APIEvents) error {
	eventState.Lock()
	defer eventState.Unlock()
	if listenerExists(listener, &eventState.listeners) {
		return ErrListenerAlreadyExists
	}
	eventState.Add(1)
	eventState.listeners = append(eventState.listeners, listener)
	return nil
}

func (eventState *eventMonitoringState) removeListener(listener chan<- *APIEvents) error {
	eventState.Lock()
	defer eventState.Unlock()
	if listenerExists(listener, &eventState.listeners) {
		var newListeners []chan<- *APIEvents
		for _, l := range eventState.listeners {
			if l != listener {
				newListeners = append(newListeners, l)
			}
		}
		eventState.listeners = newListeners
		eventState.Add(-1)
	}
	return nil
}

func (eventState *eventMonitoringState) closeListeners() {
	for _, l := range eventState.listeners {
		close(l)
		eventState.Add(-1)
	}
	eventState.listeners = nil
}

func listenerExists(a chan<- *APIEvents, list *[]chan<- *APIEvents) bool {
	for _, b := range *list {
		if b == a {
			return true
		}
	}
	return false
}

func (eventState *eventMonitoringState) enableEventMonitoring(c *Client) error {
	eventState.Lock()
	defer eventState.Unlock()
	if !eventState.enabled {
		eventState.enabled = true
		atomic.StoreInt64(&eventState.lastSeen, 0)
		eventState.C = make(chan *APIEvents, 100)
		eventState.errC = make(chan error, 1)
		go eventState.monitorEvents(c)
	}
	return nil
}

func (eventState *eventMonitoringState) disableEventMonitoring() error {
	eventState.Lock()
	defer eventState.Unlock()

	eventState.closeListeners()

	eventState.Wait()

	if eventState.enabled {
		eventState.enabled = false
		close(eventState.C)
		close(eventState.errC)
	}
	return nil
}

func (eventState *eventMonitoringState) monitorEvents(c *Client) {
	var err error
	for eventState.noListeners() {
		time.Sleep(10 * time.Millisecond)
	}
	if err = eventState.connectWithRetry(c); err != nil {
		// terminate if connect failed
		eventState.disableEventMonitoring()
		return
	}
	for eventState.isEnabled() {
		timeout := time.After(100 * time.Millisecond)
		select {
		case ev, ok := <-eventState.C:
			if !ok {
				return
			}
			if ev == EOFEvent {
				eventState.disableEventMonitoring()
				return
			}
			eventState.updateLastSeen(ev)
			go eventState.sendEvent(ev)
		case err = <-eventState.errC:
			if err == ErrNoListeners {
				eventState.disableEventMonitoring()
				return
			} else if err != nil {
				defer func() { go eventState.monitorEvents(c) }()
				return
			}
		case <-timeout:
			continue
		}
	}
}

func (eventState *eventMonitoringState) connectWithRetry(c *Client) error {
	var retries int
	err := c.eventHijack(atomic.LoadInt64(&eventState.lastSeen), eventState.C, eventState.errC)
	for ; err != nil && retries < maxMonitorConnRetries; retries++ {
		waitTime := int64(retryInitialWaitTime * math.Pow(2, float64(retries)))
		time.Sleep(time.Duration(waitTime) * time.Millisecond)
		err = c.eventHijack(atomic.LoadInt64(&eventState.lastSeen), eventState.C, eventState.errC)
	}
	return err
}

func (eventState *eventMonitoringState) noListeners() bool {
	eventState.RLock()
	defer eventState.RUnlock()
	return len(eventState.listeners) == 0
}

func (eventState *eventMonitoringState) isEnabled() bool {
	eventState.RLock()
	defer eventState.RUnlock()
	return eventState.enabled
}

func (eventState *eventMonitoringState) sendEvent(event *APIEvents) {
	eventState.RLock()
	defer eventState.RUnlock()
	eventState.Add(1)
	defer eventState.Done()
	if eventState.enabled {
		if len(eventState.listeners) == 0 {
			eventState.errC <- ErrNoListeners
			return
		}

		for _, listener := range eventState.listeners {
			listener <- event
		}
	}
}

func (eventState *eventMonitoringState) updateLastSeen(e *APIEvents) {
	eventState.Lock()
	defer eventState.Unlock()
	if atomic.LoadInt64(&eventState.lastSeen) < e.Time {
		atomic.StoreInt64(&eventState.lastSeen, e.Time)
	}
}

func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan chan error) error {
	uri := "/events"
	if startTime != 0 {
		uri += fmt.Sprintf("?since=%d", startTime)
	}
	protocol := c.endpointURL.Scheme
	address := c.endpointURL.Path
	if protocol != "unix" {
		protocol = "tcp"
		address = c.endpointURL.Host
	}
	var dial net.Conn
	var err error
	if c.TLSConfig == nil {
		dial, err = c.Dialer.Dial(protocol, address)
	} else {
		dial, err = tlsDialWithDialer(c.Dialer, protocol, address, c.TLSConfig)
	}
	if err != nil {
		return err
	}
	conn := httputil.NewClientConn(dial, nil)
	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return err
	}
	res, err := conn.Do(req)
	if err != nil {
		return err
	}
	go func(res *http.Response, conn *httputil.ClientConn) {
		defer conn.Close()
		defer res.Body.Close()
		decoder := json.NewDecoder(res.Body)
		for {
			var event APIEvents
			if err = decoder.Decode(&event); err != nil {
				if err == io.EOF || err == io.ErrUnexpectedEOF {
					if c.eventMonitor.isEnabled() && c.eventMonitor.C == eventChan {
						// Signal that we're exiting.
						eventChan <- EOFEvent
					}
					break
				}
				errChan <- err
			}
			if event.Time == 0 {
				continue
			}
			if !c.eventMonitor.isEnabled() || c.eventMonitor.C != eventChan {
				return
			}
			transformEvent(&event)
			eventChan <- &event
		}
	}(res, conn)
	return nil
}

// transformEvent takes an event and determines what version it is from
// then populates both versions of the event
func transformEvent(event *APIEvents) {
	// if event version is <= 1.21 there will be no Action and no Type
	if event.Action == "" && event.Type == "" {
		event.Action = event.Status
		event.Actor.ID = event.ID
		event.Actor.Attributes = map[string]string{}
		switch event.Status {
		case "delete", "import", "pull", "push", "tag", "untag":
			event.Type = "image"
		default:
			event.Type = "container"
			if event.From != "" {
				event.Actor.Attributes["image"] = event.From
			}
		}
	} else {
		if event.Status == "" {
			if event.Type == "image" || event.Type == "container" {
				event.Status = event.Action
			} else {
				// Because just the Status has been overloaded with different Types
				// if an event is not for an image or a container, we prepend the type
				// to avoid problems for people relying on actions being only for
				// images and containers
				event.Status = event.Type + ":" + event.Action
			}
		}
		if event.ID == "" {
			event.ID = event.Actor.ID
		}
		if event.From == "" {
			event.From = event.Actor.Attributes["image"]
		}
	}
}
<|endoftext|>


<commit_before>package rmsd

import (
	"fmt"
	"math"

	"github.com/BurntSushi/bcbgo/pdb"

	matrix "github.com/BurntSushi/go.matrix"
)

// RMSD implements a version of the Kabsch alogrithm that is described here:
// http://cnx.org/content/m11608/latest/
//
// A brief, high-level overview:
//
// Build the 3xN matrices X and Y containing, for the sets x and y
// respectively, the coordinates for each of the N atoms after centering
// the atoms by subtracting the centroids.
//
// Compute the covariance matrix C=X(Y^T)
//
// Compute the SVD (Singular Value Decomposition) of C=VS(W^T)
//
// Compute d=sign(det(C))
//
// Compute the optimal rotation U as U = W([1 0 0] [0 1 0] [0 0 d])(V^T)
//
// (In the last step, we're using WT instead of W, since that's apparently
// what Fragbag does, and we're looking to imitate them. At least, at first!)
//
// Note that RMSD will panic if the lengths of struct1 and struct2 differ.
// RMSD will also panic if the calculation of the SVD returns an error. (It's
// possible that will change, though.)
func RMSD(struct1, struct2 pdb.Atoms) float64 {
	if len(struct1) != len(struct2) {
		panic(fmt.Sprintf("Computing the RMSD of two structures require that "+
			"they have equal length. But the lengths of the two structures "+
			"provided are %d and %d.", len(struct1), len(struct2)))
	}

	// This will disable any attempt at parallelizing matrix multiplication
	// when GOMAXPROCS > 1. It just isn't read for prime-time.
	matrix.WhichParMethod = 0

	// In order to "center" the coordinates, we
	// subtract the centroid for each set of atom coordinates.
	cx1, cy1, cz1 := centroid(struct1)
	cx2, cy2, cz2 := centroid(struct2)

	// Initialize the go.matrix values from the 3 dimensional ATOM coordinates.
	// We end up with two 3xN matrices (X and Y), where N is the length of
	// struct1 and struct2.
	els1 := make([]float64, 3*len(struct1))
	els2 := make([]float64, 3*len(struct2))
	for i := 0; i < len(struct1); i++ {
		o := len(struct1)
		a1, a2 := struct1[i].Coords, struct2[i].Coords
		els1[i+0*o] = a1[0] - cx1
		els1[i+1*o] = a1[1] - cy1
		els1[i+2*o] = a1[2] - cz1

		els2[i+0*o] = a2[0] - cx2
		els2[i+1*o] = a2[1] - cy2
		els2[i+2*o] = a2[2] - cz2
	}
	X := matrix.MakeDenseMatrix(els1, 3, len(struct1))
	Y := matrix.MakeDenseMatrix(els2, 3, len(struct2))

	// Compute the covariance matrix C = X(Y^T)
	C := must(X.TimesDense(Y.Transpose()))

	// Compute the Singular Value Decomposition of C = VS(W^T)
	//
	// N.B. I suspect that this is the most optimizable portion of this
	// RMSD calculation. If you take a look at the source for SVD (in
	// skelterjohn's go.matrix package), you'll notice that it is a nightmare.
	// I think that if we wrote an optimized implementation specifically for
	// 3x3 matrices, we'd end up squeezing a lot more juice. In particular,
	// we could use the cubic formula to calculate roots of cubic equations
	// in order to find the eigen{values,vectors}.
	V, _, WT, err := C.SVD()
	if err != nil {
		// I'm not quite sure if this is the right thing to do here.
		// It may be that we simply return an invalid RMSD instead.
		panic(err)
	}

	// If the determinant of C is negative, then we have to correct for
	// something called an "improper rotation" in that the matrix doesn't
	// constitute a "right handed system". To correct for it, we multiply
	// W by ( [1 0 0] [0 1 0] [0 0 -1] ). This makes the rotation "proper".
	// When the determinant is positive, we save some cycles and just
	// compute W(V^T)
	//
	// N.B. We are using WT here, even though the algorithm described at
	// http://cnx.org/content/m11608/latest/
	// calls for W. For whatever reason, Fragbag's algorithm seems to use
	// WT. (i.e., this approach matches its output precisely.)
	var U *matrix.DenseMatrix
	VT := V.Transpose()
	if C.Det() < 0 {
		adjust := matrix.MakeDenseMatrix([]float64{
			1, 0, 0,
			0, 1, 0,
			0, 0, -1,
		}, 3, 3)
		Wadjust := must(WT.TimesDense(adjust))
		U = must(Wadjust.TimesDense(VT))
	} else {
		U = must(WT.TimesDense(VT))
	}

	// Apply the rotational matrix U to X to get the best possible alignment
	// with Y.
	Xbest := must(U.TimesDense(X))

	// Now compute the RMSD between Xbest and Y.
	rmsd := 0.0
	for r := 0; r < 3; r++ {
		for c := 0; c < len(struct1); c++ {
			dist := Xbest.Get(r, c) - Y.Get(r, c)
			rmsd += dist * dist
		}
	}
	return math.Sqrt(rmsd / float64(len(struct1)))
}

// centroid calculates the average position of a set of atoms.
func centroid(atoms pdb.Atoms) (float64, float64, float64) {
	var xs, ys, zs float64
	for _, atom := range atoms {
		xs += atom.Coords[0]
		ys += atom.Coords[1]
		zs += atom.Coords[2]
	}
	n := float64(len(atoms))
	return xs / n, ys / n, zs / n
}

// must panics if the result of a dense matrix operation returns an error.
func must(A *matrix.DenseMatrix, err error) *matrix.DenseMatrix {
	if err != nil {
		panic(err)
	}
	return A
}
<commit_msg>skelterjohn merged my pull request that allows disabling of parallel matrix multiplication.<commit_after>package rmsd

import (
	"fmt"
	"math"

	"github.com/BurntSushi/bcbgo/pdb"

	matrix "github.com/skelterjohn/go.matrix"
)

// RMSD implements a version of the Kabsch alogrithm that is described here:
// http://cnx.org/content/m11608/latest/
//
// A brief, high-level overview:
//
// Build the 3xN matrices X and Y containing, for the sets x and y
// respectively, the coordinates for each of the N atoms after centering
// the atoms by subtracting the centroids.
//
// Compute the covariance matrix C=X(Y^T)
//
// Compute the SVD (Singular Value Decomposition) of C=VS(W^T)
//
// Compute d=sign(det(C))
//
// Compute the optimal rotation U as U = W([1 0 0] [0 1 0] [0 0 d])(V^T)
//
// (In the last step, we're using WT instead of W, since that's apparently
// what Fragbag does, and we're looking to imitate them. At least, at first!)
//
// Note that RMSD will panic if the lengths of struct1 and struct2 differ.
// RMSD will also panic if the calculation of the SVD returns an error. (It's
// possible that will change, though.)
func RMSD(struct1, struct2 pdb.Atoms) float64 {
	if len(struct1) != len(struct2) {
		panic(fmt.Sprintf("Computing the RMSD of two structures require that "+
			"they have equal length. But the lengths of the two structures "+
			"provided are %d and %d.", len(struct1), len(struct2)))
	}

	// This will disable any attempt at parallelizing matrix multiplication
	// when GOMAXPROCS > 1. It just isn't read for prime-time.
	matrix.WhichParMethod = 0

	// In order to "center" the coordinates, we
	// subtract the centroid for each set of atom coordinates.
	cx1, cy1, cz1 := centroid(struct1)
	cx2, cy2, cz2 := centroid(struct2)

	// Initialize the go.matrix values from the 3 dimensional ATOM coordinates.
	// We end up with two 3xN matrices (X and Y), where N is the length of
	// struct1 and struct2.
	els1 := make([]float64, 3*len(struct1))
	els2 := make([]float64, 3*len(struct2))
	for i := 0; i < len(struct1); i++ {
		o := len(struct1)
		a1, a2 := struct1[i].Coords, struct2[i].Coords
		els1[i+0*o] = a1[0] - cx1
		els1[i+1*o] = a1[1] - cy1
		els1[i+2*o] = a1[2] - cz1

		els2[i+0*o] = a2[0] - cx2
		els2[i+1*o] = a2[1] - cy2
		els2[i+2*o] = a2[2] - cz2
	}
	X := matrix.MakeDenseMatrix(els1, 3, len(struct1))
	Y := matrix.MakeDenseMatrix(els2, 3, len(struct2))

	// Compute the covariance matrix C = X(Y^T)
	C := must(X.TimesDense(Y.Transpose()))

	// Compute the Singular Value Decomposition of C = VS(W^T)
	//
	// N.B. I suspect that this is the most optimizable portion of this
	// RMSD calculation. If you take a look at the source for SVD (in
	// skelterjohn's go.matrix package), you'll notice that it is a nightmare.
	// I think that if we wrote an optimized implementation specifically for
	// 3x3 matrices, we'd end up squeezing a lot more juice. In particular,
	// we could use the cubic formula to calculate roots of cubic equations
	// in order to find the eigen{values,vectors}.
	V, _, WT, err := C.SVD()
	if err != nil {
		// I'm not quite sure if this is the right thing to do here.
		// It may be that we simply return an invalid RMSD instead.
		panic(err)
	}

	// If the determinant of C is negative, then we have to correct for
	// something called an "improper rotation" in that the matrix doesn't
	// constitute a "right handed system". To correct for it, we multiply
	// W by ( [1 0 0] [0 1 0] [0 0 -1] ). This makes the rotation "proper".
	// When the determinant is positive, we save some cycles and just
	// compute W(V^T)
	//
	// N.B. We are using WT here, even though the algorithm described at
	// http://cnx.org/content/m11608/latest/
	// calls for W. For whatever reason, Fragbag's algorithm seems to use
	// WT. (i.e., this approach matches its output precisely.)
	var U *matrix.DenseMatrix
	VT := V.Transpose()
	if C.Det() < 0 {
		adjust := matrix.MakeDenseMatrix([]float64{
			1, 0, 0,
			0, 1, 0,
			0, 0, -1,
		}, 3, 3)
		Wadjust := must(WT.TimesDense(adjust))
		U = must(Wadjust.TimesDense(VT))
	} else {
		U = must(WT.TimesDense(VT))
	}

	// Apply the rotational matrix U to X to get the best possible alignment
	// with Y.
	Xbest := must(U.TimesDense(X))

	// Now compute the RMSD between Xbest and Y.
	rmsd := 0.0
	for r := 0; r < 3; r++ {
		for c := 0; c < len(struct1); c++ {
			dist := Xbest.Get(r, c) - Y.Get(r, c)
			rmsd += dist * dist
		}
	}
	return math.Sqrt(rmsd / float64(len(struct1)))
}

// centroid calculates the average position of a set of atoms.
func centroid(atoms pdb.Atoms) (float64, float64, float64) {
	var xs, ys, zs float64
	for _, atom := range atoms {
		xs += atom.Coords[0]
		ys += atom.Coords[1]
		zs += atom.Coords[2]
	}
	n := float64(len(atoms))
	return xs / n, ys / n, zs / n
}

// must panics if the result of a dense matrix operation returns an error.
func must(A *matrix.DenseMatrix, err error) *matrix.DenseMatrix {
	if err != nil {
		panic(err)
	}
	return A
}
<|endoftext|>


<commit_before>package steamquery

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math"
	"net"
	"time"
)

type ServerType byte

const (
	ServerType_Dedicated     ServerType = 'd'
	ServerType_NonDedicated  ServerType = 'l'
	ServerType_SourceTVRelay ServerType = 'p'
)

type Environment byte

const (
	Environment_Linux   Environment = 'l'
	Environment_Windows Environment = 'w'
	Environment_Mac     Environment = 'm' // or 'o'
)

// https://developer.valvesoftware.com/wiki/Source_Server_Queries#Response_Format
type Info struct {
	Protocol    byte   // Protocol version used by the server.
	Name        string // Name of the server.
	Map         string // Map the server has currently loaded.
	Folder      string // Name of the folder containing the game files.
	Game        string // Full name of the game.
	ID          uint16 // Steam Application ID of game.
	Players     byte   // Number of players on the server.
	MaxPlayers  byte   // Maximum number of players the server reports it can hold.
	Bots        byte   // Number of bots on the server.
	ServerType  ServerType
	Environment Environment
	Private     bool
	VAC         bool
	Version     string // Version of the game installed on the server.
	Port        uint16 // The server's game port number.
	SteamID     uint64 // Server's SteamID.
	SourceTV    struct {
		Port uint16 // Spectator port number
		Name string // Name of the spectator server
	}
	Keywords string // Tags that describe the game according to the server
	GameID   uint64 // The server's 64-bit GameID.
}

type Player struct {
	Name     string
	Score    int32
	Duration time.Duration
}

type Rule struct {
	Name  string
	Value string
}

func QueryInfo(address *net.UDPAddr) (Info, time.Duration, error) {
	c, err := net.DialUDP("udp", nil, address)
	if err != nil {
		return Info{}, time.Duration(0), err
	}
	var buf [8 * 1024]byte
	c.SetReadDeadline(time.Now().Add(time.Second * 3))
	before := time.Now()
	c.Write([]byte("\xFF\xFF\xFF\xFFTSource Engine Query\x00"))
	n, err := c.Read(buf[:])
	ping := time.Since(before)
	c.Close()
	if err != nil {
		return Info{}, time.Duration(0), err
	}
	if n < 17 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	var r Info
	o := 5 // skip "\xFF\xFF\xFF\xFFI"
	r.Protocol = buf[o]
	o++
	nb := bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Name = string(buf[o : o+nb])
	o += nb + 1
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Map = string(buf[o : o+nb])
	o += nb + 1
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Folder = string(buf[o : o+nb])
	o += nb + 1
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Game = string(buf[o : o+nb])
	o += nb + 1
	r.ID = binary.LittleEndian.Uint16(buf[o:])
	o += 2
	r.Players = buf[o]
	o++
	r.MaxPlayers = buf[o]
	o++
	r.Bots = buf[o]
	o++
	r.ServerType = ServerType(buf[o])
	o++
	if buf[o] == 'o' {
		r.Environment = Environment_Mac
	} else {
		r.Environment = Environment(buf[o])
	}
	o++
	r.Private = buf[o] == 1
	o++
	r.VAC = buf[o] == 1
	o++
	// EXTRA STUFF FOR "The Ship" not handled here
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Version = string(buf[o : o+nb])
	o += nb + 1
	extra := buf[o]
	o++
	if extra&0x80 != 0 {
		r.Port = binary.LittleEndian.Uint16(buf[o:])
		o += 2
	}
	if extra&0x10 != 0 {
		r.SteamID = binary.LittleEndian.Uint64(buf[o:])
		o += 8
	}
	if extra&0x40 != 0 {
		r.SourceTV.Port = binary.LittleEndian.Uint16(buf[o:])
		o += 2
		nb = bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return Info{}, time.Duration(0), errors.New("got invalid response")
		}
		r.SourceTV.Name = string(buf[o : o+nb])
		o += nb + 1
	}
	if extra&0x20 != 0 {
		nb = bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return Info{}, time.Duration(0), errors.New("got invalid response")
		}
		r.Keywords = string(buf[o : o+nb])
		o += nb + 1
	}
	if extra&0x01 != 0 {
		r.GameID = binary.LittleEndian.Uint64(buf[o:])
		o += 8
	}
	return r, ping, nil
}

func QueryInfoString(address string) (Info, time.Duration, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return Info{}, time.Duration(0), err
	}
	return QueryInfo(addr)
}

func QueryPlayers(address *net.UDPAddr) ([]Player, time.Duration, error) {
	c, err := net.DialUDP("udp", nil, address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	c.SetReadDeadline(time.Now().Add(time.Second * 3))
	var buf [2 * 1024]byte
	before := time.Now()
	c.Write([]byte("\xFF\xFF\xFF\xFFU\xFF\xFF\xFF\xFF"))
	n, err := c.Read(buf[:])
	ping := time.Since(before)
	if err != nil {
		c.Close()
		return nil, time.Duration(0), err
	}
	if n == 9 && buf[4] == 'A' {
		buf[4] = 'U'
		c.Write(buf[:n])
		c.SetReadDeadline(time.Now().Add(time.Second * 3))
		n, err = c.Read(buf[:])
		if err != nil {
			c.Close()
			return nil, time.Duration(0), err
		}
	}
	c.Close()
	if n < len("\xFF\xFF\xFF\xFFD0") {
		return nil, time.Duration(0), errors.New("got invalid response")
	}
	o := 5 // skip "\xFF\xFF\xFF\xFFD"
	np := buf[o]
	o++
	players := make([]Player, 0, np)
	for i := byte(0); i < np; i++ {
		var p Player
		o++ // skip index
		nb := bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return nil, time.Duration(0), errors.New("got invalid response")
		}
		p.Name = string(buf[o : o+nb])
		o += nb + 1
		p.Score = int32(binary.LittleEndian.Uint32(buf[o:]))
		o += 4
		dur := math.Float32frombits(binary.LittleEndian.Uint32(buf[o:]))
		p.Duration = time.Duration(dur * 1000 * 1000 * 1000)
		o += 4
		if len(p.Name) != 0 {
			players = append(players, p)
		}
	}
	return players, ping, nil
}

func QueryPlayersString(address string) ([]Player, time.Duration, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	return QueryPlayers(addr)
}

func QueryRules(address *net.UDPAddr) ([]Rule, time.Duration, error) {
	c, err := net.DialUDP("udp", nil, address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	var buf [8 * 1024]byte
	c.SetReadDeadline(time.Now().Add(time.Second * 3))
	before := time.Now()
	c.Write([]byte("\xFF\xFF\xFF\xFFV\xFF\xFF\xFF\xFF"))
	n, err := c.Read(buf[:])
	ping := time.Since(before)
	if err != nil {
		c.Close()
		return nil, time.Duration(0), err
	}
	if n == 9 && buf[4] == 'A' {
		buf[4] = 'V'
		c.Write(buf[:n])
		c.SetReadDeadline(time.Now().Add(time.Second * 3))
		n, err = c.Read(buf[:])
		if err != nil {
			c.Close()
			return nil, time.Duration(0), err
		}
	}
	c.Close()
	if n < len("\xFF\xFF\xFF\xFFE00") {
		return nil, time.Duration(0), errors.New("got invalid response")
	}
	o := 5 // skip "\xFF\xFF\xFF\xFFE"
	nr := binary.LittleEndian.Uint16(buf[o:])
	o += 2
	rules := make([]Rule, 0, nr)
	for i := uint16(0); i < nr; i++ {
		var r Rule
		nb := bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return nil, time.Duration(0), errors.New("got invalid response")
		}
		r.Name = string(buf[o : o+nb])
		o += nb + 1
		nb = bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return nil, time.Duration(0), errors.New("got invalid response")
		}
		r.Value = string(buf[o : o+nb])
		o += nb + 1
		if len(r.Name) != 0 {
			rules = append(rules, r)
		}
	}
	return rules, ping, nil
}

func QueryRulesString(address string) ([]Rule, time.Duration, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	return QueryRules(addr)
}
<commit_msg>added challenge to QueryInfo<commit_after>package steamquery

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math"
	"net"
	"time"
)

type ServerType byte

const (
	ServerType_Dedicated     ServerType = 'd'
	ServerType_NonDedicated  ServerType = 'l'
	ServerType_SourceTVRelay ServerType = 'p'
)

type Environment byte

const (
	Environment_Linux   Environment = 'l'
	Environment_Windows Environment = 'w'
	Environment_Mac     Environment = 'm' // or 'o'
)

// https://developer.valvesoftware.com/wiki/Source_Server_Queries#Response_Format
type Info struct {
	Protocol    byte   // Protocol version used by the server.
	Name        string // Name of the server.
	Map         string // Map the server has currently loaded.
	Folder      string // Name of the folder containing the game files.
	Game        string // Full name of the game.
	ID          uint16 // Steam Application ID of game.
	Players     byte   // Number of players on the server.
	MaxPlayers  byte   // Maximum number of players the server reports it can hold.
	Bots        byte   // Number of bots on the server.
	ServerType  ServerType
	Environment Environment
	Private     bool
	VAC         bool
	Version     string // Version of the game installed on the server.
	Port        uint16 // The server's game port number.
	SteamID     uint64 // Server's SteamID.
	SourceTV    struct {
		Port uint16 // Spectator port number
		Name string // Name of the spectator server
	}
	Keywords string // Tags that describe the game according to the server
	GameID   uint64 // The server's 64-bit GameID.
}

type Player struct {
	Name     string
	Score    int32
	Duration time.Duration
}

type Rule struct {
	Name  string
	Value string
}

func QueryInfo(address *net.UDPAddr) (Info, time.Duration, error) {
	c, err := net.DialUDP("udp", nil, address)
	if err != nil {
		return Info{}, time.Duration(0), err
	}
	var buf [8 * 1024]byte
	c.SetReadDeadline(time.Now().Add(time.Second * 3))
	before := time.Now()
	const REQ = "\xFF\xFF\xFF\xFFTSource Engine Query\x00"
	c.Write([]byte(REQ))
	n, err := c.Read(buf[:])
	ping := time.Since(before)
	if err != nil {
		return Info{}, time.Duration(0), err
	}
	if n == 9 && buf[4] == 'A' {
		var challenge [4]byte
		copy(challenge[:], buf[5:])
		c.Write(append([]byte(REQ), challenge[:]...))
		c.SetReadDeadline(time.Now().Add(time.Second * 3))
		n, err = c.Read(buf[:])
		if err != nil {
			c.Close()
			return Info{}, time.Duration(0), err
		}
	}
	c.Close()
	if n < 17 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	var r Info
	o := 5 // skip "\xFF\xFF\xFF\xFFI"
	r.Protocol = buf[o]
	o++
	nb := bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Name = string(buf[o : o+nb])
	o += nb + 1
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Map = string(buf[o : o+nb])
	o += nb + 1
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Folder = string(buf[o : o+nb])
	o += nb + 1
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Game = string(buf[o : o+nb])
	o += nb + 1
	r.ID = binary.LittleEndian.Uint16(buf[o:])
	o += 2
	r.Players = buf[o]
	o++
	r.MaxPlayers = buf[o]
	o++
	r.Bots = buf[o]
	o++
	r.ServerType = ServerType(buf[o])
	o++
	if buf[o] == 'o' {
		r.Environment = Environment_Mac
	} else {
		r.Environment = Environment(buf[o])
	}
	o++
	r.Private = buf[o] == 1
	o++
	r.VAC = buf[o] == 1
	o++
	// EXTRA STUFF FOR "The Ship" not handled here
	nb = bytes.IndexByte(buf[o:], 0)
	if nb == -1 {
		return Info{}, time.Duration(0), errors.New("got invalid response")
	}
	r.Version = string(buf[o : o+nb])
	o += nb + 1
	extra := buf[o]
	o++
	if extra&0x80 != 0 {
		r.Port = binary.LittleEndian.Uint16(buf[o:])
		o += 2
	}
	if extra&0x10 != 0 {
		r.SteamID = binary.LittleEndian.Uint64(buf[o:])
		o += 8
	}
	if extra&0x40 != 0 {
		r.SourceTV.Port = binary.LittleEndian.Uint16(buf[o:])
		o += 2
		nb = bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return Info{}, time.Duration(0), errors.New("got invalid response")
		}
		r.SourceTV.Name = string(buf[o : o+nb])
		o += nb + 1
	}
	if extra&0x20 != 0 {
		nb = bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return Info{}, time.Duration(0), errors.New("got invalid response")
		}
		r.Keywords = string(buf[o : o+nb])
		o += nb + 1
	}
	if extra&0x01 != 0 {
		r.GameID = binary.LittleEndian.Uint64(buf[o:])
		o += 8
	}
	return r, ping, nil
}

func QueryInfoString(address string) (Info, time.Duration, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return Info{}, time.Duration(0), err
	}
	return QueryInfo(addr)
}

func QueryPlayers(address *net.UDPAddr) ([]Player, time.Duration, error) {
	c, err := net.DialUDP("udp", nil, address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	c.SetReadDeadline(time.Now().Add(time.Second * 3))
	var buf [2 * 1024]byte
	before := time.Now()
	c.Write([]byte("\xFF\xFF\xFF\xFFU\xFF\xFF\xFF\xFF"))
	n, err := c.Read(buf[:])
	ping := time.Since(before)
	if err != nil {
		c.Close()
		return nil, time.Duration(0), err
	}
	if n == 9 && buf[4] == 'A' {
		buf[4] = 'U'
		c.Write(buf[:n])
		c.SetReadDeadline(time.Now().Add(time.Second * 3))
		n, err = c.Read(buf[:])
		if err != nil {
			c.Close()
			return nil, time.Duration(0), err
		}
	}
	c.Close()
	if n < len("\xFF\xFF\xFF\xFFD0") {
		return nil, time.Duration(0), errors.New("got invalid response")
	}
	o := 5 // skip "\xFF\xFF\xFF\xFFD"
	np := buf[o]
	o++
	players := make([]Player, 0, np)
	for i := byte(0); i < np; i++ {
		var p Player
		o++ // skip index
		nb := bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return nil, time.Duration(0), errors.New("got invalid response")
		}
		p.Name = string(buf[o : o+nb])
		o += nb + 1
		p.Score = int32(binary.LittleEndian.Uint32(buf[o:]))
		o += 4
		dur := math.Float32frombits(binary.LittleEndian.Uint32(buf[o:]))
		p.Duration = time.Duration(dur * 1000 * 1000 * 1000)
		o += 4
		if len(p.Name) != 0 {
			players = append(players, p)
		}
	}
	return players, ping, nil
}

func QueryPlayersString(address string) ([]Player, time.Duration, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	return QueryPlayers(addr)
}

func QueryRules(address *net.UDPAddr) ([]Rule, time.Duration, error) {
	c, err := net.DialUDP("udp", nil, address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	var buf [8 * 1024]byte
	c.SetReadDeadline(time.Now().Add(time.Second * 3))
	before := time.Now()
	c.Write([]byte("\xFF\xFF\xFF\xFFV\xFF\xFF\xFF\xFF"))
	n, err := c.Read(buf[:])
	ping := time.Since(before)
	if err != nil {
		c.Close()
		return nil, time.Duration(0), err
	}
	if n == 9 && buf[4] == 'A' {
		buf[4] = 'V'
		c.Write(buf[:n])
		c.SetReadDeadline(time.Now().Add(time.Second * 3))
		n, err = c.Read(buf[:])
		if err != nil {
			c.Close()
			return nil, time.Duration(0), err
		}
	}
	c.Close()
	if n < len("\xFF\xFF\xFF\xFFE00") {
		return nil, time.Duration(0), errors.New("got invalid response")
	}
	o := 5 // skip "\xFF\xFF\xFF\xFFE"
	nr := binary.LittleEndian.Uint16(buf[o:])
	o += 2
	rules := make([]Rule, 0, nr)
	for i := uint16(0); i < nr; i++ {
		var r Rule
		nb := bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return nil, time.Duration(0), errors.New("got invalid response")
		}
		r.Name = string(buf[o : o+nb])
		o += nb + 1
		nb = bytes.IndexByte(buf[o:], 0)
		if nb == -1 {
			return nil, time.Duration(0), errors.New("got invalid response")
		}
		r.Value = string(buf[o : o+nb])
		o += nb + 1
		if len(r.Name) != 0 {
			rules = append(rules, r)
		}
	}
	return rules, ping, nil
}

func QueryRulesString(address string) ([]Rule, time.Duration, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, time.Duration(0), err
	}
	return QueryRules(addr)
}
<|endoftext|>


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

package maps

import (
	"context"
	"errors"
	"fmt"
	"image"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	_ "image/jpeg" // Loaded for image decoder
	_ "image/png"  // Loaded for image decoder
)

var staticMapAPI = &apiConfig{
	host:            "https://maps.googleapis.com",
	path:            "/maps/api/staticmap",
	acceptsClientID: true,
}

// MapType (optional) defines the type of map to construct. There are several possible
// maptype values, including roadmap, satellite, hybrid, and terrain
type MapType string

// Format defines the format of the resulting image
type Format string

// MarkerSize specifies the size of marker from the set {tiny, mid, small}
type MarkerSize string

// Anchor sets how the icon is placed in relation to the specified markers locations
type Anchor string

const (
	// RoadMap (default) specifies a standard roadmap image, as is normally shown on the
	// Google Maps website. If no maptype value is specified, the Google Static Maps API
	// serves roadmap tiles by default.
	RoadMap MapType = "roadmap"
	//Satellite specifies a satellite image.
	Satellite MapType = "satellite"
	//Terrain specifies a physical relief map image, showing terrain and vegetation.
	Terrain MapType = "terrain"
	// Hybrid specifies a hybrid of the satellite and roadmap image, showing a
	// transparent layer of major streets and place names on the satellite image.
	Hybrid MapType = "hybrid"
	//PNG8 or png (default) specifies the 8-bit PNG format.
	PNG8 Format = "png8"
	// PNG32 specifies the 32-bit PNG format.
	PNG32 Format = "png32"
	// GIF specifies the GIF format.
	GIF Format = "gif"
	// JPG specifies the JPEG compression format.
	JPG Format = "jpg"
	// JPGBaseline specifies a non-progressive JPEG compression format.
	JPGBaseline Format = "jpg-baseline"

	// Tiny Marker size
	Tiny MarkerSize = "tiny"
	// Mid Marker size
	Mid MarkerSize = "mid"
	// Small Marker size
	Small MarkerSize = "small"

	// Top Marker anchor position
	Top Anchor = "top"
	// Bottom Marker anchor position
	Bottom Anchor = "Bottom"
	// Left Marker anchor position
	Left Anchor = "left"
	// Right Marker anchor position
	Right Anchor = "right"
	// Center Marker anchor position
	Center Anchor = "center"
	// Topleft Marker anchor position
	Topleft Anchor = "topleft"
	// Topright Marker anchor position
	Topright Anchor = "topright"
	// Bottomleft Marker anchor position
	Bottomleft Anchor = "bottomleft"
	// Bottomright Marker anchor position
	Bottomright Anchor = "bottomright"
)

// CustomIcon replace the default Map Pin
type CustomIcon struct {
	// IconURL is th icon URL
	IconURL string
	// Anchor sets how the icon is placed in relation to the specified markers locations
	Anchor Anchor
}

func (c CustomIcon) String() string {
	var r []string

	if c.IconURL != "" {
		r = append(r, fmt.Sprintf("icon:%s", c.IconURL))
	}

	if c.Anchor != "" {
		r = append(r, fmt.Sprintf("anchor:%s", c.Anchor))
	}

	return strings.Join(r, "|")
}

// Marker is a Map pin
type Marker struct {
	// Color specifies a 24-bit color (example: color=0xFFFFCC) or a predefined color
	// from the set {black, brown, green, purple, yellow, blue, gray, orange, red,
	// white}.
	Color string
	// Label specifies a single uppercase alphanumeric character from the set {A-Z, 0-9}
	Label string
	// MarkerSize specifies the size of marker from the set {tiny, mid, small}
	Size string
	// CustomIcon replace the default Map Pin
	CustomIcon CustomIcon
	// Location is the Marker position
	Location []LatLng
	// Scale is the Marker scale
	Scale int
}

func (m Marker) String() string {
	var r []string

	if m.CustomIcon != (CustomIcon{}) {
		r = append(r, m.CustomIcon.String())
	} else {
		if m.Color != "" {
			r = append(r, fmt.Sprintf("color:%s", m.Color))
		}

		if m.Label != "" {
			r = append(r, fmt.Sprintf("label:%s", m.Label))
		}

		if m.Size != "" {
			r = append(r, fmt.Sprintf("size:%s", m.Size))
		}

		if m.Scale != 0 {
			r = append(r, fmt.Sprintf("scale:%d", m.Scale))
		}
	}

	for _, l := range m.Location {
		r = append(r, l.String())
	}
	return strings.Join(r, "|")
}

// Path defines a single path of two or more connected points to overlay on the image
// at specified locations
type Path struct {
	// Weight (optional) specifies the thickness of the path in pixels.
	Weight int
	// Color (optional) specifies a color in HEX
	Color string
	// Fillcolor (optional) indicates both that the path marks off a polygonal area and
	// specifies the fill color to use as an overlay within that area.
	FillColor string
	// Geodesic (optional) indicates that the requested path should be interpreted as a
	// geodesic line that follows the curvature of the earth.
	Geodesic bool
	// Location two or more connected points to overlay on the image at specified
	// locations
	Location []LatLng
}

func (p Path) String() string {
	var r []string

	if p.Color != "" {
		r = append(r, fmt.Sprintf("color:%s", p.Color))
	}

	if p.FillColor != "" {
		r = append(r, fmt.Sprintf("fillcolor:%s", p.FillColor))
	}

	if p.Weight != 0 {
		r = append(r, fmt.Sprintf("weight:%d", p.Weight))
	}

	if p.Geodesic {
		r = append(r, "geodesic:true")
	}

	for _, l := range p.Location {
		r = append(r, l.String())
	}
	return strings.Join(r, "|")
}

// StaticMapRequest is the functional options struct for staticMap.Get
type StaticMapRequest struct {
	// Center focus the map at the correct location
	Center string
	// Zoom (required if markers not present) defines the zoom level of the map
	Zoom int
	// Size (required) defines the rectangular dimensions of the map image. This
	// parameter takes a string of the form {horizontal_value}x{vertical_value}
	Size string
	// Scale (optional) affects the number of pixels that are returned. Accepted values
	// are 2 and 4
	Scale int
	// Format format (optional) defines the format of the resulting image. Default: PNG.
	// Accepted Values: There are several possible formats including GIF, JPEG and PNG
	// types.
	Format Format
	// Language (optional) defines the language to use for display of labels on map
	// tiles
	Language string
	// Region (optional) defines the appropriate borders to display, based on
	// geo-political sensitivities.
	Region string
	// MapType (optional) defines the type of map to construct.
	MapType MapType
	// Markers (optional) define one or more markers to attach to the image at specified
	// locations.
	Markers []Marker
	// Paths (optional) defines multiple paths of two or more connected points to
	// overlay on the image at specified locations
	Paths []Path
	// Visible specifies one or more locations that should remain visible on the map,
	// though no markers or other indicators will be displayed.
	Visible []LatLng
	// MapStyles (optional) contains map styles.
	MapStyles []string
}

func (r *StaticMapRequest) params() url.Values {
	q := make(url.Values)

	if r.Center != "" {
		q.Set("center", r.Center)
	}

	if r.Zoom > 0 {
		q.Set("zoom", strconv.Itoa(r.Zoom))
	}
	if r.Size != "" {
		q.Set("size", r.Size)
	}
	if r.Scale > 0 {
		q.Set("scale", strconv.Itoa(r.Scale))
	}
	if r.Format != "" {
		q.Set("format", string(r.Format))
	}

	if r.Language != "" {
		q.Set("language", r.Language)
	}

	if r.Region != "" {
		q.Set("region", r.Region)
	}
	if r.MapType != "" {
		q.Set("maptype", string(r.MapType))
	}

	for _, m := range r.Markers {
		q.Add("markers", m.String())
	}

	for _, ps := range r.Paths {
		q.Add("path", ps.String())
	}

	if len(r.Visible) > 0 {
		t := make([]string, len(r.Visible))
		for i, l := range r.Visible {
			t[i] = l.String()
		}
		q.Set("visible", strings.Join(t, "|"))
	}

	for _, style := range r.MapStyles {
		q.Add("style", style)
	}

	return q
}

// StaticMap makes a StaticMap API request.
func (c *Client) StaticMap(ctx context.Context, r *StaticMapRequest) (image.Image, error) {
	if len(r.Markers) == 0 && r.Center == "" && r.Zoom == 0 {
		return nil, errors.New("maps: Center & Zoom required if Markers empty")
	}
	if r.Size == "" {
		return nil, errors.New("maps: Size empty")
	}

	resp, err := c.getBinary(ctx, staticMapAPI, r)
	if err != nil {
		return nil, err
	}
	defer resp.data.Close()

	if resp.statusCode != http.StatusOK {
		b, err := ioutil.ReadAll(resp.data)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("Maps Static API: %d - %s", resp.statusCode, b)
	}

	img, _, err := image.Decode(resp.data)
	return img, err
}
<commit_msg>Add missing Marker scale parameter for static maps (custom icon)<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package maps

import (
	"context"
	"errors"
	"fmt"
	"image"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	_ "image/jpeg" // Loaded for image decoder
	_ "image/png"  // Loaded for image decoder
)

var staticMapAPI = &apiConfig{
	host:            "https://maps.googleapis.com",
	path:            "/maps/api/staticmap",
	acceptsClientID: true,
}

// MapType (optional) defines the type of map to construct. There are several possible
// maptype values, including roadmap, satellite, hybrid, and terrain
type MapType string

// Format defines the format of the resulting image
type Format string

// MarkerSize specifies the size of marker from the set {tiny, mid, small}
type MarkerSize string

// Anchor sets how the icon is placed in relation to the specified markers locations
type Anchor string

const (
	// RoadMap (default) specifies a standard roadmap image, as is normally shown on the
	// Google Maps website. If no maptype value is specified, the Google Static Maps API
	// serves roadmap tiles by default.
	RoadMap MapType = "roadmap"
	//Satellite specifies a satellite image.
	Satellite MapType = "satellite"
	//Terrain specifies a physical relief map image, showing terrain and vegetation.
	Terrain MapType = "terrain"
	// Hybrid specifies a hybrid of the satellite and roadmap image, showing a
	// transparent layer of major streets and place names on the satellite image.
	Hybrid MapType = "hybrid"
	//PNG8 or png (default) specifies the 8-bit PNG format.
	PNG8 Format = "png8"
	// PNG32 specifies the 32-bit PNG format.
	PNG32 Format = "png32"
	// GIF specifies the GIF format.
	GIF Format = "gif"
	// JPG specifies the JPEG compression format.
	JPG Format = "jpg"
	// JPGBaseline specifies a non-progressive JPEG compression format.
	JPGBaseline Format = "jpg-baseline"

	// Tiny Marker size
	Tiny MarkerSize = "tiny"
	// Mid Marker size
	Mid MarkerSize = "mid"
	// Small Marker size
	Small MarkerSize = "small"

	// Top Marker anchor position
	Top Anchor = "top"
	// Bottom Marker anchor position
	Bottom Anchor = "Bottom"
	// Left Marker anchor position
	Left Anchor = "left"
	// Right Marker anchor position
	Right Anchor = "right"
	// Center Marker anchor position
	Center Anchor = "center"
	// Topleft Marker anchor position
	Topleft Anchor = "topleft"
	// Topright Marker anchor position
	Topright Anchor = "topright"
	// Bottomleft Marker anchor position
	Bottomleft Anchor = "bottomleft"
	// Bottomright Marker anchor position
	Bottomright Anchor = "bottomright"
)

// CustomIcon replace the default Map Pin
type CustomIcon struct {
	// IconURL is th icon URL
	IconURL string
	// Anchor sets how the icon is placed in relation to the specified markers locations
	Anchor Anchor
	// Scale is the custom icon scale
	Scale int
}

func (c CustomIcon) String() string {
	var r []string

	if c.IconURL != "" {
		r = append(r, fmt.Sprintf("icon:%s", c.IconURL))
	}

	if c.Anchor != "" {
		r = append(r, fmt.Sprintf("anchor:%s", c.Anchor))
	}

	if c.Scale != 0 {
		r = append(r, fmt.Sprintf("scale:%d", c.Scale))
	}

	return strings.Join(r, "|")
}

// Marker is a Map pin
type Marker struct {
	// Color specifies a 24-bit color (example: color=0xFFFFCC) or a predefined color
	// from the set {black, brown, green, purple, yellow, blue, gray, orange, red,
	// white}.
	Color string
	// Label specifies a single uppercase alphanumeric character from the set {A-Z, 0-9}
	Label string
	// MarkerSize specifies the size of marker from the set {tiny, mid, small}
	Size string
	// CustomIcon replace the default Map Pin
	CustomIcon CustomIcon
	// Location is the Marker position
	Location []LatLng
}

func (m Marker) String() string {
	var r []string

	if m.CustomIcon != (CustomIcon{}) {
		r = append(r, m.CustomIcon.String())
	} else {
		if m.Color != "" {
			r = append(r, fmt.Sprintf("color:%s", m.Color))
		}

		if m.Label != "" {
			r = append(r, fmt.Sprintf("label:%s", m.Label))
		}

		if m.Size != "" {
			r = append(r, fmt.Sprintf("size:%s", m.Size))
		}
	}

	for _, l := range m.Location {
		r = append(r, l.String())
	}
	return strings.Join(r, "|")
}

// Path defines a single path of two or more connected points to overlay on the image
// at specified locations
type Path struct {
	// Weight (optional) specifies the thickness of the path in pixels.
	Weight int
	// Color (optional) specifies a color in HEX
	Color string
	// Fillcolor (optional) indicates both that the path marks off a polygonal area and
	// specifies the fill color to use as an overlay within that area.
	FillColor string
	// Geodesic (optional) indicates that the requested path should be interpreted as a
	// geodesic line that follows the curvature of the earth.
	Geodesic bool
	// Location two or more connected points to overlay on the image at specified
	// locations
	Location []LatLng
}

func (p Path) String() string {
	var r []string

	if p.Color != "" {
		r = append(r, fmt.Sprintf("color:%s", p.Color))
	}

	if p.FillColor != "" {
		r = append(r, fmt.Sprintf("fillcolor:%s", p.FillColor))
	}

	if p.Weight != 0 {
		r = append(r, fmt.Sprintf("weight:%d", p.Weight))
	}

	if p.Geodesic {
		r = append(r, "geodesic:true")
	}

	for _, l := range p.Location {
		r = append(r, l.String())
	}
	return strings.Join(r, "|")
}

// StaticMapRequest is the functional options struct for staticMap.Get
type StaticMapRequest struct {
	// Center focus the map at the correct location
	Center string
	// Zoom (required if markers not present) defines the zoom level of the map
	Zoom int
	// Size (required) defines the rectangular dimensions of the map image. This
	// parameter takes a string of the form {horizontal_value}x{vertical_value}
	Size string
	// Scale (optional) affects the number of pixels that are returned. Accepted values
	// are 2 and 4
	Scale int
	// Format format (optional) defines the format of the resulting image. Default: PNG.
	// Accepted Values: There are several possible formats including GIF, JPEG and PNG
	// types.
	Format Format
	// Language (optional) defines the language to use for display of labels on map
	// tiles
	Language string
	// Region (optional) defines the appropriate borders to display, based on
	// geo-political sensitivities.
	Region string
	// MapType (optional) defines the type of map to construct.
	MapType MapType
	// Markers (optional) define one or more markers to attach to the image at specified
	// locations.
	Markers []Marker
	// Paths (optional) defines multiple paths of two or more connected points to
	// overlay on the image at specified locations
	Paths []Path
	// Visible specifies one or more locations that should remain visible on the map,
	// though no markers or other indicators will be displayed.
	Visible []LatLng
	// MapStyles (optional) contains map styles.
	MapStyles []string
}

func (r *StaticMapRequest) params() url.Values {
	q := make(url.Values)

	if r.Center != "" {
		q.Set("center", r.Center)
	}

	if r.Zoom > 0 {
		q.Set("zoom", strconv.Itoa(r.Zoom))
	}
	if r.Size != "" {
		q.Set("size", r.Size)
	}
	if r.Scale > 0 {
		q.Set("scale", strconv.Itoa(r.Scale))
	}
	if r.Format != "" {
		q.Set("format", string(r.Format))
	}

	if r.Language != "" {
		q.Set("language", r.Language)
	}

	if r.Region != "" {
		q.Set("region", r.Region)
	}
	if r.MapType != "" {
		q.Set("maptype", string(r.MapType))
	}

	for _, m := range r.Markers {
		q.Add("markers", m.String())
	}

	for _, ps := range r.Paths {
		q.Add("path", ps.String())
	}

	if len(r.Visible) > 0 {
		t := make([]string, len(r.Visible))
		for i, l := range r.Visible {
			t[i] = l.String()
		}
		q.Set("visible", strings.Join(t, "|"))
	}

	for _, style := range r.MapStyles {
		q.Add("style", style)
	}

	return q
}

// StaticMap makes a StaticMap API request.
func (c *Client) StaticMap(ctx context.Context, r *StaticMapRequest) (image.Image, error) {
	if len(r.Markers) == 0 && r.Center == "" && r.Zoom == 0 {
		return nil, errors.New("maps: Center & Zoom required if Markers empty")
	}
	if r.Size == "" {
		return nil, errors.New("maps: Size empty")
	}

	resp, err := c.getBinary(ctx, staticMapAPI, r)
	if err != nil {
		return nil, err
	}
	defer resp.data.Close()

	if resp.statusCode != http.StatusOK {
		b, err := ioutil.ReadAll(resp.data)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("Maps Static API: %d - %s", resp.statusCode, b)
	}

	img, _, err := image.Decode(resp.data)
	return img, err
}
<|endoftext|>


<commit_before>package jira

import (
	"fmt"
)

// RoleService handles roles for the JIRA instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Role
type RoleService struct {
	client *Client
}

// Role represents a JIRA product role
type Role struct {
	Self        string   `json:"self" structs:"self"`
	Name        string   `json:"name" structs:"name"`
	ID          int      `json:"id" structs:"id"`
	Description string   `json:"description" structs:"description"`
	Actors      []*Actor `json:"actors" structs:"actors"`
}

// Actor represents a JIRA actor
type Actor struct {
	ID          int        `json:"id" structs:"id"`
	DisplayName string     `json:"displayName" structs:"displayName"`
	Type        string     `json:"type" structs:"type"`
	Name        string     `json:"name" structs:"name"`
	AvatarURL   string     `json:"avatarUrl" structs:"avatarUrl"`
	ActorUser   *ActorUser `json:"actorUser" structs:"actoruser"`
}

// ActorUser contains the account id of the actor/user
type ActorUser struct {
	AccountID string `json:"accountId" structs:"accountId"`
}

// GetList returns a list of all available project roles
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-get
func (s *RoleService) GetList() (*[]Role, *Response, error) {
	apiEndpoint := "rest/api/3/role"
	req, err := s.client.NewRequest("GET", apiEndpoint, nil)
	if err != nil {
		return nil, nil, err
	}
	roles := new([]Role)
	resp, err := s.client.Do(req, roles)
	if err != nil {
		jerr := NewJiraError(resp, err)
		return nil, resp, jerr
	}
	return roles, resp, err
}

// Get retreives a single Role from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-id-get
func (s *RoleService) Get(roleID int) (*Role, *Response, error) {
	apiEndpoint := fmt.Sprintf("rest/api/3/role/%d", roleID)
	req, err := s.client.NewRequest("GET", apiEndpoint, nil)
	if err != nil {
		return nil, nil, err
	}
	role := new(Role)
	resp, err := s.client.Do(req, role)
	if err != nil {
		jerr := NewJiraError(resp, err)
		return nil, resp, jerr
	}

	return role, resp, err
}
<commit_msg>Add missing check to roleService.Get<commit_after>package jira

import (
	"fmt"
)

// RoleService handles roles for the JIRA instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Role
type RoleService struct {
	client *Client
}

// Role represents a JIRA product role
type Role struct {
	Self        string   `json:"self" structs:"self"`
	Name        string   `json:"name" structs:"name"`
	ID          int      `json:"id" structs:"id"`
	Description string   `json:"description" structs:"description"`
	Actors      []*Actor `json:"actors" structs:"actors"`
}

// Actor represents a JIRA actor
type Actor struct {
	ID          int        `json:"id" structs:"id"`
	DisplayName string     `json:"displayName" structs:"displayName"`
	Type        string     `json:"type" structs:"type"`
	Name        string     `json:"name" structs:"name"`
	AvatarURL   string     `json:"avatarUrl" structs:"avatarUrl"`
	ActorUser   *ActorUser `json:"actorUser" structs:"actoruser"`
}

// ActorUser contains the account id of the actor/user
type ActorUser struct {
	AccountID string `json:"accountId" structs:"accountId"`
}

// GetList returns a list of all available project roles
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-get
func (s *RoleService) GetList() (*[]Role, *Response, error) {
	apiEndpoint := "rest/api/3/role"
	req, err := s.client.NewRequest("GET", apiEndpoint, nil)
	if err != nil {
		return nil, nil, err
	}
	roles := new([]Role)
	resp, err := s.client.Do(req, roles)
	if err != nil {
		jerr := NewJiraError(resp, err)
		return nil, resp, jerr
	}
	return roles, resp, err
}

// Get retreives a single Role from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-id-get
func (s *RoleService) Get(roleID int) (*Role, *Response, error) {
	apiEndpoint := fmt.Sprintf("rest/api/3/role/%d", roleID)
	req, err := s.client.NewRequest("GET", apiEndpoint, nil)
	if err != nil {
		return nil, nil, err
	}
	role := new(Role)
	resp, err := s.client.Do(req, role)
	if err != nil {
		jerr := NewJiraError(resp, err)
		return nil, resp, jerr
	}
	if role.Self == "" {
		return nil, resp, fmt.Errorf("No role with ID %d found", roleID)
	}

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


<commit_before>package server

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"

	hh "dmitryfrank.com/geekmarks/server/httphelper"
	"dmitryfrank.com/geekmarks/server/storage"

	"github.com/juju/errors"
)

type userTagsGetResp struct {
	Tags []userTagData `json:"tags"`
}

type userTagData struct {
	ID          int           `json:"id"`
	Description string        `json:"description"`
	Names       []string      `json:"names"`
	Subtags     []userTagData `json:"subtags,omitempty"`
}

type userTagsParent struct {
	ParentPath *string `json:"parentPath,omitempty"`
	ParentID   *int    `json:"parentID,omitempty"`
}

type userTagsPostArgs struct {
	userTagsParent `json:",omitempty"`
	Names          []string `json:"names"`
	Description    string   `json:"description"`
}

type userTagsPostResp struct {
	TagID int `json:"tagID"`
}

func (up *userTagsParent) getParentID(
	r *http.Request, tx *sql.Tx, userID int, gm *GMServer,
) (int, error) {
	var err error
	parentTagID := 0
	// If parent tag ID is provided, use it; otherwise, get the root tag id for
	// the user
	if up.ParentPath != nil {
		parentTagID, err = gm.si.GetTagIDByPath(tx, userID, *up.ParentPath)
		if err != nil {
			return 0, errors.Trace(err)
		}
	} else if up.ParentID != nil {
		parentTagData, err := gm.si.GetTag(tx, *up.ParentID, &storage.GetTagOpts{})
		if err != nil {
			return 0, errors.Trace(err)
		}
		ok, err := gm.authorizeOperation(r, &authzArgs{OwnerID: parentTagData.OwnerID})
		if err != nil {
			return 0, errors.Trace(err)
		}
		if !ok {
			return 0, hh.MakeForbiddenError()
		}
		parentTagID = *up.ParentID
	} else {
		parentTagID, err = gm.si.GetRootTagID(tx, userID)
		if err != nil {
			return 0, errors.Trace(err)
		}
	}

	return parentTagID, nil
}

func (gm *GMServer) userTagsGet(
	r *http.Request, gu getUser,
) (resp interface{}, err error) {
	ud, err := gm.getUserAndAuthorize(r, gu, &authzArgs{})
	if err != nil {
		return nil, errors.Trace(err)
	}

	up := userTagsParent{}
	if parentIdStr := r.FormValue("parent_id"); parentIdStr != "" {
		parentID, err := strconv.Atoi(parentIdStr)
		if err != nil {
			return nil, errors.Trace(err)
		}
		up.ParentID = &parentID
	}
	if parentPathStr := r.FormValue("parent_id"); parentPathStr != "" {
		up.ParentPath = &parentPathStr
	}

	var tagsData []storage.TagData
	err = gm.si.Tx(func(tx *sql.Tx) error {
		parentTagID, err := up.getParentID(r, tx, ud.ID, gm)
		if err != nil {
			return errors.Trace(err)
		}

		tagsData, err = gm.si.GetTags(tx, parentTagID, &storage.GetTagOpts{
			GetNames:   true,
			GetSubtags: true,
		})
		if err != nil {
			return errors.Trace(err)
		}

		return nil
	})
	if err != nil {
		return nil, errors.Trace(err)
	}

	resp = userTagsGetResp{
		Tags: gm.createUserTagsData(tagsData),
	}

	return resp, nil
}

func (gm *GMServer) createUserTagsData(in []storage.TagData) []userTagData {
	if in == nil {
		return nil
	}

	res := []userTagData{}

	for _, td := range in {
		resTD := userTagData{
			ID:          td.ID,
			Description: td.Description,
			Names:       td.Names,
			Subtags:     gm.createUserTagsData(td.Subtags),
		}
		res = append(res, resTD)
	}

	return res
}

func (gm *GMServer) userTagsPost(r *http.Request, gu getUser) (resp interface{}, err error) {
	ud, err := gm.getUserAndAuthorize(r, gu, &authzArgs{})
	if err != nil {
		return nil, errors.Trace(err)
	}

	decoder := json.NewDecoder(r.Body)
	var args userTagsPostArgs
	err = decoder.Decode(&args)
	if err != nil {
		// TODO: provide request data example
		return nil, errors.Errorf("invalid data")
	}

	tagID := 0

	err = gm.si.Tx(func(tx *sql.Tx) error {
		parentTagID, err := args.getParentID(r, tx, ud.ID, gm)
		if err != nil {
			return errors.Trace(err)
		}

		tagID, err = gm.si.CreateTag(tx, &storage.TagData{
			OwnerID:     ud.ID,
			ParentTagID: parentTagID,
			Names:       args.Names,
			Description: args.Description,
		})
		if err != nil {
			return errors.Trace(err)
		}

		return nil
	})
	if err != nil {
		return nil, errors.Trace(err)
	}

	resp = userTagsPostResp{
		TagID: tagID,
	}

	return resp, nil
}
<commit_msg>Fix getting tags from a non-root tag<commit_after>package server

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"

	hh "dmitryfrank.com/geekmarks/server/httphelper"
	"dmitryfrank.com/geekmarks/server/storage"

	"github.com/juju/errors"
)

type userTagsGetResp struct {
	Tags []userTagData `json:"tags"`
}

type userTagData struct {
	ID          int           `json:"id"`
	Description string        `json:"description"`
	Names       []string      `json:"names"`
	Subtags     []userTagData `json:"subtags,omitempty"`
}

type userTagsParent struct {
	ParentPath *string `json:"parentPath,omitempty"`
	ParentID   *int    `json:"parentID,omitempty"`
}

type userTagsPostArgs struct {
	userTagsParent `json:",omitempty"`
	Names          []string `json:"names"`
	Description    string   `json:"description"`
}

type userTagsPostResp struct {
	TagID int `json:"tagID"`
}

func (up *userTagsParent) getParentID(
	r *http.Request, tx *sql.Tx, userID int, gm *GMServer,
) (int, error) {
	var err error
	parentTagID := 0
	// If parent tag ID is provided, use it; otherwise, get the root tag id for
	// the user
	if up.ParentPath != nil {
		parentTagID, err = gm.si.GetTagIDByPath(tx, userID, *up.ParentPath)
		if err != nil {
			return 0, errors.Trace(err)
		}
	} else if up.ParentID != nil {
		parentTagData, err := gm.si.GetTag(tx, *up.ParentID, &storage.GetTagOpts{})
		if err != nil {
			return 0, errors.Trace(err)
		}
		ok, err := gm.authorizeOperation(r, &authzArgs{OwnerID: parentTagData.OwnerID})
		if err != nil {
			return 0, errors.Trace(err)
		}
		if !ok {
			return 0, hh.MakeForbiddenError()
		}
		parentTagID = *up.ParentID
	} else {
		parentTagID, err = gm.si.GetRootTagID(tx, userID)
		if err != nil {
			return 0, errors.Trace(err)
		}
	}

	return parentTagID, nil
}

func (gm *GMServer) userTagsGet(
	r *http.Request, gu getUser,
) (resp interface{}, err error) {
	ud, err := gm.getUserAndAuthorize(r, gu, &authzArgs{})
	if err != nil {
		return nil, errors.Trace(err)
	}

	up := userTagsParent{}
	if parentIdStr := r.FormValue("parent_id"); parentIdStr != "" {
		parentID, err := strconv.Atoi(parentIdStr)
		if err != nil {
			return nil, errors.Trace(err)
		}
		up.ParentID = &parentID
	}
	if parentPathStr := r.FormValue("parent_path"); parentPathStr != "" {
		up.ParentPath = &parentPathStr
	}

	var tagsData []storage.TagData
	err = gm.si.Tx(func(tx *sql.Tx) error {
		parentTagID, err := up.getParentID(r, tx, ud.ID, gm)
		if err != nil {
			return errors.Trace(err)
		}

		tagsData, err = gm.si.GetTags(tx, parentTagID, &storage.GetTagOpts{
			GetNames:   true,
			GetSubtags: true,
		})
		if err != nil {
			return errors.Trace(err)
		}

		return nil
	})
	if err != nil {
		return nil, errors.Trace(err)
	}

	resp = userTagsGetResp{
		Tags: gm.createUserTagsData(tagsData),
	}

	return resp, nil
}

func (gm *GMServer) createUserTagsData(in []storage.TagData) []userTagData {
	if in == nil {
		return nil
	}

	res := []userTagData{}

	for _, td := range in {
		resTD := userTagData{
			ID:          td.ID,
			Description: td.Description,
			Names:       td.Names,
			Subtags:     gm.createUserTagsData(td.Subtags),
		}
		res = append(res, resTD)
	}

	return res
}

func (gm *GMServer) userTagsPost(r *http.Request, gu getUser) (resp interface{}, err error) {
	ud, err := gm.getUserAndAuthorize(r, gu, &authzArgs{})
	if err != nil {
		return nil, errors.Trace(err)
	}

	decoder := json.NewDecoder(r.Body)
	var args userTagsPostArgs
	err = decoder.Decode(&args)
	if err != nil {
		// TODO: provide request data example
		return nil, errors.Errorf("invalid data")
	}

	tagID := 0

	err = gm.si.Tx(func(tx *sql.Tx) error {
		parentTagID, err := args.getParentID(r, tx, ud.ID, gm)
		if err != nil {
			return errors.Trace(err)
		}

		tagID, err = gm.si.CreateTag(tx, &storage.TagData{
			OwnerID:     ud.ID,
			ParentTagID: parentTagID,
			Names:       args.Names,
			Description: args.Description,
		})
		if err != nil {
			return errors.Trace(err)
		}

		return nil
	})
	if err != nil {
		return nil, errors.Trace(err)
	}

	resp = userTagsPostResp{
		TagID: tagID,
	}

	return resp, nil
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/fatih/color"
)

var warn = color.New(color.FgYellow).Add(color.Bold).Println

func procTestResponse(res *http.Response, t *testing.T) {
	if res.StatusCode == 204 {
		return
	}
	if res.StatusCode == 404 {
		warn("WARNING! " + res.Request.Method + ": " + res.Request.URL.Path + " returned 404")
		return
	}
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		t.Error(err)
	}
	var marsh interface{}
	err = json.Unmarshal(body, &marsh)
	if err != nil {
		t.Error(errors.New(res.Request.Method + ": failed to unmarshal response from " + res.Request.URL.Path + ". Status code was: " + strconv.Itoa(res.StatusCode)))
	}
	_, err = json.MarshalIndent(marsh, "", "  ")
	if err != nil {
		t.Error("failed to Marshal")
	}
	// fmt.Println(string(b) + "\n")
}

func TestEndpoints(t *testing.T) {

	s := NewServer()
	r := s.NewRouter()
	server := httptest.NewServer(r)
	defer server.Close()

	// a couple of closures to save time below
	get := func(endpoint string) {
		res, err := http.Get(server.URL + endpoint)
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t)
	}

	post := func(endpoint, msg string) {
		res, err := http.Post(server.URL+endpoint, "application/json", bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t)
	}

	put := func(endpoint, msg string) {
		req, err := http.NewRequest("PUT", server.URL+endpoint, bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t)
	}

	del := func(endpoint string) {
		req, err := http.NewRequest("DELETE", server.URL+endpoint, nil)
		if err != nil {
			t.Error(err)
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t)
	}

	// set up a group (1)
	post("/groups", `{"group":0}`)

	// set up a + block (2)
	post("/blocks", `{"type":"+","group":1}`)

	// label group 1
	put("/groups/1/label", "The Best Group Ever")

	// label the plus block
	put("/blocks/2/label", "my bestest adder")

	// get all the groups
	get("/groups")

	// get group 1
	get("/groups/1")

	// get all the blocks
	get("/blocks")

	// get the + block
	get("/blocks/2")

	// make a delay block (3)
	post("/blocks", `{"type":"delay", "group":1}`)

	// set the delay's value
	put("/blocks/3/routes/1", `{"type":"const","value":"1s"}`)

	// make a log block (4)
	post("/blocks", `{"type":"log", "group":1}`)

	// connect the + block to the delay block (5)
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":3, "Route":0}}`)

	// connect the delay block to the log block (6)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":4, "Route":0}}`)

	// set the value of the plus inputs
	put("/blocks/2/routes/0", `{"type":"const","value":1}`)
	put("/blocks/2/routes/1", `{"type":"const","value":1}`)

	// make a set block (7)
	post("/blocks", `{"type":"set", "group":1}`)

	// disconnect the log block from the delay block
	del("/connections/6")

	// connect the set block to the log block and delay block (8) (9)
	post("/connections", `{"source":{"id":7, "Route":0}, "target":{"id":4, "Route":0}}`)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":7, "Route":1}}`)

	// list connections
	get("/connections")
	// describe connection 8
	get("/connections/8")

	// set the value of the set key
	put("/blocks/7/routes/0", `{"type":"const","value":"myResult"}`)

	// set the path on the log block
	put("/blocks/4/routes/0", `{"type":"fetch","value":".myResult"}`)

	// move log block to root group
	put("/groups/0/children/4", "")
	// move + block to root group (we will generate some errors with this later)
	put("/groups/0/children/2", "")

	// create a keyvalue source (10)
	post("/sources", `{"type":"KeyValue"}`)

	// get all the sources
	get("/sources")

	// get the keyvalue source
	get("/sources/10")

	// make a stream source (11)
	post("/sources", `{"type":"Stream"}`)

	// change a parameter in the stream
	put("/sources/11", `{"topic":"test"}`)

	// delete the keyvalue store
	del("/sources/10")

	// export the pattern
	get("/groups/0/export")

	// import a pattern
	pattern := `{"blocks":[{"label":"","type":"+","id":2,"inputs":[{"name":"addend","type":"fetch","value":"."},{"name":"addend","type":"fetch","value":"."}],"outputs":[{"name":"sum"}],"position":{"x":0,"y":0}},{"label":"","type":"delay","id":3,"inputs":[{"name":"passthrough","type":"fetch","value":"."},{"name":"duration","type":"const","value":"1s"}],"outputs":[{"name":"passthrough"}],"position":{"x":0,"y":0}}],"connections":[{"source":{"id":2,"route":0},"target":{"id":3,"route":0},"id":4}],"groups":[{"id":1,"label":"","children":[2,3],"position":{"x":0,"y":0}}]}`
	post("/groups/1/import", pattern)

	// delete the log block
	del("/blocks/4")

	// delete group 1
	del("/groups/1")

	// get the library
	get("/library")

	// generate some errors
	del("/groups/1")                                                                       // delete a group we've already deleted
	del("/groups/")                                                                        // delete unspecified group
	del("/blocks/246")                                                                     // delete an unknown block
	post("/groups/1/import", "{}")                                                         // import empty
	post("/groups/1/import", "{bla}")                                                      // import malformed
	get("/groups/6/export")                                                                // export an unknown group
	post("/sources", `{"type":"GodHead"}`)                                                 // create an unknown source
	put("/groups/8/children/4", "")                                                        // modify an unknown group
	put("/groups/0/children/34", "")                                                       // move an unknown block to group 0
	put("/blocks/2/routes/20", `{"type":"fetch","value":".myResult"}`)                     // set an unknown route
	put("/blocks/240/routes/0", `{"type":"fetch","value":".myResult"}`)                    // set an unknown block's route
	put("/blocks/2/routes/0", `{"type":"fetch","value":"invalid"}`)                        // set the + block's route to an invalid path
	put("/blocks/2/routes/0", `{"type":"value","value":"bob"}`)                            // set the + block's route to an invalid value
	put("/blocks/2/routes/0", `{bobo}`)                                                    // set the + block's route using malformed json
	post("/groups", `{"group":10}`)                                                        // create a group with an unknown parent
	post("/groups", `{"group"10}`)                                                         // create a group with malformed JSON
	post("/blocks", `{"type":"invalid", "group":0}`)                                       // create a block of invalid type
	post("/blocks", `{"type"lid", "group":1}`)                                             // create a block with malformed json
	post("/blocks", `{"type":"latch", "group":10}`)                                        // create a block witha group that doesn't exist
	post("/connections", `{"source":{"id":700, "Route":0}, "target":{"id":2, "Route":0}}`) //connect unknown source
	//TODO this one panics
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":200, "Route":0}}`) //connect unknown target
	post("/connections", `{"source":{"i:0}, "ta200, "Route":0}}`)                          //connect with malformed json
	post("/connections", `{}`)                                                             //connect with empty json
	post("/connections", "")                                                               //connect with empty string
	del("/connections/289")                                                                //delete unknown connection
	del("/connections/")                                                                   //delete unspecified connection
	del("/connections/invalid")                                                            //delete malformed connection
}
<commit_msg>added an exoected code for each request. Now the test fails immediately if gets the wrong code<commit_after>package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/fatih/color"
)

var warn = color.New(color.FgYellow).Add(color.Bold).Println

func procTestResponse(res *http.Response, t *testing.T, expectedCode int) {
	if res.StatusCode != expectedCode {
		t.Error(res.Request.Method + ": " + res.Request.URL.Path + " returned " + strconv.Itoa(res.StatusCode) + ". Expected " + strconv.Itoa(expectedCode) + ".")
		return
	}
	if res.StatusCode == 204 {
		return
	}
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		t.Error(err)
	}
	var marsh interface{}
	err = json.Unmarshal(body, &marsh)
	if err != nil {
		t.Error(errors.New(res.Request.Method + ": failed to unmarshal response from " + res.Request.URL.Path + ". Status code was: " + strconv.Itoa(res.StatusCode)))
	}
	_, err = json.MarshalIndent(marsh, "", "  ")
	if err != nil {
		t.Error("failed to Marshal")
	}
	// fmt.Println(string(b) + "\n")
}

func TestEndpoints(t *testing.T) {

	s := NewServer()
	r := s.NewRouter()
	server := httptest.NewServer(r)
	defer server.Close()

	// a couple of closures to save time below
	get := func(endpoint string, expectedCode int) {
		res, err := http.Get(server.URL + endpoint)
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t, expectedCode)
	}

	post := func(endpoint, msg string, expectedCode int) {
		res, err := http.Post(server.URL+endpoint, "application/json", bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t, expectedCode)
	}

	put := func(endpoint, msg string, expectedCode int) {
		req, err := http.NewRequest("PUT", server.URL+endpoint, bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t, expectedCode)
	}

	del := func(endpoint string, expectedCode int) {
		req, err := http.NewRequest("DELETE", server.URL+endpoint, nil)
		if err != nil {
			t.Error(err)
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
		}
		procTestResponse(res, t, expectedCode)
	}

	// set up a group (1)
	post("/groups", `{"group":0}`, 200)

	// set up a + block (2)
	post("/blocks", `{"type":"+","group":1}`, 200)

	// label group 1
	put("/groups/1/label", "The Best Group Ever", 200)

	// label the plus block
	put("/blocks/2/label", "my bestest adder", 204)

	// get all the groups
	get("/groups", 200)

	// get group 1
	get("/groups/1", 200)

	// get all the blocks
	get("/blocks", 200)

	// get the + block
	get("/blocks/2", 200)

	// make a delay block (3)
	post("/blocks", `{"type":"delay", "group":1}`, 200)

	// set the delay's value
	put("/blocks/3/routes/1", `{"type":"const","value":"1s"}`, 204)

	// make a log block (4)
	post("/blocks", `{"type":"log", "group":1}`, 200)

	// connect the + block to the delay block (5)
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":3, "Route":0}}`, 200)

	// connect the delay block to the log block (6)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":4, "Route":0}}`, 200)

	// set the value of the plus inputs
	put("/blocks/2/routes/0", `{"type":"const","value":1}`, 204)
	put("/blocks/2/routes/1", `{"type":"const","value":1}`, 204)

	// make a set block (7)
	post("/blocks", `{"type":"set", "group":1}`, 200)

	// disconnect the log block from the delay block
	del("/connections/6", 204)

	// connect the set block to the log block and delay block (8) (9)
	post("/connections", `{"source":{"id":7, "Route":0}, "target":{"id":4, "Route":0}}`, 200)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":7, "Route":1}}`, 200)

	// list connections
	get("/connections", 200)
	// describe connection 8
	get("/connections/8", 200)

	// set the value of the set key
	put("/blocks/7/routes/0", `{"type":"const","value":"myResult"}`, 204)

	// set the path on the log block
	put("/blocks/4/routes/0", `{"type":"fetch","value":".myResult"}`, 204)

	// move log block to root group
	put("/groups/0/children/4", "", 204)
	// move + block to root group (we will generate some errors with this later)
	put("/groups/0/children/2", "", 204)

	// create a keyvalue source (10)
	post("/sources", `{"type":"KeyValue"}`, 200)

	// get all the sources
	get("/sources", 200)

	// get the keyvalue source
	get("/sources/10", 200)

	// make a stream source (11)
	post("/sources", `{"type":"Stream"}`, 200)

	// change a parameter in the stream
	put("/sources/11", `{"topic":"test"}`, 204)

	// delete the keyvalue store
	del("/sources/10", 204)

	// export the pattern
	get("/groups/0/export", 200)

	// import a pattern
	pattern := `{"blocks":[{"label":"","type":"+","id":2,"inputs":[{"name":"addend","type":"fetch","value":"."},{"name":"addend","type":"fetch","value":"."}],"outputs":[{"name":"sum"}],"position":{"x":0,"y":0}},{"label":"","type":"delay","id":3,"inputs":[{"name":"passthrough","type":"fetch","value":"."},{"name":"duration","type":"const","value":"1s"}],"outputs":[{"name":"passthrough"}],"position":{"x":0,"y":0}}],"connections":[{"source":{"id":2,"route":0},"target":{"id":3,"route":0},"id":4}],"groups":[{"id":1,"label":"","children":[2,3],"position":{"x":0,"y":0}}]}`
	post("/groups/1/import", pattern, 200)

	// delete the log block
	del("/blocks/4", 204)

	// delete group 1
	del("/groups/1", 204)

	// get the library
	get("/library", 200)

	// generate some errors
	del("/groups/1", 400)                                                                       // delete a group we've already deleted
	del("/groups/", 400)                                                                        // delete unspecified group
	del("/blocks/246", 400)                                                                     // delete an unknown block
	post("/groups/1/import", "{}", 400)                                                         // import empty
	post("/groups/1/import", "{bla}", 400)                                                      // import malformed
	get("/groups/6/export", 400)                                                                // export an unknown group
	post("/sources", `{"type":"GodHead"}`, 400)                                                 // create an unknown source
	put("/groups/8/children/4", "", 400)                                                        // modify an unknown group
	put("/groups/0/children/34", "", 400)                                                       // move an unknown block to group 0
	put("/blocks/2/routes/20", `{"type":"fetch","value":".myResult"}`, 400)                     // set an unknown route
	put("/blocks/240/routes/0", `{"type":"fetch","value":".myResult"}`, 400)                    // set an unknown block's route
	put("/blocks/2/routes/0", `{"type":"fetch","value":"invalid"}`, 400)                        // set the + block's route to an invalid path
	put("/blocks/2/routes/0", `{"type":"value","value":"bob"}`, 400)                            // set the + block's route to an invalid value
	put("/blocks/2/routes/0", `{bobo}`, 400)                                                    // set the + block's route using malformed json
	post("/groups", `{"group":10}`, 400)                                                        // create a group with an unknown parent
	post("/groups", `{"group"10}`, 400)                                                         // create a group with malformed JSON
	post("/blocks", `{"type":"invalid", "group":0}`, 400)                                       // create a block of invalid type
	post("/blocks", `{"type"lid", "group":1}`, 400)                                             // create a block with malformed json
	post("/blocks", `{"type":"latch", "group":10}`, 400)                                        // create a block witha group that doesn't exist
	post("/connections", `{"source":{"id":700, "Route":0}, "target":{"id":2, "Route":0}}`, 400) //connect unknown source
	//TODO this one panics
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":200, "Route":0}}`, 400) //connect unknown target
	post("/connections", `{"source":{"i:0}, "ta200, "Route":0}}`, 400)                          //connect with malformed json
	post("/connections", `{}`, 400)                                                             //connect with empty json
	post("/connections", "", 400)                                                               //connect with empty string
	del("/connections/289", 400)                                                                //delete unknown connection
	del("/connections/", 400)                                                                   //delete unspecified connection
	del("/connections/invalid", 400)                                                            //delete malformed connection
}
<|endoftext|>


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

package go_n1ql

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

type n1qlRows struct {
	resp       *http.Response
	results    io.Reader
	resultChan chan interface{}
	errChan    chan error
	closed     bool
	rows       []string
	extras     interface{}
	metrics    interface{}
}

func resultToRows(results io.Reader, resp *http.Response, signature []string, metrics, extraVals interface{}) (*n1qlRows, error) {

	rows := &n1qlRows{results: results,
		resp:       resp,
		rows:       signature,
		extras:     extraVals,
		metrics:    metrics,
		resultChan: make(chan interface{}, 1),
		errChan:    make(chan error),
	}
	go rows.populateRows()

	return rows, nil
}

func (rows *n1qlRows) populateRows() {
	var resultRows []interface{}
	defer rows.resp.Body.Close()

	resultsDecoder := json.NewDecoder(rows.results)
	err := resultsDecoder.Decode(&resultRows)

	if err != nil {
		rows.errChan <- err
	}

	if rows.extras != nil {
		rows.resultChan <- rows.extras
	}

	for _, row := range resultRows {
		if rows.closed == true {
			break
		}
		rows.resultChan <- row
	}

	// last row will be metrics
	if rows.metrics != nil {
		rows.resultChan <- rows.metrics
	}

	close(rows.resultChan)

}

func (rows *n1qlRows) Columns() []string {
	return rows.rows
}

func (rows *n1qlRows) Close() error {
	rows.closed = true
	return nil
}

func (rows *n1qlRows) Next(dest []driver.Value) error {
	select {
	case r, ok := <-rows.resultChan:
		if ok {
			numColumns := len(rows.Columns())

			if numColumns == 1 {
				bytes, _ := json.Marshal(r)
				dest[0] = bytes
			} else {
				switch resultRow := r.(type) {
				case map[string]interface{}:
					if len(resultRow) != numColumns {
						return fmt.Errorf("N1QL: Columns do not match %d != %d r %v", len(resultRow), numColumns, r)
					}
					i := 0
					for _, value := range resultRow {
						bytes, _ := json.Marshal(value)
						dest[i] = bytes
						i++
					}
				case []interface{}:
					i := 0
					for _, value := range resultRow {
						bytes, _ := json.Marshal(value)
						dest[i] = bytes
						i++
					}

				}
			}
			return nil
		} else {
			return io.EOF
		}
	case e := <-rows.errChan:
		return e
	}
}
<commit_msg>Make metrics the second row for the results<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package go_n1ql

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

type n1qlRows struct {
	resp       *http.Response
	results    io.Reader
	resultChan chan interface{}
	errChan    chan error
	closed     bool
	rows       []string
	extras     interface{}
	metrics    interface{}
}

func resultToRows(results io.Reader, resp *http.Response, signature []string, metrics, extraVals interface{}) (*n1qlRows, error) {

	rows := &n1qlRows{results: results,
		resp:       resp,
		rows:       signature,
		extras:     extraVals,
		metrics:    metrics,
		resultChan: make(chan interface{}, 1),
		errChan:    make(chan error),
	}
	go rows.populateRows()

	return rows, nil
}

func (rows *n1qlRows) populateRows() {
	var resultRows []interface{}
	defer rows.resp.Body.Close()

	resultsDecoder := json.NewDecoder(rows.results)
	err := resultsDecoder.Decode(&resultRows)

	if err != nil {
		rows.errChan <- err
	}

	if rows.extras != nil {
		rows.resultChan <- rows.extras
	}

	// second row will be metrics
	if rows.metrics != nil {
		rows.resultChan <- rows.metrics
	}

	for _, row := range resultRows {
		if rows.closed == true {
			break
		}
		rows.resultChan <- row
	}

	close(rows.resultChan)

}

func (rows *n1qlRows) Columns() []string {
	return rows.rows
}

func (rows *n1qlRows) Close() error {
	rows.closed = true
	return nil
}

func (rows *n1qlRows) Next(dest []driver.Value) error {
	select {
	case r, ok := <-rows.resultChan:
		if ok {
			numColumns := len(rows.Columns())

			if numColumns == 1 {
				bytes, _ := json.Marshal(r)
				dest[0] = bytes
			} else {
				switch resultRow := r.(type) {
				case map[string]interface{}:
					if len(resultRow) != numColumns {
						return fmt.Errorf("N1QL: Columns do not match %d != %d r %v", len(resultRow), numColumns, r)
					}
					i := 0
					for _, value := range resultRow {
						bytes, _ := json.Marshal(value)
						dest[i] = bytes
						i++
					}
				case []interface{}:
					i := 0
					for _, value := range resultRow {
						bytes, _ := json.Marshal(value)
						dest[i] = bytes
						i++
					}

				}
			}
			return nil
		} else {
			return io.EOF
		}
	case e := <-rows.errChan:
		return e
	}
}
<|endoftext|>