<commit_before>package engine

import (
	"errors"
	"fmt"
	"sync"
)

type ErrNotLocated struct {
	Location Location
}

func (e *ErrNotLocated) Error() string {
	return "not located"
}

type ErrLocated struct {
	Location Location
}

func (e *ErrLocated) Error() string {
	return "located"
}

type ErrDotsOccupied struct {
	Dots []Dot // List of occupied dots
}

func (e *ErrDotsOccupied) Error() string {
	return "dots is occupied"
}

// Scene contains locations
type Scene struct {
	area           Area
	locations      []Location
	locationsMutex *sync.RWMutex
}

// NewScene returns new empty scene
func NewScene(width, height uint8) (*Scene, error) {
	area, err := NewUsefulArea(width, height)
	if err != nil {
		return nil, fmt.Errorf("cannot create scene: %s", err)
	}

	return &Scene{
		area:           area,
		locations:      make([]Location, 0),
		locationsMutex: &sync.RWMutex{},
	}, nil
}

// unsafeLocated returns true if passed location is located on scene
func (s *Scene) unsafeLocated(location Location) bool {
	for i := range s.locations {
		if s.locations[i].Equals(location) {
			return true
		}
	}
	return false
}

func (s *Scene) Located(location Location) bool {
	s.locationsMutex.RLock()
	defer s.locationsMutex.RUnlock()
	return s.unsafeLocated(location)
}

// unsafeDotOccupied returns true if passed dot already used by a location on scene
func (s *Scene) unsafeDotOccupied(dot Dot) bool {
	if s.area.Contains(dot) {
		for _, location := range s.locations {
			if location.Contains(dot) {
				return true
			}
		}
	}
	return false
}

func (s *Scene) DotOccupied(dot Dot) bool {
	s.locationsMutex.RLock()
	defer s.locationsMutex.RUnlock()
	return s.unsafeDotOccupied(dot)
}

// unsafeGetLocationByDot returns location which contains passed dot
func (s *Scene) unsafeGetLocationByDot(dot Dot) Location {
	if s.area.Contains(dot) {
		for _, location := range s.locations {
			if location.Contains(dot) {
				return location.Copy()
			}
		}
	}
	return nil
}

func (s *Scene) GetLocationByDot(dot Dot) Location {
	s.locationsMutex.RLock()
	defer s.locationsMutex.RUnlock()
	return s.unsafeGetLocationByDot(dot)
}

type ErrLocate struct {
	Err error
}

func (e *ErrLocate) Error() string {
	return "cannot locate: " + e.Err.Error()
}

func (s *Scene) unsafeLocate(location Location) *ErrLocate {
	if s.unsafeLocated(location) {
		return &ErrLocate{
			Err: &ErrLocated{
				Location: location.Copy(),
			},
		}
	}

	location = location.Copy()
	occupiedDots := make([]Dot, 0)

	// Check each dot of passed location
	for i := uint16(0); i < location.DotCount(); i++ {
		var dot = location.Dot(i)

		if !s.area.Contains(dot) {
			return &ErrLocate{
				Err: &ErrAreaNotContainsDot{
					Dot: dot,
				},
			}
		}

		for i := range s.locations {
			if s.locations[i].Contains(dot) {
				occupiedDots = append(occupiedDots, dot)
				break
			}
		}
	}

	if len(occupiedDots) > 0 {
		return &ErrLocate{
			Err: &ErrDotsOccupied{
				Dots: occupiedDots,
			},
		}
	}

	// Add to location list of scene
	s.locations = append(s.locations, location)

	return nil
}

// Locate tries to create location to scene
func (s *Scene) Locate(location Location) *ErrLocate {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocate(location)
}

func (s *Scene) unsafeLocateAvailableDots(location Location) Location {
	if s.unsafeLocated(location) {
		return Location{}
	}

	location = location.Copy()
	locationMirror := location.Copy()

	// Check each dot of passed location
	for i := uint16(0); i < locationMirror.DotCount(); i++ {
		var dot = locationMirror.Dot(i)

		if !s.area.Contains(dot) {
			location = location.Delete(dot)
			continue
		}

		for i := range s.locations {
			if s.locations[i].Contains(dot) {
				location = location.Delete(dot)
				break
			}
		}
	}

	if len(location) > 0 {
		s.locations = append(s.locations, location)
	}

	return location.Copy()
}

func (s *Scene) LocateAvailableDots(location Location) Location {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateAvailableDots(location)
}

type ErrDelete struct {
	Err error
}

func (e *ErrDelete) Error() string {
	return "cannot delete"
}

// unsafeDelete deletes passed location from scene and returns error if there is a problem
func (s *Scene) unsafeDelete(location Location) *ErrDelete {
	for i := range s.locations {
		if s.locations[i].Equals(location) {
			s.locations = append(s.locations[:i], s.locations[i+1:]...)
			return nil
		}
	}

	return &ErrDelete{
		Err: &ErrNotLocated{
			Location: location.Copy(),
		},
	}
}

// Delete deletes passed location from scene and returns error if there is a problem
func (s *Scene) Delete(location Location) *ErrDelete {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeDelete(location)
}

type ErrRelocate struct {
	Err error
}

func (e *ErrRelocate) Error() string {
	return "cannot relocate"
}

func (s *Scene) unsafeRelocate(old, new Location) *ErrRelocate {
	if !s.unsafeLocated(old) {
		return &ErrRelocate{
			Err: &ErrNotLocated{
				Location: old.Copy(),
			},
		}
	}

	if s.unsafeLocated(new) {
		return &ErrRelocate{
			Err: &ErrLocated{
				Location: new.Copy(),
			},
		}
	}

	if err := s.unsafeDelete(old); err != nil {
		return &ErrRelocate{
			Err: err,
		}
	}

	if err := s.unsafeLocate(new); err != nil {
		return &ErrRelocate{
			Err: err,
		}
	}

	return nil
}

func (s *Scene) Relocate(old, new Location) *ErrRelocate {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeRelocate(old, new)
}

type ErrRelocateAvailableDots struct {
	Err error
}

func (e *ErrRelocateAvailableDots) Error() string {
	return "cannot relocate with available dots"
}

func (s *Scene) unsafeRelocateAvailableDots(old, new Location) (Location, *ErrRelocateAvailableDots) {
	if !s.unsafeLocated(old) {
		return nil, &ErrRelocateAvailableDots{
			Err: &ErrNotLocated{
				Location: old.Copy(),
			},
		}
	}

	if err := s.unsafeDelete(old); err != nil {
		return nil, &ErrRelocateAvailableDots{
			Err: err,
		}
	}

	dots := s.unsafeLocateAvailableDots(new)
	if len(dots) == 0 {
		return nil, &ErrRelocateAvailableDots{
			Err: &ErrDotsOccupied{
				Dots: new,
			},
		}
	}

	return dots.Copy(), nil
}

func (s *Scene) RelocateAvailableDots(old, new Location) (Location, *ErrRelocateAvailableDots) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeRelocateAvailableDots(old, new)
}

var FindRetriesNumber = 32

var ErrRetriesLimit = errors.New("retries limit was reached")

func (s *Scene) unsafeLocateRandomDot() (Location, error) {
	for count := 0; count < FindRetriesNumber; count++ {
		if dot := s.area.NewRandomDot(0, 0); !s.unsafeDotOccupied(dot) {
			if err := s.unsafeLocate(Location{dot}); err != nil {
				return nil, err
			}
			return Location{dot}, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomDot() (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateRandomDot()
}

func (s *Scene) unsafeLocateRandomRectTryOnce(rw, rh uint8) (Location, error) {
	if rect, err := s.area.NewRandomRect(rw, rh, 0, 0); err == nil {
		if err := s.unsafeLocate(rect.Location()); err != nil {
			return nil, err
		}
		return rect.Location(), nil
	} else {
		return nil, err
	}
}

func (s *Scene) unsafeLocateRandomRect(rw, rh uint8) (Location, error) {
	for count := 0; count < FindRetriesNumber; count++ {
		if rect, err := s.unsafeLocateRandomRectTryOnce(rw, rh); err == nil {
			return rect, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomRect(rw, rh uint8) (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateRandomRect(rw, rh)
}

func (s *Scene) unsafeLocateRandomRectMarginTryOnce(rw, rh, margin uint8) (Location, error) {
	if rect, err := s.area.NewRandomRect(rw+margin*2, rh+margin*2, 0, 0); err == nil {
		for i := uint16(0); i < rect.DotCount(); i++ {
			dot := rect.Dot(i)

			if !s.area.Contains(dot) {
				return nil, errors.New("area not contains generated dot")
			}

			for i := range s.locations {
				if s.locations[i].Contains(dot) {
					return nil, errors.New("generated dot is occupied")
				}
			}
		}

		resultRect := &Rect{
			x: rect.x + margin,
			y: rect.y + margin,
			w: rect.w - margin*2,
			h: rect.h - margin*2,
		}

		if err := s.unsafeLocate(resultRect.Location()); err != nil {
			return nil, err
		}

		return resultRect.Location(), nil
	} else {
		return nil, err
	}
}

func (s *Scene) unsafeLocateRandomRectMargin(rw, rh, margin uint8) (Location, error) {
	if margin == 0 {
		return s.unsafeLocateRandomRect(rw, rh)
	}

	for count := 0; count < FindRetriesNumber; count++ {
		if rect, err := s.unsafeLocateRandomRectMarginTryOnce(rw, rh, margin); err == nil {
			return rect, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomRectMargin(rw, rh, margin uint8) (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()

	if margin == 0 {
		return s.unsafeLocateRandomRect(rw, rh)
	}

	return s.unsafeLocateRandomRectMargin(rw, rh, margin)
}

func (s *Scene) Navigate(dot Dot, dir Direction, dis uint8) (Dot, error) {
	return s.area.Navigate(dot, dir, dis)
}

func (s *Scene) Size() uint16 {
	return s.area.Size()
}

func (s *Scene) Width() uint8 {
	return s.area.Width()
}

func (s *Scene) Height() uint8 {
	return s.area.Height()
}
<commit_msg>Create methods LocateRandomByDotsMask to Scene<commit_after>package engine

import (
	"errors"
	"fmt"
	"sync"
)

type ErrNotLocated struct {
	Location Location
}

func (e *ErrNotLocated) Error() string {
	return "not located"
}

type ErrLocated struct {
	Location Location
}

func (e *ErrLocated) Error() string {
	return "located"
}

type ErrDotsOccupied struct {
	Dots []Dot // List of occupied dots
}

func (e *ErrDotsOccupied) Error() string {
	return "dots is occupied"
}

// Scene contains locations
type Scene struct {
	area           Area
	locations      []Location
	locationsMutex *sync.RWMutex
}

// NewScene returns new empty scene
func NewScene(width, height uint8) (*Scene, error) {
	area, err := NewUsefulArea(width, height)
	if err != nil {
		return nil, fmt.Errorf("cannot create scene: %s", err)
	}

	return &Scene{
		area:           area,
		locations:      make([]Location, 0),
		locationsMutex: &sync.RWMutex{},
	}, nil
}

// unsafeLocated returns true if passed location is located on scene
func (s *Scene) unsafeLocated(location Location) bool {
	for i := range s.locations {
		if s.locations[i].Equals(location) {
			return true
		}
	}
	return false
}

func (s *Scene) Located(location Location) bool {
	s.locationsMutex.RLock()
	defer s.locationsMutex.RUnlock()
	return s.unsafeLocated(location)
}

// unsafeDotOccupied returns true if passed dot already used by a location on scene
func (s *Scene) unsafeDotOccupied(dot Dot) bool {
	if s.area.Contains(dot) {
		for _, location := range s.locations {
			if location.Contains(dot) {
				return true
			}
		}
	}
	return false
}

func (s *Scene) DotOccupied(dot Dot) bool {
	s.locationsMutex.RLock()
	defer s.locationsMutex.RUnlock()
	return s.unsafeDotOccupied(dot)
}

// unsafeGetLocationByDot returns location which contains passed dot
func (s *Scene) unsafeGetLocationByDot(dot Dot) Location {
	if s.area.Contains(dot) {
		for _, location := range s.locations {
			if location.Contains(dot) {
				return location.Copy()
			}
		}
	}
	return nil
}

func (s *Scene) GetLocationByDot(dot Dot) Location {
	s.locationsMutex.RLock()
	defer s.locationsMutex.RUnlock()
	return s.unsafeGetLocationByDot(dot)
}

type ErrLocate struct {
	Err error
}

func (e *ErrLocate) Error() string {
	return "cannot locate: " + e.Err.Error()
}

func (s *Scene) unsafeLocate(location Location) *ErrLocate {
	if s.unsafeLocated(location) {
		return &ErrLocate{
			Err: &ErrLocated{
				Location: location.Copy(),
			},
		}
	}

	location = location.Copy()
	occupiedDots := make([]Dot, 0)

	// Check each dot of passed location
	for i := uint16(0); i < location.DotCount(); i++ {
		var dot = location.Dot(i)

		if !s.area.Contains(dot) {
			return &ErrLocate{
				Err: &ErrAreaNotContainsDot{
					Dot: dot,
				},
			}
		}

		for i := range s.locations {
			if s.locations[i].Contains(dot) {
				occupiedDots = append(occupiedDots, dot)
				break
			}
		}
	}

	if len(occupiedDots) > 0 {
		return &ErrLocate{
			Err: &ErrDotsOccupied{
				Dots: occupiedDots,
			},
		}
	}

	// Add to location list of scene
	s.locations = append(s.locations, location)

	return nil
}

// Locate tries to create location to scene
func (s *Scene) Locate(location Location) *ErrLocate {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocate(location)
}

func (s *Scene) unsafeLocateAvailableDots(location Location) Location {
	if s.unsafeLocated(location) {
		return Location{}
	}

	location = location.Copy()
	locationMirror := location.Copy()

	// Check each dot of passed location
	for i := uint16(0); i < locationMirror.DotCount(); i++ {
		var dot = locationMirror.Dot(i)

		if !s.area.Contains(dot) {
			location = location.Delete(dot)
			continue
		}

		for i := range s.locations {
			if s.locations[i].Contains(dot) {
				location = location.Delete(dot)
				break
			}
		}
	}

	if len(location) > 0 {
		s.locations = append(s.locations, location)
	}

	return location.Copy()
}

func (s *Scene) LocateAvailableDots(location Location) Location {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateAvailableDots(location)
}

type ErrDelete struct {
	Err error
}

func (e *ErrDelete) Error() string {
	return "cannot delete"
}

// unsafeDelete deletes passed location from scene and returns error if there is a problem
func (s *Scene) unsafeDelete(location Location) *ErrDelete {
	for i := range s.locations {
		if s.locations[i].Equals(location) {
			s.locations = append(s.locations[:i], s.locations[i+1:]...)
			return nil
		}
	}

	return &ErrDelete{
		Err: &ErrNotLocated{
			Location: location.Copy(),
		},
	}
}

// Delete deletes passed location from scene and returns error if there is a problem
func (s *Scene) Delete(location Location) *ErrDelete {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeDelete(location)
}

type ErrRelocate struct {
	Err error
}

func (e *ErrRelocate) Error() string {
	return "cannot relocate"
}

func (s *Scene) unsafeRelocate(old, new Location) *ErrRelocate {
	if !s.unsafeLocated(old) {
		return &ErrRelocate{
			Err: &ErrNotLocated{
				Location: old.Copy(),
			},
		}
	}

	if s.unsafeLocated(new) {
		return &ErrRelocate{
			Err: &ErrLocated{
				Location: new.Copy(),
			},
		}
	}

	if err := s.unsafeDelete(old); err != nil {
		return &ErrRelocate{
			Err: err,
		}
	}

	if err := s.unsafeLocate(new); err != nil {
		return &ErrRelocate{
			Err: err,
		}
	}

	return nil
}

func (s *Scene) Relocate(old, new Location) *ErrRelocate {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeRelocate(old, new)
}

type ErrRelocateAvailableDots struct {
	Err error
}

func (e *ErrRelocateAvailableDots) Error() string {
	return "cannot relocate with available dots"
}

func (s *Scene) unsafeRelocateAvailableDots(old, new Location) (Location, *ErrRelocateAvailableDots) {
	if !s.unsafeLocated(old) {
		return nil, &ErrRelocateAvailableDots{
			Err: &ErrNotLocated{
				Location: old.Copy(),
			},
		}
	}

	if err := s.unsafeDelete(old); err != nil {
		return nil, &ErrRelocateAvailableDots{
			Err: err,
		}
	}

	dots := s.unsafeLocateAvailableDots(new)
	if len(dots) == 0 {
		return nil, &ErrRelocateAvailableDots{
			Err: &ErrDotsOccupied{
				Dots: new,
			},
		}
	}

	return dots.Copy(), nil
}

func (s *Scene) RelocateAvailableDots(old, new Location) (Location, *ErrRelocateAvailableDots) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeRelocateAvailableDots(old, new)
}

var FindRetriesNumber = 32

var ErrRetriesLimit = errors.New("retries limit was reached")

func (s *Scene) unsafeLocateRandomDot() (Location, error) {
	for count := 0; count < FindRetriesNumber; count++ {
		if dot := s.area.NewRandomDot(0, 0); !s.unsafeDotOccupied(dot) {
			if err := s.unsafeLocate(Location{dot}); err != nil {
				return nil, err
			}
			return Location{dot}, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomDot() (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateRandomDot()
}

func (s *Scene) unsafeLocateRandomRectTryOnce(rw, rh uint8) (Location, error) {
	if rect, err := s.area.NewRandomRect(rw, rh, 0, 0); err == nil {
		if err := s.unsafeLocate(rect.Location()); err != nil {
			return nil, err
		}
		return rect.Location(), nil
	} else {
		return nil, err
	}
}

func (s *Scene) unsafeLocateRandomRect(rw, rh uint8) (Location, error) {
	for count := 0; count < FindRetriesNumber; count++ {
		if rect, err := s.unsafeLocateRandomRectTryOnce(rw, rh); err == nil {
			return rect, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomRect(rw, rh uint8) (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateRandomRect(rw, rh)
}

func (s *Scene) unsafeLocateRandomRectMarginTryOnce(rw, rh, margin uint8) (Location, error) {
	if rect, err := s.area.NewRandomRect(rw+margin*2, rh+margin*2, 0, 0); err == nil {
		for i := uint16(0); i < rect.DotCount(); i++ {
			dot := rect.Dot(i)

			if !s.area.Contains(dot) {
				return nil, errors.New("area not contains generated dot")
			}

			for i := range s.locations {
				if s.locations[i].Contains(dot) {
					return nil, errors.New("generated dot is occupied")
				}
			}
		}

		resultRect := &Rect{
			x: rect.x + margin,
			y: rect.y + margin,
			w: rect.w - margin*2,
			h: rect.h - margin*2,
		}

		if err := s.unsafeLocate(resultRect.Location()); err != nil {
			return nil, err
		}

		return resultRect.Location(), nil
	} else {
		return nil, err
	}
}

func (s *Scene) unsafeLocateRandomRectMargin(rw, rh, margin uint8) (Location, error) {
	if margin == 0 {
		return s.unsafeLocateRandomRect(rw, rh)
	}

	for count := 0; count < FindRetriesNumber; count++ {
		if rect, err := s.unsafeLocateRandomRectMarginTryOnce(rw, rh, margin); err == nil {
			return rect, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomRectMargin(rw, rh, margin uint8) (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()

	if margin == 0 {
		return s.unsafeLocateRandomRect(rw, rh)
	}

	return s.unsafeLocateRandomRectMargin(rw, rh, margin)
}

func (s *Scene) unsafeLocateRandomByDotsMaskTryOnce(dm *DotsMask) (Location, error) {
	if rect, err := s.area.NewRandomRect(dm.Width(), dm.Height(), 0, 0); err == nil {
		location := dm.Location(rect.x, rect.y)
		for i := uint16(0); i < location.DotCount(); i++ {
			dot := location.Dot(i)

			if !s.area.Contains(dot) {
				return nil, errors.New("area not contains generated dot")
			}

			for i := range s.locations {
				if s.locations[i].Contains(dot) {
					return nil, errors.New("generated dot is occupied")
				}
			}
		}

		if err := s.unsafeLocate(location); err != nil {
			return nil, err
		}

		return location, nil
	} else {
		return nil, err
	}
}

func (s *Scene) unsafeLocateRandomByDotsMask(dm *DotsMask) (Location, error) {
	for count := 0; count < FindRetriesNumber; count++ {
		if location, err := s.unsafeLocateRandomByDotsMaskTryOnce(dm); err == nil {
			return location, nil
		}
	}

	return nil, ErrRetriesLimit
}

func (s *Scene) LocateRandomByDotsMask(dm *DotsMask) (Location, error) {
	s.locationsMutex.Lock()
	defer s.locationsMutex.Unlock()
	return s.unsafeLocateRandomByDotsMask(dm)
}

func (s *Scene) Navigate(dot Dot, dir Direction, dis uint8) (Dot, error) {
	return s.area.Navigate(dot, dir, dis)
}

func (s *Scene) Size() uint16 {
	return s.area.Size()
}

func (s *Scene) Width() uint8 {
	return s.area.Width()
}

func (s *Scene) Height() uint8 {
	return s.area.Height()
}
<|endoftext|>


<commit_before>// Package data provide simple CRUD operation on couchdb doc
package data

import (
	"encoding/json"
	"net/http"
	"net/url"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	perm "github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/web/files"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

func paramIsTrue(c echo.Context, param string) bool {
	return c.QueryParam(param) == "true"
}

// ValidDoctype validates the doctype and sets it in the context of the request.
func ValidDoctype(next echo.HandlerFunc) echo.HandlerFunc {
	// TODO extends me to verify characters allowed in db name.
	return func(c echo.Context) error {
		doctype := c.Param("doctype")
		if doctype == "" {
			return jsonapi.NewError(http.StatusBadRequest, "Invalid doctype '%s'", doctype)
		}
		c.Set("doctype", doctype)

		docidraw := c.Param("docid")
		docid, err := url.QueryUnescape(docidraw)
		if err != nil {
			return jsonapi.NewError(http.StatusBadRequest, "Invalid docid '%s'", docid)
		}
		c.Set("docid", docid)

		return next(c)
	}
}

func fixErrorNoDatabaseIsWrongDoctype(err error) error {
	if couchdb.IsNoDatabaseError(err) {
		err.(*couchdb.Error).Reason = "wrong_doctype"
	}
	return err
}

func allDoctypes(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	if err := permissions.AllowWholeType(c, permissions.GET, consts.Doctypes); err != nil {
		return err
	}

	types, err := couchdb.AllDoctypes(instance)
	if err != nil {
		return err
	}
	var doctypes []string
	for _, typ := range types {
		if perm.CheckReadable(typ) == nil {
			doctypes = append(doctypes, typ)
		}
	}
	return c.JSON(http.StatusOK, doctypes)
}

// GetDoc get a doc by its type and id
func getDoc(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)
	docid := c.Get("docid").(string)

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if docid == "" {
		return dbStatus(c)
	}

	if paramIsTrue(c, "revs") {
		return proxy(c, docid)
	}

	var out couchdb.JSONDoc
	err := couchdb.GetDoc(instance, doctype, docid, &out)
	if err != nil {
		return fixErrorNoDatabaseIsWrongDoctype(err)
	}

	out.Type = doctype

	if err := permissions.Allow(c, permissions.GET, &out); err != nil {
		return err
	}

	return c.JSON(http.StatusOK, out.ToMapWithType())
}

// CreateDoc create doc from the json passed as body
func createDoc(c echo.Context) error {
	doctype := c.Get("doctype").(string)
	instance := middlewares.GetInstance(c)

	doc := couchdb.JSONDoc{Type: doctype}
	if err := json.NewDecoder(c.Request().Body).Decode(&doc.M); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	if err := perm.CheckWritable(doctype); err != nil {
		return err
	}

	if err := permissions.Allow(c, permissions.POST, &doc); err != nil {
		return err
	}

	if err := couchdb.CreateDoc(instance, doc); err != nil {
		return err
	}

	return c.JSON(http.StatusCreated, echo.Map{
		"ok":   true,
		"id":   doc.ID(),
		"rev":  doc.Rev(),
		"type": doc.DocType(),
		"data": doc.ToMapWithType(),
	})
}

func createNamedDoc(c echo.Context, doc couchdb.JSONDoc) error {
	instance := middlewares.GetInstance(c)

	err := permissions.Allow(c, permissions.POST, &doc)
	if err != nil {
		return err
	}

	err = couchdb.CreateNamedDocWithDB(instance, doc)
	if err != nil {
		return fixErrorNoDatabaseIsWrongDoctype(err)
	}

	return c.JSON(http.StatusOK, echo.Map{
		"ok":   true,
		"id":   doc.ID(),
		"rev":  doc.Rev(),
		"type": doc.DocType(),
		"data": doc.ToMapWithType(),
	})
}

// UpdateDoc updates the document given in the request or creates a new one with
// the given id.
func UpdateDoc(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	var doc couchdb.JSONDoc
	if err := json.NewDecoder(c.Request().Body).Decode(&doc); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	doc.Type = c.Param("doctype")

	if err := perm.CheckWritable(doc.Type); err != nil {
		return err
	}

	if (doc.ID() == "") != (doc.Rev() == "") {
		return jsonapi.NewError(http.StatusBadRequest,
			"You must either provide an _id and _rev in document (update) or neither (create with fixed id).")
	}

	if doc.ID() != "" && doc.ID() != c.Get("docid").(string) {
		return jsonapi.NewError(http.StatusBadRequest, "document _id doesnt match url")
	}

	if doc.ID() == "" {
		doc.SetID(c.Get("docid").(string))
		return createNamedDoc(c, doc)
	}

	errWhole := permissions.AllowWholeType(c, permissions.PUT, doc.DocType())
	if errWhole != nil {

		// we cant apply to whole type, let's fetch old doc and see if it applies there
		var old couchdb.JSONDoc
		errFetch := couchdb.GetDoc(instance, doc.DocType(), doc.ID(), &old)
		if errFetch != nil {
			return errFetch
		}
		old.Type = doc.DocType()
		// check if permissions set allows manipulating old doc
		errOld := permissions.Allow(c, permissions.PUT, &old)
		if errOld != nil {
			return errOld
		}

		// also check if permissions set allows manipulating new doc
		errNew := permissions.Allow(c, permissions.PUT, &doc)
		if errNew != nil {
			return errNew
		}
	}

	errUpdate := couchdb.UpdateDoc(instance, doc)
	if errUpdate != nil {
		return fixErrorNoDatabaseIsWrongDoctype(errUpdate)
	}

	return c.JSON(http.StatusOK, echo.Map{
		"ok":   true,
		"id":   doc.ID(),
		"rev":  doc.Rev(),
		"type": doc.DocType(),
		"data": doc.ToMapWithType(),
	})
}

// DeleteDoc deletes the provided document from its database.
func DeleteDoc(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)
	docid := c.Get("docid").(string)
	revHeader := c.Request().Header.Get("If-Match")
	revQuery := c.QueryParam("rev")
	rev := ""

	if revHeader != "" && revQuery != "" && revQuery != revHeader {
		return jsonapi.NewError(http.StatusBadRequest,
			"If-Match Header and rev query parameters mismatch")
	} else if revHeader != "" {
		rev = revHeader
	} else if revQuery != "" {
		rev = revQuery
	} else {
		return jsonapi.NewError(http.StatusBadRequest, "delete without revision")
	}

	if err := perm.CheckWritable(doctype); err != nil {
		return err
	}

	var doc couchdb.JSONDoc
	err := couchdb.GetDoc(instance, doctype, docid, &doc)
	if err != nil {
		return err
	}
	doc.Type = doctype
	doc.SetRev(rev)

	err = permissions.Allow(c, permissions.DELETE, &doc)
	if err != nil {
		return err
	}

	err = couchdb.DeleteDoc(instance, &doc)
	if err != nil {
		return fixErrorNoDatabaseIsWrongDoctype(err)
	}

	return c.JSON(http.StatusOK, echo.Map{
		"ok":      true,
		"id":      doc.ID(),
		"rev":     doc.Rev(),
		"type":    doc.DocType(),
		"deleted": true,
	})
}

func defineIndex(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)

	var definitionRequest map[string]interface{}
	if err := json.NewDecoder(c.Request().Body).Decode(&definitionRequest); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	result, err := couchdb.DefineIndexRaw(instance, doctype, &definitionRequest)
	if couchdb.IsNoDatabaseError(err) {
		if err = couchdb.CreateDB(instance, doctype); err == nil || couchdb.IsFileExists(err) {
			result, err = couchdb.DefineIndexRaw(instance, doctype, &definitionRequest)
		}
	}
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, result)
}

const maxMangoLimit = 100

func findDocuments(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)

	var findRequest map[string]interface{}
	if err := json.NewDecoder(c.Request().Body).Decode(&findRequest); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	limit, hasLimit := findRequest["limit"].(float64)
	if !hasLimit || limit > maxMangoLimit {
		limit = 100
	}

	// add 1 so we know if there is more.
	findRequest["limit"] = limit + 1

	var results []couchdb.JSONDoc
	err := couchdb.FindDocsRaw(instance, doctype, &findRequest, &results)
	if err != nil {
		return err
	}

	out := echo.Map{
		"docs":  results,
		"limit": limit,
		"next":  false,
	}
	if len(results) > int(limit) {
		out["docs"] = results[:len(results)-1]
		out["next"] = true
	}

	return c.JSON(http.StatusOK, out)
}

func allDocs(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	return proxy(c, "_all_docs")
}

// mostly just to prevent couchdb crash on replications
func dataAPIWelcome(c echo.Context) error {
	return c.JSON(http.StatusOK, echo.Map{
		"message": "welcome to a cozy API",
	})
}

func couchdbStyleErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		err := next(c)
		if err == nil {
			return nil
		}

		if ce, ok := err.(*couchdb.Error); ok {
			return c.JSON(ce.StatusCode, ce.JSON())
		}

		if he, ok := err.(*echo.HTTPError); ok {
			return c.JSON(he.Code, echo.Map{"error": he.Error()})
		}

		if je, ok := err.(*jsonapi.Error); ok {
			return c.JSON(je.Status, echo.Map{"error": je.Title})
		}

		return c.JSON(http.StatusInternalServerError, echo.Map{
			"error": err.Error(),
		})
	}
}

// Routes sets the routing for the data service
func Routes(router *echo.Group) {
	router.Use(couchdbStyleErrorHandler)

	// API Routes that don't depend on a doctype
	router.GET("/", dataAPIWelcome)
	router.GET("/_all_doctypes", allDoctypes)

	// Accounts are handled specifically to remove the auth fields
	{
		accountGroup := router.Group("/"+consts.Accounts, accountDoctype)
		accountGroup.GET("/:docid", getAccount)
		accountGroup.PUT("/:docid", updateAccount)
		accountGroup.DELETE("/:docid", DeleteDoc)
		accountGroup.GET("/:docid/relationships/references", echo.NotFoundHandler)
		accountGroup.POST("/:docid/relationships/references", echo.MethodNotAllowedHandler)
		accountGroup.DELETE("/:docid/relationships/references", echo.MethodNotAllowedHandler)
		accountGroup.POST("/", createAccount)
		accountGroup.GET("/_all_docs", allDocs)
		accountGroup.POST("/_all_docs", allDocs)
		accountGroup.POST("/_index", defineIndex)
		accountGroup.POST("/_find", findDocuments)
	}

	group := router.Group("/:doctype", ValidDoctype)

	replicationRoutes(group)

	// API Routes under /:doctype
	group.GET("/:docid", getDoc)
	group.PUT("/:docid", UpdateDoc)
	group.DELETE("/:docid", DeleteDoc)
	group.GET("/:docid/relationships/references", files.ListReferencesHandler)
	group.POST("/:docid/relationships/references", files.AddReferencesHandler)
	group.DELETE("/:docid/relationships/references", files.RemoveReferencesHandler)
	group.POST("/", createDoc)
	group.GET("/_all_docs", allDocs)
	group.POST("/_all_docs", allDocs)
	group.POST("/_index", defineIndex)
	group.POST("/_find", findDocuments)
}
<commit_msg>Fix routing for replication routes<commit_after>// Package data provide simple CRUD operation on couchdb doc
package data

import (
	"encoding/json"
	"net/http"
	"net/url"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	perm "github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/web/files"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

func paramIsTrue(c echo.Context, param string) bool {
	return c.QueryParam(param) == "true"
}

// ValidDoctype validates the doctype and sets it in the context of the request.
func ValidDoctype(next echo.HandlerFunc) echo.HandlerFunc {
	// TODO extends me to verify characters allowed in db name.
	return func(c echo.Context) error {
		doctype := c.Param("doctype")
		if doctype == "" {
			return jsonapi.NewError(http.StatusBadRequest, "Invalid doctype '%s'", doctype)
		}
		c.Set("doctype", doctype)

		docidraw := c.Param("docid")
		docid, err := url.QueryUnescape(docidraw)
		if err != nil {
			return jsonapi.NewError(http.StatusBadRequest, "Invalid docid '%s'", docid)
		}
		c.Set("docid", docid)

		return next(c)
	}
}

func fixErrorNoDatabaseIsWrongDoctype(err error) error {
	if couchdb.IsNoDatabaseError(err) {
		err.(*couchdb.Error).Reason = "wrong_doctype"
	}
	return err
}

func allDoctypes(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	if err := permissions.AllowWholeType(c, permissions.GET, consts.Doctypes); err != nil {
		return err
	}

	types, err := couchdb.AllDoctypes(instance)
	if err != nil {
		return err
	}
	var doctypes []string
	for _, typ := range types {
		if perm.CheckReadable(typ) == nil {
			doctypes = append(doctypes, typ)
		}
	}
	return c.JSON(http.StatusOK, doctypes)
}

// GetDoc get a doc by its type and id
func getDoc(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)
	docid := c.Get("docid").(string)

	// Accounts are handled specifically to remove the auth fields
	if doctype == consts.Accounts {
		return getAccount(c)
	}

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if docid == "" {
		return dbStatus(c)
	}

	if paramIsTrue(c, "revs") {
		return proxy(c, docid)
	}

	var out couchdb.JSONDoc
	err := couchdb.GetDoc(instance, doctype, docid, &out)
	if err != nil {
		return fixErrorNoDatabaseIsWrongDoctype(err)
	}

	out.Type = doctype

	if err := permissions.Allow(c, permissions.GET, &out); err != nil {
		return err
	}

	return c.JSON(http.StatusOK, out.ToMapWithType())
}

// CreateDoc create doc from the json passed as body
func createDoc(c echo.Context) error {
	doctype := c.Get("doctype").(string)
	instance := middlewares.GetInstance(c)

	// Accounts are handled specifically to remove the auth fields
	if doctype == consts.Accounts {
		return createAccount(c)
	}

	doc := couchdb.JSONDoc{Type: doctype}
	if err := json.NewDecoder(c.Request().Body).Decode(&doc.M); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	if err := perm.CheckWritable(doctype); err != nil {
		return err
	}

	if err := permissions.Allow(c, permissions.POST, &doc); err != nil {
		return err
	}

	if err := couchdb.CreateDoc(instance, doc); err != nil {
		return err
	}

	return c.JSON(http.StatusCreated, echo.Map{
		"ok":   true,
		"id":   doc.ID(),
		"rev":  doc.Rev(),
		"type": doc.DocType(),
		"data": doc.ToMapWithType(),
	})
}

func createNamedDoc(c echo.Context, doc couchdb.JSONDoc) error {
	instance := middlewares.GetInstance(c)

	err := permissions.Allow(c, permissions.POST, &doc)
	if err != nil {
		return err
	}

	err = couchdb.CreateNamedDocWithDB(instance, doc)
	if err != nil {
		return fixErrorNoDatabaseIsWrongDoctype(err)
	}

	return c.JSON(http.StatusOK, echo.Map{
		"ok":   true,
		"id":   doc.ID(),
		"rev":  doc.Rev(),
		"type": doc.DocType(),
		"data": doc.ToMapWithType(),
	})
}

// UpdateDoc updates the document given in the request or creates a new one with
// the given id.
func UpdateDoc(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Param("doctype")

	// Accounts are handled specifically to remove the auth fields
	if doctype == consts.Accounts {
		return updateAccount(c)
	}

	var doc couchdb.JSONDoc
	if err := json.NewDecoder(c.Request().Body).Decode(&doc); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	doc.Type = doctype

	if err := perm.CheckWritable(doc.Type); err != nil {
		return err
	}

	if (doc.ID() == "") != (doc.Rev() == "") {
		return jsonapi.NewError(http.StatusBadRequest,
			"You must either provide an _id and _rev in document (update) or neither (create with fixed id).")
	}

	if doc.ID() != "" && doc.ID() != c.Get("docid").(string) {
		return jsonapi.NewError(http.StatusBadRequest, "document _id doesnt match url")
	}

	if doc.ID() == "" {
		doc.SetID(c.Get("docid").(string))
		return createNamedDoc(c, doc)
	}

	errWhole := permissions.AllowWholeType(c, permissions.PUT, doc.DocType())
	if errWhole != nil {

		// we cant apply to whole type, let's fetch old doc and see if it applies there
		var old couchdb.JSONDoc
		errFetch := couchdb.GetDoc(instance, doc.DocType(), doc.ID(), &old)
		if errFetch != nil {
			return errFetch
		}
		old.Type = doc.DocType()
		// check if permissions set allows manipulating old doc
		errOld := permissions.Allow(c, permissions.PUT, &old)
		if errOld != nil {
			return errOld
		}

		// also check if permissions set allows manipulating new doc
		errNew := permissions.Allow(c, permissions.PUT, &doc)
		if errNew != nil {
			return errNew
		}
	}

	errUpdate := couchdb.UpdateDoc(instance, doc)
	if errUpdate != nil {
		return fixErrorNoDatabaseIsWrongDoctype(errUpdate)
	}

	return c.JSON(http.StatusOK, echo.Map{
		"ok":   true,
		"id":   doc.ID(),
		"rev":  doc.Rev(),
		"type": doc.DocType(),
		"data": doc.ToMapWithType(),
	})
}

// DeleteDoc deletes the provided document from its database.
func DeleteDoc(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)
	docid := c.Get("docid").(string)
	revHeader := c.Request().Header.Get("If-Match")
	revQuery := c.QueryParam("rev")
	rev := ""

	if revHeader != "" && revQuery != "" && revQuery != revHeader {
		return jsonapi.NewError(http.StatusBadRequest,
			"If-Match Header and rev query parameters mismatch")
	} else if revHeader != "" {
		rev = revHeader
	} else if revQuery != "" {
		rev = revQuery
	} else {
		return jsonapi.NewError(http.StatusBadRequest, "delete without revision")
	}

	if err := perm.CheckWritable(doctype); err != nil {
		return err
	}

	var doc couchdb.JSONDoc
	err := couchdb.GetDoc(instance, doctype, docid, &doc)
	if err != nil {
		return err
	}
	doc.Type = doctype
	doc.SetRev(rev)

	err = permissions.Allow(c, permissions.DELETE, &doc)
	if err != nil {
		return err
	}

	err = couchdb.DeleteDoc(instance, &doc)
	if err != nil {
		return fixErrorNoDatabaseIsWrongDoctype(err)
	}

	return c.JSON(http.StatusOK, echo.Map{
		"ok":      true,
		"id":      doc.ID(),
		"rev":     doc.Rev(),
		"type":    doc.DocType(),
		"deleted": true,
	})
}

func defineIndex(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)

	var definitionRequest map[string]interface{}
	if err := json.NewDecoder(c.Request().Body).Decode(&definitionRequest); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	result, err := couchdb.DefineIndexRaw(instance, doctype, &definitionRequest)
	if couchdb.IsNoDatabaseError(err) {
		if err = couchdb.CreateDB(instance, doctype); err == nil || couchdb.IsFileExists(err) {
			result, err = couchdb.DefineIndexRaw(instance, doctype, &definitionRequest)
		}
	}
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, result)
}

const maxMangoLimit = 100

func findDocuments(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)

	var findRequest map[string]interface{}
	if err := json.NewDecoder(c.Request().Body).Decode(&findRequest); err != nil {
		return jsonapi.NewError(http.StatusBadRequest, err)
	}

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	limit, hasLimit := findRequest["limit"].(float64)
	if !hasLimit || limit > maxMangoLimit {
		limit = 100
	}

	// add 1 so we know if there is more.
	findRequest["limit"] = limit + 1

	var results []couchdb.JSONDoc
	err := couchdb.FindDocsRaw(instance, doctype, &findRequest, &results)
	if err != nil {
		return err
	}

	out := echo.Map{
		"docs":  results,
		"limit": limit,
		"next":  false,
	}
	if len(results) > int(limit) {
		out["docs"] = results[:len(results)-1]
		out["next"] = true
	}

	return c.JSON(http.StatusOK, out)
}

func allDocs(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := perm.CheckReadable(doctype); err != nil {
		return err
	}

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	return proxy(c, "_all_docs")
}

// mostly just to prevent couchdb crash on replications
func dataAPIWelcome(c echo.Context) error {
	return c.JSON(http.StatusOK, echo.Map{
		"message": "welcome to a cozy API",
	})
}

func couchdbStyleErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		err := next(c)
		if err == nil {
			return nil
		}

		if ce, ok := err.(*couchdb.Error); ok {
			return c.JSON(ce.StatusCode, ce.JSON())
		}

		if he, ok := err.(*echo.HTTPError); ok {
			return c.JSON(he.Code, echo.Map{"error": he.Error()})
		}

		if je, ok := err.(*jsonapi.Error); ok {
			return c.JSON(je.Status, echo.Map{"error": je.Title})
		}

		return c.JSON(http.StatusInternalServerError, echo.Map{
			"error": err.Error(),
		})
	}
}

// Routes sets the routing for the data service
func Routes(router *echo.Group) {
	router.Use(couchdbStyleErrorHandler)

	// API Routes that don't depend on a doctype
	router.GET("/", dataAPIWelcome)
	router.GET("/_all_doctypes", allDoctypes)

	group := router.Group("/:doctype", ValidDoctype)

	replicationRoutes(group)

	// API Routes under /:doctype
	group.GET("/:docid", getDoc)
	group.PUT("/:docid", UpdateDoc)
	group.DELETE("/:docid", DeleteDoc)
	group.GET("/:docid/relationships/references", files.ListReferencesHandler)
	group.POST("/:docid/relationships/references", files.AddReferencesHandler)
	group.DELETE("/:docid/relationships/references", files.RemoveReferencesHandler)
	group.POST("/", createDoc)
	group.GET("/_all_docs", allDocs)
	group.POST("/_all_docs", allDocs)
	group.POST("/_index", defineIndex)
	group.POST("/_find", findDocuments)
}
<|endoftext|>


<commit_before>// Package aws provides core functionality for making requests to AWS services.
package aws

// SDKName is the name of this AWS SDK
const SDKName = "aws-sdk-go"

// SDKVersion is the version of this SDK
const SDKVersion = "1.1.23"
<commit_msg>Tag release v1.1.24<commit_after>// Package aws provides core functionality for making requests to AWS services.
package aws

// SDKName is the name of this AWS SDK
const SDKName = "aws-sdk-go"

// SDKVersion is the version of this SDK
const SDKVersion = "1.1.24"
<|endoftext|>


<commit_before>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 client

import (
	. "atlantis/manager/rpc/types"
	"atlantis/router/config"
)

type UpdatePoolCommand struct {
	Name             string   `short:"n" long:"name" description:"the name of the pool"`
	HealthCheckEvery string   `short:"e" long:"check-every" default:"5s" description:"how often to check healthz"`
	HealthzTimeout   string   `short:"z" long:"healthz-timeout" default:"5s" description:"timeout for healthz checks"`
	RequestTimeout   string   `short:"r" long:"request-timeout" default:"120s" description:"timeout for requests"`
	Status           string   `short:"s" long:"status" default:"OK" description:"the pool's status"`
	Hosts            []string `short:"H" long:"host" description:"the pool's hosts"`
	Internal         bool     `short:"i" long:"internal" description:"true if internal"`
}

func (c *UpdatePoolCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Update Pool...")
	hosts := make(map[string]config.Host, len(c.Hosts))
	for _, host := range c.Hosts {
		hosts[host] = config.Host{Address: host}
	}
	arg := ManagerUpdatePoolArg{dummyAuthArg, config.Pool{Name: c.Name, Hosts: hosts, Internal: c.Internal,
		Config: config.PoolConfig{HealthzEvery: c.HealthCheckEvery, HealthzTimeout: c.HealthzTimeout,
			RequestTimeout: c.RequestTimeout, Status: c.Status}}}
	var reply ManagerUpdatePoolReply
	err = rpcClient.CallAuthed("UpdatePool", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type DeletePoolCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the pool"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *DeletePoolCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Delete Pool...")
	arg := ManagerDeletePoolArg{dummyAuthArg, c.Name, c.Internal}
	var reply ManagerDeletePoolReply
	err = rpcClient.CallAuthed("DeletePool", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type GetPoolCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the pool"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *GetPoolCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Get Pool...")
	arg := ManagerGetPoolArg{dummyAuthArg, c.Name, c.Internal}
	var reply ManagerGetPoolReply
	err = rpcClient.CallAuthed("GetPool", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> %v", reply.Pool.String())
	return Output(map[string]interface{}{"status": reply.Status, "pool": reply.Pool}, reply.Pool, nil)
}

type ListPoolsCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
}

func (c *ListPoolsCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("List Pools...")
	arg := ManagerListPoolsArg{dummyAuthArg, c.Internal}
	var reply ManagerListPoolsReply
	err = rpcClient.CallAuthed("ListPools", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> pools:")
	for _, pool := range reply.Pools {
		Log("->   %s", pool)
	}
	return Output(map[string]interface{}{"status": reply.Status, "pools": reply.Pools}, reply.Pools, nil)
}

type UpdateRuleCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the rule"`
	Type     string `short:"t" long:"type" description:"the type of the rule"`
	Value    string `short:"v" long:"value" description:"the rule's value"`
	Next     string `short:"x" long:"next" description:"the next ruleset"`
	Pool     string `short:"p" long:"pool" description:"the pool to point to if this rule succeeds"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *UpdateRuleCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Update Rule...")
	arg := ManagerUpdateRuleArg{dummyAuthArg, config.Rule{Name: c.Name, Type: c.Type, Value: c.Value, Next: c.Next,
		Pool: c.Pool, Internal: c.Internal}}
	var reply ManagerUpdateRuleReply
	err = rpcClient.Call("UpdateRule", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type DeleteRuleCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the rule"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *DeleteRuleCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Delete Rule...")
	arg := ManagerDeleteRuleArg{dummyAuthArg, c.Name, c.Internal}
	var reply ManagerDeleteRuleReply
	err = rpcClient.CallAuthed("DeleteRule", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type GetRuleCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the rule"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *GetRuleCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Get Rule...")
	arg := ManagerGetRuleArg{dummyAuthArg, c.Name, c.Internal}
	var reply ManagerGetRuleReply
	err = rpcClient.Call("GetRule", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> %v", reply.Rule.String())
	return Output(map[string]interface{}{"status": reply.Status, "rule": reply.Rule}, reply.Rule, nil)
}

type ListRulesCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
}

func (c *ListRulesCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("List Rules...")
	arg := ManagerListRulesArg{dummyAuthArg, c.Internal}
	var reply ManagerListRulesReply
	err = rpcClient.CallAuthed("ListRules", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> rules:")
	for _, rule := range reply.Rules {
		Log("->   %s", rule)
	}
	return Output(map[string]interface{}{"status": reply.Status, "rules": reply.Rules}, reply.Rules, nil)
}

type UpdateTrieCommand struct {
	Name     string   `short:"n" long:"name" description:"the name of the rule"`
	Rules    []string `short:"r" long:"rule" description:"the rules that make up the ruleset"`
	Internal bool     `short:"i" long:"internal" description:"true if internal"`
}

func (c *UpdateTrieCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Update Trie...")
	arg := ManagerUpdateTrieArg{dummyAuthArg, config.Trie{Name: c.Name, Rules: c.Rules, Internal: c.Internal}}
	var reply ManagerUpdateTrieReply
	err = rpcClient.CallAuthed("UpdateTrie", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type DeleteTrieCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the trie"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *DeleteTrieCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Delete Trie...")
	arg := ManagerDeleteTrieArg{dummyAuthArg, c.Name, c.Internal}
	var reply ManagerDeleteTrieReply
	err = rpcClient.CallAuthed("DeleteTrie", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type GetTrieCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the trie"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *GetTrieCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Get Trie...")
	arg := ManagerGetTrieArg{dummyAuthArg, c.Name, c.Internal}
	var reply ManagerGetTrieReply
	err = rpcClient.CallAuthed("GetTrie", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> %v", reply.Trie.String())
	return Output(map[string]interface{}{"status": reply.Status, "trie": reply.Trie}, reply.Trie, nil)
}

type ListTriesCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
}

func (c *ListTriesCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("List Tries...")
	arg := ManagerListTriesArg{dummyAuthArg, c.Internal}
	var reply ManagerListTriesReply
	err = rpcClient.CallAuthed("ListTries", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> tries:")
	for _, trie := range reply.Tries {
		Log("->   %s", trie)
	}
	return Output(map[string]interface{}{"status": reply.Status, "tries": reply.Tries}, reply.Tries, nil)
}

type UpdatePortCommand struct {
	Port     uint16 `short:"p" long:"port" description:"the actual port to listen on"`
	Trie     string `short:"t" long:"trie" description:"the trie to use as root for this port"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *UpdatePortCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Update Port...")
	arg := ManagerUpdatePortArg{
		ManagerAuthArg: dummyAuthArg,
		Port: config.Port{
			Port:     c.Port,
			Trie:     c.Trie,
			Internal: c.Internal,
		},
	}
	var reply ManagerUpdatePortReply
	err = rpcClient.CallAuthed("UpdatePort", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type DeletePortCommand struct {
	Port     uint16 `short:"p" long:"port" description:"the port number"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *DeletePortCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Delete Port...")
	arg := ManagerDeletePortArg{dummyAuthArg, c.Port, c.Internal}
	var reply ManagerDeletePortReply
	err = rpcClient.CallAuthed("DeletePort", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type GetPortCommand struct {
	Port     uint16 `short:"p" long:"port" description:"the port number"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *GetPortCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Get Port...")
	arg := ManagerGetPortArg{dummyAuthArg, c.Port, c.Internal}
	var reply ManagerGetPortReply
	err = rpcClient.CallAuthed("GetPort", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> %v", reply.Port.String())
	return Output(map[string]interface{}{"status": reply.Status, "port": reply.Port}, reply.Port, nil)
}

type ListPortsCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
}

func (c *ListPortsCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("List Ports...")
	arg := ManagerListPortsArg{dummyAuthArg, c.Internal}
	var reply ManagerListPortsReply
	err = rpcClient.CallAuthed("ListPorts", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> ports:")
	for _, port := range reply.Ports {
		Log("->   %d", port)
	}
	return Output(map[string]interface{}{"status": reply.Status, "ports": reply.Ports}, reply.Ports, nil)
}

type GetAppEnvPortCommand struct {
	App      string `short:"a" long:"app" description:"the app of the port"`
	Env      string `short:"e" long:"env" description:"the env of the port"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
}

func (c *GetAppEnvPortCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Get AppEnv Port...")
	arg := ManagerGetAppEnvPortArg{
		ManagerAuthArg: dummyAuthArg,
		App:            c.App,
		Env:            c.Env,
	}
	var reply ManagerGetAppEnvPortReply
	err = rpcClient.CallAuthed("GetAppEnvPort", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> %v", reply.Port.String())
	return Output(map[string]interface{}{"status": reply.Status, "port": reply.Port}, reply.Port, nil)
}

type ListAppEnvsWithPortCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
}

func (c *ListAppEnvsWithPortCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("List AppEnvs With Ports...")
	arg := ManagerListAppEnvsWithPortArg{dummyAuthArg, c.Internal}
	var reply ManagerListAppEnvsWithPortReply
	err = rpcClient.CallAuthed("ListAppEnvsWithPort", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	Log("-> app+envs:")
	for _, appEnv := range reply.AppEnvs {
		Log("->   %s in %s", appEnv.App, appEnv.Env)
	}
	return Output(map[string]interface{}{"status": reply.Status, "appEnvs": reply.AppEnvs}, reply.AppEnvs, nil)
}
<commit_msg>Client: Migrate router.go to genericExecutor<commit_after>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 client

import (
	. "atlantis/manager/rpc/types"
	"atlantis/router/config"
)

type UpdatePoolCommand struct {
	Name             string   `short:"n" long:"name" description:"the name of the pool"`
	HealthCheckEvery string   `short:"e" long:"check-every" default:"5s" description:"how often to check healthz"`
	HealthzTimeout   string   `short:"z" long:"healthz-timeout" default:"5s" description:"timeout for healthz checks"`
	RequestTimeout   string   `short:"r" long:"request-timeout" default:"120s" description:"timeout for requests"`
	Status           string   `short:"s" long:"status" default:"OK" description:"the pool's status"`
	Hosts            []string `short:"H" long:"host" description:"the pool's hosts"`
	Internal         bool     `short:"i" long:"internal" description:"true if internal"`
}

func (c *UpdatePoolCommand) Execute(args []string) error {
	err := Init()
	if err != nil {
		return OutputError(err)
	}
	Log("Update Pool...")
	hosts := make(map[string]config.Host, len(c.Hosts))
	for _, host := range c.Hosts {
		hosts[host] = config.Host{Address: host}
	}
	arg := ManagerUpdatePoolArg{dummyAuthArg, config.Pool{Name: c.Name, Hosts: hosts, Internal: c.Internal,
		Config: config.PoolConfig{HealthzEvery: c.HealthCheckEvery, HealthzTimeout: c.HealthzTimeout,
			RequestTimeout: c.RequestTimeout, Status: c.Status}}}
	var reply ManagerUpdatePoolReply
	err = rpcClient.CallAuthed("UpdatePool", &arg, &reply)
	if err != nil {
		return OutputError(err)
	}
	Log("-> status: %s", reply.Status)
	return Output(map[string]interface{}{"status": reply.Status}, nil, nil)
}

type DeletePoolCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the pool"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerDeletePoolArg
	Reply    ManagerDeletePoolReply
}

func (c *DeletePoolCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type GetPoolCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the pool"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerGetPoolArg
	Reply    ManagerGetPoolReply
}

func (c *GetPoolCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type ListPoolsCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerListPoolsArg
	Reply    ManagerListPoolsReply
}

func (c *ListPoolsCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type UpdateRuleCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the rule"`
	Type     string `short:"t" long:"type" description:"the type of the rule"`
	Value    string `short:"v" long:"value" description:"the rule's value"`
	Next     string `short:"x" long:"next" description:"the next ruleset"`
	Pool     string `short:"p" long:"pool" description:"the pool to point to if this rule succeeds"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerUpdateRuleArg
	Reply    ManagerUpdateRuleReply
}

func (c *UpdateRuleCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type DeleteRuleCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the rule"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerDeleteRuleArg
	Reply    ManagerDeleteRuleReply
}

func (c *DeleteRuleCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type GetRuleCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the rule"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerGetRuleArg
	Reply    ManagerGetRuleReply
}

func (c *GetRuleCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type ListRulesCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerListRulesArg
	Reply    ManagerListRulesReply
}

func (c *ListRulesCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type UpdateTrieCommand struct {
	Name     string   `short:"n" long:"name" description:"the name of the rule"`
	Rules    []string `short:"r" long:"rule" description:"the rules that make up the ruleset"`
	Internal bool     `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerUpdateTrieArg
	Reply    ManagerUpdateTrieReply
}

func (c *UpdateTrieCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type DeleteTrieCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the trie"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerDeleteTrieArg
	Reply    ManagerDeleteTrieReply
}

func (c *DeleteTrieCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type GetTrieCommand struct {
	Name     string `short:"n" long:"name" description:"the name of the trie"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerGetTrieArg
	Reply    ManagerGetTrieReply
}

func (c *GetTrieCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type ListTriesCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerListTriesArg
	Reply    ManagerListTriesReply
}

func (c *ListTriesCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type UpdatePortCommand struct {
	Port     uint16 `short:"p" long:"port" description:"the actual port to listen on"`
	Trie     string `short:"t" long:"trie" description:"the trie to use as root for this port"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerUpdatePortArg
	Reply    ManagerUpdatePortReply
}

func (c *UpdatePortCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type DeletePortCommand struct {
	Port     uint16 `short:"p" long:"port" description:"the port number"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerDeletePortArg
	Reply    ManagerDeletePortReply
}

func (c *DeletePortCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type GetPortCommand struct {
	Port     uint16 `short:"p" long:"port" description:"the port number"`
	Internal bool   `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerGetPortArg
	Reply    ManagerGetPortReply
}

func (c *GetPortCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type ListPortsCommand struct {
	Internal bool `short:"i" long:"internal" description:"true if internal"`
	Arg      ManagerListPortsArg
	Reply    ManagerListPortsReply
}

func (c *ListPortsCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type GetAppEnvPortCommand struct {
	App        string `short:"a" long:"app" description:"the app of the port"`
	Env        string `short:"e" long:"env" description:"the env of the port"`
	Internal   bool   `short:"i" long:"internal" description:"true if internal"`
	Properties string `message:"Get AppEnv Port"`
	Arg        ManagerGetAppEnvPortArg
	Reply      ManagerGetAppEnvPortReply
}

func (c *GetAppEnvPortCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}

type ListAppEnvsWithPortCommand struct {
	Internal   bool   `short:"i" long:"internal" description:"true if internal"`
	Properties string `message:"List AppEnvs With Ports" field:"AppEnvs" name:"app+envs"`
	Arg        ManagerListAppEnvsWithPortArg
	Reply      ManagerListAppEnvsWithPortReply
}

func (c *ListAppEnvsWithPortCommand) Execute(args []string) error {
	return genericExecuter(c, args)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"log"
	"os"
	"os/exec"
)

func cloneRepo(repo Repo, env Env) error {
	repoPath := env.Config.Path + "/" + repo.FullName
	log.Println("Cloning new repository:", repoPath)

	// Clone into the current directory
	cmd := exec.Command("git", "clone", repo.SSHUrl, ".")

	// Set the current directory as the path to the repository
	cmd.Dir = repoPath

	// Grab stdout so we can log it if an error occurs
	var out bytes.Buffer
	cmd.Stdout = &out

	// Execute the clone
	err := cmd.Run()

	// If an error occurs, return a new error with the stdout
	if err != nil {
		return errors.New("Error cloning: " + out.String())
	}

	// If everything went well, return nil
	return nil
}

func fetchRepo(repo Repo, env Env) error {
	repoPath := env.Config.Path + "/" + repo.FullName

	log.Println("Fetching new repository:", repoPath)

	// Fetch the current directory
	cmd := exec.Command("git", "fetch")

	// Set the current directory as the path to the repository
	cmd.Dir = repoPath

	// Grab stdout so we can log it if an error occurs
	var out bytes.Buffer
	cmd.Stdout = &out

	// Execute the clone
	err := cmd.Run()

	// If an error occurs, return a new error with the stdout
	if err != nil {
		return errors.New("Error fetching: " + out.String())
	}

	// If everything went well, return nil
	return nil
}

// Checks a repostories in the get configured path.
// If the repository path doesn't exist, we should clone it.
// If the repository path does exist, we should run a fetch on it.
// All succesfull clones and fetches should be passed down their
// particular channel to communicate back to the user. Any errors
// encountered should be passed back through the error channel to
// notify the user of such.
// This returns "error", "fetch", "clone" or "ignore"
func checkRepo(repo Repo, env Env) string {
	log.Println("Starting check for:", repo.FullName)
	repoPath := env.Config.Path + "/" + repo.FullName

	// Check if the repo is ignored by it's name
	for _, ignoredName := range env.Config.IgnoredRepos {
		if ignoredName == repo.Name() {
			log.Println("Ignoring repository based on configuration:", repo.FullName)
			return "ignore"
		}
	}

	// Check if the repo is ignored by it's owner
	for _, ignoredOwner := range env.Config.IgnoredOwners {
		if ignoredOwner == repo.Owner() {
			log.Println("Ignoring repository based on configuration:", repo.FullName)
			return "ignore"
		}
	}

	// The path to the expected git internals
	gitPath := repoPath + "/.git"

	// Check to see if the directory is a git repository
	stat, _ := os.Stat(gitPath)

	if stat.IsDir() != true {
		// If the directory does not exist, we want to run a clone to
		// get it.
		cloneerr := cloneRepo(repo, env)

		if cloneerr != nil {
			// If there is a clone error, we should log it and return
			// that this repo failed with an error.
			log.Println(cloneerr)
			return "error"
		} else {
			// If there isn't a clone error, we should log that it was
			// succesful and return that it was a clone
			log.Println("Succesfully cloned repository:", repo.FullName)
			return "clone"
		}
	} else {
		// If the directory does exist, we want to run a fetch on it.
		fetcherr := fetchRepo(repo, env)

		if fetcherr != nil {
			// If there is a fetch error, we should log it and return
			// that this repo failed with an error.
			log.Println(fetcherr)
			return "error"
		} else {
			// If there isn't a fetch error, we should log that it was
			// succesful and return that it was a fetch
			log.Println("Succesfully fetched repository:", repo.FullName)
			return "fetch"
		}
	}

	return "unknown"
}
<commit_msg>More robust checking of the repositories existence<commit_after>package main

import (
	"bytes"
	"errors"
	"log"
	"os"
	"os/exec"
)

func cloneRepo(repo Repo, env Env) error {
	repoPath := env.Config.Path + "/" + repo.FullName
	log.Println("Cloning new repository:", repoPath)

	// Make the repository directory
	mkdirerr := os.MkdirAll(repoPath, 0777)

	// If an error occurs, return it
	if mkdirerr != nil {
		return mkdirerr
	}

	// Clone into the current directory
	cmd := exec.Command("git", "clone", repo.SSHUrl, ".")

	// Set the current directory as the path to the repository
	cmd.Dir = repoPath

	// Grab stdout so we can log it if an error occurs
	var out bytes.Buffer
	cmd.Stdout = &out

	// Execute the clone
	cloneerr := cmd.Run()

	// If an error occurs, return a new error with the stdout
	if cloneerr != nil {
		return errors.New("Error cloning: " + out.String())
	}

	// If everything went well, return nil
	return nil
}

func fetchRepo(repo Repo, env Env) error {
	repoPath := env.Config.Path + "/" + repo.FullName

	log.Println("Fetching new repository:", repoPath)

	// Fetch the current directory
	cmd := exec.Command("git", "fetch")

	// Set the current directory as the path to the repository
	cmd.Dir = repoPath

	// Grab stdout so we can log it if an error occurs
	var out bytes.Buffer
	cmd.Stdout = &out

	// Execute the clone
	err := cmd.Run()

	// If an error occurs, return a new error with the stdout
	if err != nil {
		return errors.New("Error fetching: " + out.String())
	}

	// If everything went well, return nil
	return nil
}

// Checks a repostories in the get configured path.
// If the repository path doesn't exist, we should clone it.
// If the repository path does exist, we should run a fetch on it.
// All succesfull clones and fetches should be passed down their
// particular channel to communicate back to the user. Any errors
// encountered should be passed back through the error channel to
// notify the user of such.
// This returns "error", "fetch", "clone" or "ignore"
func checkRepo(repo Repo, env Env) string {
	log.Println("Starting check for:", repo.FullName)
	repoPath := env.Config.Path + "/" + repo.FullName

	// Check if the repo is ignored by it's name
	for _, ignoredName := range env.Config.IgnoredRepos {
		if ignoredName == repo.Name() {
			log.Println("Ignoring repository based on configuration:", repo.FullName)
			return "ignore"
		}
	}

	// Check if the repo is ignored by it's owner
	for _, ignoredOwner := range env.Config.IgnoredOwners {
		if ignoredOwner == repo.Owner() {
			log.Println("Ignoring repository based on configuration:", repo.FullName)
			return "ignore"
		}
	}

	// The path to the expected git internals
	gitPath := repoPath + "/.git"

	// Check to see if the directory is a git repository
	stat, staterr := os.Stat(gitPath)

	if staterr != nil || stat.IsDir() != true {
		// If the directory does not exist, we want to run a clone to
		// get it.
		cloneerr := cloneRepo(repo, env)

		if cloneerr != nil {
			// If there is a clone error, we should log it and return
			// that this repo failed with an error.
			log.Println(cloneerr)
			return "error"
		} else {
			// If there isn't a clone error, we should log that it was
			// succesful and return that it was a clone
			log.Println("Succesfully cloned repository:", repo.FullName)
			return "clone"
		}
	} else {
		// If the directory does exist, we want to run a fetch on it.
		fetcherr := fetchRepo(repo, env)

		if fetcherr != nil {
			// If there is a fetch error, we should log it and return
			// that this repo failed with an error.
			log.Println(fetcherr)
			return "error"
		} else {
			// If there isn't a fetch error, we should log that it was
			// succesful and return that it was a fetch
			log.Println("Succesfully fetched repository:", repo.FullName)
			return "fetch"
		}
	}

	return "unknown"
}
<|endoftext|>


<commit_before>package gorequest

import (
	"fmt"
	"github.com/elazarl/goproxy"
	_ "io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

var robotsTxtHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Last-Modified", "sometime")
	fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
})

func TestGetFormat(t *testing.T) {
	//defer afterTest(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			t.Errorf("Expected method %q; got %q", "GET", r.Method)
		}
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
	}))
	defer ts.Close()

	New().Get(ts.URL).
		End()
}

func TestGetSetHeader(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			t.Errorf("Expected method %q; got %q", "GET", r.Method)
		}
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		if r.Header.Get("API-Key") != "fookey" {
			t.Errorf("Expected 'API-Key' == %q; got %q", "fookey", r.Header.Get("API-Key"))
		}
	}))
	defer ts.Close()

	New().Get(ts.URL).
		Set("API-Key", "fookey").
		End()
}

func TestPostFormat(t *testing.T) {

}

func TestPostSetHeader(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			t.Errorf("Expected method %q; got %q", "POST", r.Method)
		}
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		if r.Header.Get("API-Key") != "fookey" {
			t.Errorf("Expected 'API-Key' == %q; got %q", "fookey", r.Header.Get("API-Key"))
		}
	}))
	defer ts.Close()

	New().Post(ts.URL).
		Set("API-Key", "fookey").
		End()
}

/* TODO:Testing post for application/x-www-form-urlencoded
post.query(json), post.query(string), post.send(json), post.send(string), post.query(both).send(both)
*/
func TestPostFormSendString(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Send("query1=test").
		Send("query2=test").
		End()
}
func TestPostFormSendJson(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Send(`{"query1":"test"}`).
		Send(`{"query2":"test"}`).
		End()
}
func TestPostFormSendJsonAndString(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Send("query1=test").
		Send(`{"query2":"test"}`).
		End()
}

// TODO: check url query (all testcases)
func TestQueryFunc(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Query("query1=test").
		Query("query2=test").
		End(func(r Response, body string, errs []error) {
		r.Status = "10"
	})
	//fmt.Println(resp.Status)

}

// TODO: check redirect
func TestRedirectPolicyFunc(t *testing.T) {
	redirectSuccess := false
	redirectFuncGetCalled := false
	tsRedirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		redirectSuccess = true
	}))
	defer tsRedirect.Close()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, tsRedirect.URL, http.StatusMovedPermanently)
	}))
	defer ts.Close()

	New().
		Get(ts.URL).
		RedirectPolicy(func(req Request, via []Request) error {
		redirectFuncGetCalled = true
		return nil
	}).End()
	if !redirectSuccess {
		t.Errorf("Expected reaching another redirect url not original one")
	}
	if !redirectFuncGetCalled {
		t.Errorf("Expected redirect policy func to get called")
	}
}

func TestProxyFunc(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "proxy passed")
	}))
	defer ts.Close()
	// start proxy
	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			return r, nil
		})
	ts2 := httptest.NewServer(proxy)
	// sending request via Proxy
	resp, body, _ := New().Proxy(ts2.URL).Get(ts.URL).End()
	if resp.StatusCode != 200 {
		t.Errorf("Expected 200 Status code")
	}
	if body != "proxy passed" {
		t.Errorf("Expected 'proxy passed' body string")
	}
}

// TODO: added check for the correct timeout error string
// Right now, I see 2 different errors from timeout. Need to check why there are two of them. (i/o timeout and operation timed out)
func TestTimeoutFunc(t *testing.T) {
	_, _, errs := New().Timeout(1000 * time.Millisecond).Get("http://www.google.com:81").End()
	if errs == nil {
		t.Errorf("Expected timeout error but get nothing")
	}
}

func TestIntegration(t *testing.T) {

}
<commit_msg>Added sendStruct test body<commit_after>package gorequest

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/elazarl/goproxy"
)

var robotsTxtHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Last-Modified", "sometime")
	fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
})

func TestGetFormat(t *testing.T) {
	//defer afterTest(t)
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			t.Errorf("Expected method %q; got %q", "GET", r.Method)
		}
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
	}))
	defer ts.Close()

	New().Get(ts.URL).
		End()
}

func TestGetSetHeader(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			t.Errorf("Expected method %q; got %q", "GET", r.Method)
		}
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		if r.Header.Get("API-Key") != "fookey" {
			t.Errorf("Expected 'API-Key' == %q; got %q", "fookey", r.Header.Get("API-Key"))
		}
	}))
	defer ts.Close()

	New().Get(ts.URL).
		Set("API-Key", "fookey").
		End()
}

func TestPostFormat(t *testing.T) {

}

func TestPostSetHeader(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			t.Errorf("Expected method %q; got %q", "POST", r.Method)
		}
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		if r.Header.Get("API-Key") != "fookey" {
			t.Errorf("Expected 'API-Key' == %q; got %q", "fookey", r.Header.Get("API-Key"))
		}
	}))
	defer ts.Close()

	New().Post(ts.URL).
		Set("API-Key", "fookey").
		End()
}

/* TODO:Testing post for application/x-www-form-urlencoded
post.query(json), post.query(string), post.send(json), post.send(string), post.query(both).send(both)
*/
func TestPostFormSendString(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Send("query1=test").
		Send("query2=test").
		End()
}
func TestPostFormSendJson(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Send(`{"query1":"test"}`).
		Send(`{"query2":"test"}`).
		End()
}
func TestPostFormSendJsonAndString(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Send("query1=test").
		Send(`{"query2":"test"}`).
		End()
}

// TODO: check url query (all testcases)
func TestQueryFunc(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header == nil {
			t.Errorf("Expected non-nil request Header")
		}
		//fmt.Println(r.URL.Query())
	}))
	defer ts.Close()
	New().Post(ts.URL).
		Query("query1=test").
		Query("query2=test").
		End(func(r Response, body string, errs []error) {
		r.Status = "10"
	})
	//fmt.Println(resp.Status)

}

// TODO: check redirect
func TestRedirectPolicyFunc(t *testing.T) {
	redirectSuccess := false
	redirectFuncGetCalled := false
	tsRedirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		redirectSuccess = true
	}))
	defer tsRedirect.Close()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, tsRedirect.URL, http.StatusMovedPermanently)
	}))
	defer ts.Close()

	New().
		Get(ts.URL).
		RedirectPolicy(func(req Request, via []Request) error {
		redirectFuncGetCalled = true
		return nil
	}).End()
	if !redirectSuccess {
		t.Errorf("Expected reaching another redirect url not original one")
	}
	if !redirectFuncGetCalled {
		t.Errorf("Expected redirect policy func to get called")
	}
}

func TestProxyFunc(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "proxy passed")
	}))
	defer ts.Close()
	// start proxy
	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			return r, nil
		})
	ts2 := httptest.NewServer(proxy)
	// sending request via Proxy
	resp, body, _ := New().Proxy(ts2.URL).Get(ts.URL).End()
	if resp.StatusCode != 200 {
		t.Errorf("Expected 200 Status code")
	}
	if body != "proxy passed" {
		t.Errorf("Expected 'proxy passed' body string")
	}
}

// TODO: add all test cases in body comment
func TestSendStructFunc(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()
		body, _ := ioutil.ReadAll(r.Body)
		fmt.Println(body)
		// 1. test normal struct
		// 2. test 2nd layer nested struct
		// 3. test struct pointer
		// 4. test lowercase won't be export to json
		// 5. test field tag change to json field name
	}))
	defer ts.Close()
	type Upper struct {
		Color string
		Size  int
		note  string
	}
	type Lower struct {
		Color string
		Size  float64
		note  string
	}

	type Style struct {
		Upper Upper
		Lower Lower
		Name  string `json:"name"`
	}
	myStyle := Style{Upper: Upper{Color: "red"}, Name: "Cindy"}
	New().Post(ts.URL).
		Send(`{"a":"a"}`).
		Send(myStyle).
		End()
}

// TODO: added check for the correct timeout error string
// Right now, I see 2 different errors from timeout. Need to check why there are two of them. (i/o timeout and operation timed out)
func TestTimeoutFunc(t *testing.T) {
	_, _, errs := New().Timeout(1000 * time.Millisecond).Get("http://www.google.com:81").End()
	if errs == nil {
		t.Errorf("Expected timeout error but get nothing")
	}
}

func TestIntegration(t *testing.T) {

}
<|endoftext|>


<commit_before>package request

import (
	"testing"
)

const url string = "http://httpbin.org/get"

type httpbinResponseStruct struct {
	Args    map[string]interface{} `json:"args"`
	Headers map[string]interface{} `json:"headers"`
	Origin  string                 `json:"origin"`
	URL     string                 `json:"url"`
	JSON    map[string]string      `json:"json"`
}

type httpbinRequest struct {
	Property        string `json:"property"`
	AnotherProperty string `json:"anotherProperty"`
}

func TestAssignURL(t *testing.T) {

	r := New(url)

	if r.url != url {
		t.FailNow()
	}
}

func TestAssignGetMethod(t *testing.T) {

	method := GET

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignPutMethod(t *testing.T) {

	method := PUT

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignPostMethod(t *testing.T) {

	method := POST

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignDeleteMethod(t *testing.T) {

	method := DELETE

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignHeadMethod(t *testing.T) {

	method := HEAD

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignOptionsMethod(t *testing.T) {

	method := OPTIONS

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignIndividualHeader(t *testing.T) {

	r := New(url).Method(GET).Header("X-Test-Key", "Test Value")

	if r.headers["X-Test-Key"] != "Test Value" {
		t.FailNow()
	}
}

func TestAssignHeaderMap(t *testing.T) {

	// Assign an individual header to make sure
	// the header map does not override existing headers
	r := New(url).Method(GET).Header("X-Test-Key", "Test Value")

	r.Headers(map[string]string{"Another-Test": "Test2"})

	if r.headers["X-Test-Key"] != "Test Value" {
		t.FailNow()
	}

	if r.headers["Another-Test"] != "Test2" {
		t.FailNow()
	}
}

func TestGetRequestWithDo(t *testing.T) {

	_, err := New(url).Do()
	if err != nil {
		t.FailNow()
	}
}

func TestGetRequestWithMap(t *testing.T) {

	var result httpbinResponseStruct

	err := New(url).Map(&result)
	if err != nil {
		t.FailNow()
	}

	if result.URL != url {
		t.FailNow()
	}
}

func TestPostRequestWithoutPayload(t *testing.T) {

	var result httpbinResponseStruct

	err := Post("http://httpbin.org/post", nil).Map(&result)
	if err != nil {
		t.FailNow()
	}

	if result.URL != "http://httpbin.org/post" {
		t.FailNow()
	}
}

func TestPostRequestWithPayload(t *testing.T) {

	var result httpbinResponseStruct

	body := &httpbinRequest{
		Property:        "Foo",
		AnotherProperty: "Bar",
	}

	err := Post("http://httpbin.org/post", body).Map(&result)
	if err != nil {
		t.FailNow()
	}

	if result.URL != "http://httpbin.org/post" {
		t.FailNow()
	}

	if result.JSON["anotherProperty"] != body.AnotherProperty {
		t.FailNow()
	}
}
<commit_msg>add const for post url<commit_after>package request

import (
	"testing"
)

const url string = "http://httpbin.org/get"
const postURL string = "http://httpbin.org/post"

type httpbinResponseStruct struct {
	Args    map[string]interface{} `json:"args"`
	Headers map[string]interface{} `json:"headers"`
	Origin  string                 `json:"origin"`
	URL     string                 `json:"url"`
	JSON    map[string]string      `json:"json"`
}

type httpbinRequest struct {
	Property        string `json:"property"`
	AnotherProperty string `json:"anotherProperty"`
}

func TestAssignURL(t *testing.T) {

	r := New(url)

	if r.url != url {
		t.FailNow()
	}
}

func TestAssignGetMethod(t *testing.T) {

	method := GET

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignPutMethod(t *testing.T) {

	method := PUT

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignPostMethod(t *testing.T) {

	method := POST

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignDeleteMethod(t *testing.T) {

	method := DELETE

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignHeadMethod(t *testing.T) {

	method := HEAD

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignOptionsMethod(t *testing.T) {

	method := OPTIONS

	r := New(url).Method(method)

	if r.method != method {
		t.FailNow()
	}
}

func TestAssignIndividualHeader(t *testing.T) {

	r := New(url).Method(GET).Header("X-Test-Key", "Test Value")

	if r.headers["X-Test-Key"] != "Test Value" {
		t.FailNow()
	}
}

func TestAssignHeaderMap(t *testing.T) {

	// Assign an individual header to make sure
	// the header map does not override existing headers
	r := New(url).Method(GET).Header("X-Test-Key", "Test Value")

	r.Headers(map[string]string{"Another-Test": "Test2"})

	if r.headers["X-Test-Key"] != "Test Value" {
		t.FailNow()
	}

	if r.headers["Another-Test"] != "Test2" {
		t.FailNow()
	}
}

func TestGetRequestWithDo(t *testing.T) {

	_, err := New(url).Do()
	if err != nil {
		t.FailNow()
	}
}

func TestGetRequestWithMap(t *testing.T) {

	var result httpbinResponseStruct

	err := New(url).Map(&result)
	if err != nil {
		t.FailNow()
	}

	if result.URL != url {
		t.FailNow()
	}
}

func TestPostRequestWithoutPayload(t *testing.T) {

	var result httpbinResponseStruct

	err := Post("http://httpbin.org/post", nil).Map(&result)
	if err != nil {
		t.FailNow()
	}

	if result.URL != "http://httpbin.org/post" {
		t.FailNow()
	}
}

func TestPostRequestWithPayload(t *testing.T) {

	var result httpbinResponseStruct

	body := &httpbinRequest{
		Property:        "Foo",
		AnotherProperty: "Bar",
	}

	err := Post("http://httpbin.org/post", body).Map(&result)
	if err != nil {
		t.FailNow()
	}

	if result.URL != "http://httpbin.org/post" {
		t.FailNow()
	}

	if result.JSON["anotherProperty"] != body.AnotherProperty {
		t.FailNow()
	}
}
<|endoftext|>


<commit_before>package gremgo

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

// TestRequestPreparation tests the ability to package a query and a set of bindings into a request struct for further manipulation
func TestRequestPreparation(t *testing.T) {
	query := "g.V(x)"
	bindings := map[string]string{"x": "10"}
	req, id := prepareRequest(query, bindings)

	expectedRequest := request{
		Requestid: id,
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"bindings": map[string]string{"x": "10"},
			"language": "gremlin-groovy",
		},
	}

	if reflect.DeepEqual(req, expectedRequest) != true {
		t.Fail()
	}
}

func TestRequestPreparationNilBindings(t *testing.T) {
	query := "g.V(x)"
	req, id := prepareRequest(query, nil)

	expectedRequest := request{
		Requestid: id,
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"language": "gremlin-groovy",
		},
	}

	if reflect.DeepEqual(req, expectedRequest) != true {
		t.Fail()
	}
}

// TestRequestPackaging tests the ability for gremgo to format a request using the established Gremlin Server WebSockets protocol for delivery to the server
func TestRequestPackaging(t *testing.T) {
	testRequest := request{
		Requestid: "1d6d02bd-8e56-421d-9438-3bd6d0079ff1",
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"bindings": map[string]string{"x": "10"},
			"language": "gremlin-groovy",
		},
	}

	msg, err := packageRequest(testRequest)
	if err != nil {
		t.Error(err)
	}

	j, err := json.Marshal(testRequest)
	if err != nil {
		t.Error(err)
	}

	var expected []byte

	mimetype := []byte("application/json")
	mimetypelen := byte(len(mimetype))

	expected = append(expected, mimetypelen)
	expected = append(expected, mimetype...)
	expected = append(expected, j...)

	if reflect.DeepEqual(msg, expected) != true {
		t.Fail()
	}
}

// TestRequestDispatch tests the ability for a requester to send a request to the client for writing to Gremlin Server
func TestRequestDispatch(t *testing.T) {
	testRequest := request{
		Requestid: "1d6d02bd-8e56-421d-9438-3bd6d0079ff1",
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"bindings": map[string]string{"x": "10"},
			"language": "gremlin-groovy",
		},
	}
	c := newClient()
	msg, err := packageRequest(testRequest)
	if err != nil {
		t.Error(err)
	}
	c.dispatchRequest(msg)
	req := <-c.requests // c.requests is the channel where all requests are sent for writing to Gremlin Server, write workers listen on this channel
	if reflect.DeepEqual(msg, req) != true {
		t.Fail()
	}
}
<commit_msg>Fixed buggy test<commit_after>package gremgo

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

// TestRequestPreparation tests the ability to package a query and a set of bindings into a request struct for further manipulation
func TestRequestPreparation(t *testing.T) {
	query := "g.V(x)"
	bindings := map[string]string{"x": "10"}
	req, id := prepareRequest(query, bindings)

	expectedRequest := request{
		Requestid: id,
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"bindings": map[string]string{"x": "10"},
			"language": "gremlin-groovy",
		},
	}

	if reflect.DeepEqual(req, expectedRequest) != true {
		t.Fail()
	}
}

// TestRequestPackaging tests the ability for gremgo to format a request using the established Gremlin Server WebSockets protocol for delivery to the server
func TestRequestPackaging(t *testing.T) {
	testRequest := request{
		Requestid: "1d6d02bd-8e56-421d-9438-3bd6d0079ff1",
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"bindings": map[string]string{"x": "10"},
			"language": "gremlin-groovy",
		},
	}

	msg, err := packageRequest(testRequest)
	if err != nil {
		t.Error(err)
	}

	j, err := json.Marshal(testRequest)
	if err != nil {
		t.Error(err)
	}

	var expected []byte

	mimetype := []byte("application/json")
	mimetypelen := byte(len(mimetype))

	expected = append(expected, mimetypelen)
	expected = append(expected, mimetype...)
	expected = append(expected, j...)

	if reflect.DeepEqual(msg, expected) != true {
		t.Fail()
	}
}

// TestRequestDispatch tests the ability for a requester to send a request to the client for writing to Gremlin Server
func TestRequestDispatch(t *testing.T) {
	testRequest := request{
		Requestid: "1d6d02bd-8e56-421d-9438-3bd6d0079ff1",
		Op:        "eval",
		Processor: "",
		Args: map[string]interface{}{
			"gremlin":  "g.V(x)",
			"bindings": map[string]string{"x": "10"},
			"language": "gremlin-groovy",
		},
	}
	c := newClient()
	msg, err := packageRequest(testRequest)
	if err != nil {
		t.Error(err)
	}
	c.dispatchRequest(msg)
	req := <-c.requests // c.requests is the channel where all requests are sent for writing to Gremlin Server, write workers listen on this channel
	if reflect.DeepEqual(msg, req) != true {
		t.Fail()
	}
}
<|endoftext|>