<commit_before>package controllers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/astaxie/beego"

	"github.com/dockercn/wharf/models"
	"github.com/dockercn/wharf/utils"
)

type RepoWebAPIV1Controller struct {
	beego.Controller
}

func (this *RepoWebAPIV1Controller) Prepare() {
	this.EnableXSRF = false

	if user, exist := this.Ctx.Input.CruSession.Get("user").(models.User); exist {
		user.GetById(user.Id)
		this.Ctx.Input.CruSession.Set("user", user)
	}

	this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
}

func (this *RepoWebAPIV1Controller) JSONOut(code int, message string, data interface{}) {
	if data == nil {
		result := map[string]string{"message": message}
		this.Data["json"] = result
	} else {
		this.Data["json"] = data
	}

	this.Ctx.Output.Context.Output.SetStatus(code)
	this.ServeJson()
}

func (this *RepoWebAPIV1Controller) URLMapping() {
	this.Mapping("PostRepository", this.PostRepository)
}

func (this *RepoWebAPIV1Controller) PostRepository() {
	var user models.User
	user, exist := this.Ctx.Input.CruSession.Get("user").(models.User)
	if exist != true {
		this.JSONOut(http.StatusBadRequest, "", map[string]string{"message": "Session load failure", "url": "/auth"})
		return
	}

	var repo models.Repository

	if err := json.Unmarshal(this.Ctx.Input.CopyBody(), &repo); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else {
		if exist, _, err := repo.Has(repo.Namespace, repo.Repository); err != nil {
			this.JSONOut(http.StatusBadRequest, err.Error(), nil)
			return
		} else if exist == true {
			this.JSONOut(http.StatusBadRequest, "Repository already exist.", nil)
			return
		} else {
			repo.Id = string(utils.GeneralKey(fmt.Sprint(repo.Namespace, repo.Repository)))
			repo.Created = time.Now().UnixNano() / int64(time.Millisecond)
			repo.Updated = time.Now().UnixNano() / int64(time.Millisecond)

			if err := repo.Save(); err != nil {
				this.JSONOut(http.StatusBadRequest, "Repository save error.", nil)
				return
			}

			if user.Username == repo.Namespace {
				user.Repositories = append(user.Repositories, repo.Id)
				if err := user.Save(); err != nil {
					this.JSONOut(http.StatusBadRequest, err.Error(), nil)
					return
				}
				this.Ctx.Input.CruSession.Set("user", user)
			} else {
				org := new(models.Organization)
				if exist, _, _ := org.Has(repo.Namespace); exist == true {
					org.Repositories = append(org.Repositories, repo.Id)
					if err := org.Save(); err != nil {
						this.JSONOut(http.StatusBadRequest, "Organization save error.", nil)
						return
					}
				}
			}

			memo, _ := json.Marshal(this.Ctx.Input.Header)
			repo.Log(models.ACTION_ADD_REPO, models.LEVELINFORMATIONAL, models.TYPE_WEBV1, repo.Id, memo)
			user.Log(models.ACTION_ADD_REPO, models.LEVELINFORMATIONAL, models.TYPE_WEBV1, user.Id, memo)

			this.JSONOut(http.StatusOK, "Repository create successfully!", nil)
			return
		}
	}
}

func (this *RepoWebAPIV1Controller) GetRepository() {
	repo := new(models.Repository)

	if exist, _, err := repo.Has(this.Ctx.Input.Param(":namespace"), this.Ctx.Input.Param(":repository")); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else if exist == false {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	}

	this.JSONOut(http.StatusOK, "", repo)
	return
}

func (this *RepoWebAPIV1Controller) PutRepository() {
	repo := new(models.Repository)

	if exist, _, err := repo.Has(this.Ctx.Input.Param(":namespace"), this.Ctx.Input.Param(":repository")); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else if exist == false {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	}

	if err := json.Unmarshal(this.Ctx.Input.CopyBody(), &repo); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	}

	repo.Updated = time.Now().UnixNano() / int64(time.Millisecond)

	if err := repo.Save(); err != nil {
		this.JSONOut(http.StatusBadRequest, "Repository save error.", nil)
		return
	}

	this.JSONOut(http.StatusOK, "Repository update successfully!", nil)
	return
}

func (this *RepoWebAPIV1Controller) GetRepositories() {
	var user models.User
	user, exist := this.Ctx.Input.CruSession.Get("user").(models.User)
	if exist != true {
		this.JSONOut(http.StatusBadRequest, "", map[string]string{"message": "Session load failure", "url": "/auth"})
		return
	}

	repositories := make([]models.Repository, 0)
	for _, repoUUID := range user.Repositories {
		repo := new(models.Repository)
		if err := repo.Get(repoUUID); err != nil {
			continue
		}
		repositories = append(repositories, *repo)
	}

	this.JSONOut(http.StatusOK, "", user)
	return
}

func (this *RepoWebAPIV1Controller) GetCollaborators() {

}

func (this *RepoWebAPIV1Controller) PostCollaborator() {
	if user, exist := this.Ctx.Input.CruSession.Get("user").(models.User); exist == false {
		this.JSONOut(http.StatusBadRequest, "", map[string]string{"message": "Session load failure", "url": "/auth"})
		return
	} else {
		repo := new(models.Repository)

		//Collaborator In Organization
		if exist, _, err := repo.Has(this.Ctx.Input.Param(":namespace"), this.Ctx.Input.Param(":repository")); err != nil || exist == false {
			this.JSONOut(http.StatusBadRequest, err.Error(), nil)
			return
		}

		if user.Username == this.Ctx.Input.Param("namespace") {
		} else {
			for _, v := range repo.Permissions {
				if v == this.Ctx.Input.Param(":collaborator") {
					this.JSONOut(http.StatusBadRequest, "Collaborator already in permissions", nil)
					return
				}
			}

			repo.Permissions = append(repo.Permissions, this.Ctx.Input.Param(":collaborator"))

			if err := repo.Save(); err != nil {
				this.JSONOut(http.StatusBadRequest, err.Error(), nil)
				return
			}

			this.JSONOut(http.StatusOK, "Repository update successfully!", nil)
			return
		}
	}
}

func (this *RepoWebAPIV1Controller) PutCollaborator() {

}
<commit_msg>Refactoring the GetRepositories method.<commit_after>package controllers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/astaxie/beego"

	"github.com/dockercn/wharf/models"
	"github.com/dockercn/wharf/utils"
)

type RepoWebAPIV1Controller struct {
	beego.Controller
}

func (this *RepoWebAPIV1Controller) Prepare() {
	this.EnableXSRF = false

	if user, exist := this.Ctx.Input.CruSession.Get("user").(models.User); exist {
		user.GetById(user.Id)
		this.Ctx.Input.CruSession.Set("user", user)
	}

	this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
}

func (this *RepoWebAPIV1Controller) JSONOut(code int, message string, data interface{}) {
	if data == nil {
		result := map[string]string{"message": message}
		this.Data["json"] = result
	} else {
		this.Data["json"] = data
	}

	this.Ctx.Output.Context.Output.SetStatus(code)
	this.ServeJson()
}

func (this *RepoWebAPIV1Controller) URLMapping() {
	this.Mapping("GetRepositories", this.GetRepositories)
	this.Mapping("PostRepository", this.PostRepository)
	this.Mapping("PutRepository", this.PutRepository)
	this.Mapping("GetRepository", this.GetRepository)
	this.Mapping("GetCollaborators", this.GetCollaborators)
	this.Mapping("PostCollaborator", this.PostCollaborator)
	this.Mapping("PutCollaborator", this.PutCollaborator)
}

func (this *RepoWebAPIV1Controller) GetRepositories() {
	repositories := make([]models.Repository, 0)

	var user models.User
	if exist, _, err := user.Has(this.Ctx.Input.Param(":namespace")); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else if exist == false {
		var org models.Organization
		if exist, _, err := org.Has(this.Ctx.Input.Param(":namespace")); err != nil {
			this.JSONOut(http.StatusBadRequest, err.Error(), nil)
			return
		} else if exist == false {
			this.JSONOut(http.StatusBadRequest, "Invalide Namespace", nil)
			return
		}

		for _, id := range org.Repositories {
			repo := new(models.Repository)
			if err := repo.Get(id); err != nil {
				continue
			}
			repositories = append(repositories, *repo)
		}

		this.JSONOut(http.StatusOK, "", repositories)
		return
	} else if exist == true {
		for _, id := range user.Repositories {
			repo := new(models.Repository)
			if err := repo.Get(id); err != nil {
				continue
			}
			repositories = append(repositories, *repo)
		}

		this.JSONOut(http.StatusOK, "", repositories)
		return
	}
}

func (this *RepoWebAPIV1Controller) PostRepository() {
	var user models.User
	user, exist := this.Ctx.Input.CruSession.Get("user").(models.User)
	if exist != true {
		this.JSONOut(http.StatusBadRequest, "", map[string]string{"message": "Session load failure", "url": "/auth"})
		return
	}

	var repo models.Repository

	if err := json.Unmarshal(this.Ctx.Input.CopyBody(), &repo); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else {
		if exist, _, err := repo.Has(repo.Namespace, repo.Repository); err != nil {
			this.JSONOut(http.StatusBadRequest, err.Error(), nil)
			return
		} else if exist == true {
			this.JSONOut(http.StatusBadRequest, "Repository already exist.", nil)
			return
		} else {
			repo.Id = string(utils.GeneralKey(fmt.Sprint(repo.Namespace, repo.Repository)))
			repo.Created = time.Now().UnixNano() / int64(time.Millisecond)
			repo.Updated = time.Now().UnixNano() / int64(time.Millisecond)

			if err := repo.Save(); err != nil {
				this.JSONOut(http.StatusBadRequest, "Repository save error.", nil)
				return
			}

			if user.Username == repo.Namespace {
				user.Repositories = append(user.Repositories, repo.Id)
				if err := user.Save(); err != nil {
					this.JSONOut(http.StatusBadRequest, err.Error(), nil)
					return
				}
				this.Ctx.Input.CruSession.Set("user", user)
			} else {
				org := new(models.Organization)
				if exist, _, _ := org.Has(repo.Namespace); exist == true {
					org.Repositories = append(org.Repositories, repo.Id)
					if err := org.Save(); err != nil {
						this.JSONOut(http.StatusBadRequest, "Organization save error.", nil)
						return
					}
				}
			}

			memo, _ := json.Marshal(this.Ctx.Input.Header)
			repo.Log(models.ACTION_ADD_REPO, models.LEVELINFORMATIONAL, models.TYPE_WEBV1, repo.Id, memo)
			user.Log(models.ACTION_ADD_REPO, models.LEVELINFORMATIONAL, models.TYPE_WEBV1, user.Id, memo)

			this.JSONOut(http.StatusOK, "Repository create successfully!", nil)
			return
		}
	}
}

func (this *RepoWebAPIV1Controller) GetRepository() {
	repo := new(models.Repository)

	if exist, _, err := repo.Has(this.Ctx.Input.Param(":namespace"), this.Ctx.Input.Param(":repository")); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else if exist == false {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	}

	this.JSONOut(http.StatusOK, "", repo)
	return
}

func (this *RepoWebAPIV1Controller) PutRepository() {
	repo := new(models.Repository)

	if exist, _, err := repo.Has(this.Ctx.Input.Param(":namespace"), this.Ctx.Input.Param(":repository")); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	} else if exist == false {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	}

	if err := json.Unmarshal(this.Ctx.Input.CopyBody(), &repo); err != nil {
		this.JSONOut(http.StatusBadRequest, err.Error(), nil)
		return
	}

	repo.Updated = time.Now().UnixNano() / int64(time.Millisecond)

	if err := repo.Save(); err != nil {
		this.JSONOut(http.StatusBadRequest, "Repository save error.", nil)
		return
	}

	this.JSONOut(http.StatusOK, "Repository update successfully!", nil)
	return
}

func (this *RepoWebAPIV1Controller) GetCollaborators() {

}

func (this *RepoWebAPIV1Controller) PostCollaborator() {

}

func (this *RepoWebAPIV1Controller) PutCollaborator() {

}
<|endoftext|>


<commit_before>package tests

import (
	"github.com/bozaro/tech-db-forum/generated/client"
	"github.com/bozaro/tech-db-forum/generated/client/operations"
	"github.com/bozaro/tech-db-forum/generated/models"
)

func init() {
	Register(Checker{
		Name:        "forum_get_users_simple",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersSimple),
		Deps: []string{
			"post_create_simple",
		},
	})
	Register(Checker{
		Name:        "forum_get_users_notfound",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersNotFound),
		Deps: []string{
			"post_create_simple",
		},
	})
	Register(Checker{
		Name:        "forum_get_users_empty",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersEmpty),
		Deps: []string{
			"post_create_simple",
		},
	})
	Register(Checker{
		Name:        "forum_get_users_vote",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersVote),
		Deps: []string{
			"post_create_simple",
			"thread_create_vote_simple",
		},
	})
}

func CheckForumGetUsersSimple(c *client.Forum, m *Modify) {
	/*forum := CreateForum(c, nil, nil)
	threads := []models.Thread{}
	created := time.Now()
	created.Round(time.Millisecond)
	for i := 0; i < 10; i++ {
		thread := CreateThread(c, nil, forum, nil)
		threads = append(threads, *thread)
	}
	sort.Sort(ThreadByCreated(threads))

	var desc *bool

	// Desc
	small := time.Millisecond
	switch m.Int(3) {
	case 1:
		v := bool(true)
		small = -small
		desc = &v
		sort.Sort(sort.Reverse(ThreadByCreated(threads)))
	case 2:
		v := bool(false)
		desc = &v
	}

	// Check read all
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithDesc(desc).
		WithContext(Expected(200, &threads, nil)))

	// Check read by 4 records
	limit := int32(4)
	var since *strfmt.DateTime = nil
	for n := 0; n < len(threads); n += int(limit) - 1 {
		m := n + int(limit)
		if m > len(threads) {
			m = len(threads)
		}
		expected := threads[n:m]
		c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
			WithSlug(forum.Slug).
			WithLimit(&limit).
			WithDesc(desc).
			WithSince(since).
			WithContext(Expected(200, &expected, nil)))
		since = &threads[m - 1].Created
	}

	// Check read after all
	after_last := strfmt.DateTime(time.Time(threads[len(threads) - 1].Created).Add(small))
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(&limit).
		WithDesc(desc).
		WithSince(&after_last).
		WithContext(Expected(200, &[]models.Thread{}, nil)))*/
}

func CheckForumGetUsersEmpty(c *client.Forum, m *Modify) {
	var limit *int32
	var since *string
	var desc *bool

	forum := CreateForum(c, nil, nil)
	// Limit
	if m.Bool() {
		v := int32(10)
		limit = &v
	}
	// Since
	if m.Bool() {
		v := RandomNickname()
		since = &v
	}
	// Desc
	switch m.Int(3) {
	case 1:
		v := bool(true)
		desc = &v
	case 2:
		v := bool(false)
		desc = &v
	}

	// Check
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(limit).
		WithSince(since).
		WithDesc(desc).
		WithContext(Expected(200, &[]models.User{}, nil)))
}

func CheckForumGetUsersVote(c *client.Forum, m *Modify) {
	var limit *int32
	var since *string
	var desc *bool

	forum := CreateForum(c, nil, nil)

	user := CreateUser(c, nil)
	thread := CreateThread(c, nil, nil, nil)
	var err error
	// Like user1
	thread.Votes = 1
	_, err = c.Operations.ThreadVote(operations.NewThreadVoteParams().
		WithSlugOrID(m.SlugOrId(thread)).
		WithVote(&models.Vote{
			Nickname: user.Nickname,
			Voice:    1,
		}).
		WithContext(Expected(200, &thread, nil)))
	CheckNil(err)

	thread.Votes = 1
	CheckThread(c, thread)

	// Check
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(limit).
		WithSince(since).
		WithDesc(desc).
		WithContext(Expected(200, &[]models.User{}, nil)))
}

func CheckForumGetUsersNotFound(c *client.Forum, m *Modify) {
	var limit *int32
	var since *string
	var desc *bool

	forum := RandomForum()
	// Limit
	if m.Bool() {
		v := int32(10)
		limit = &v
	}
	// Since
	if m.Bool() {
		v := RandomNickname()
		since = &v
	}
	// Desc
	switch m.Int(3) {
	case 1:
		v := bool(true)
		desc = &v
	case 2:
		v := bool(false)
		desc = &v
	}

	// Check
	_, err := c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(limit).
		WithSince(since).
		WithDesc(desc).
		WithContext(Expected(404, nil, nil)))
	CheckIsType(operations.NewForumGetUsersNotFound(), err)
}
<commit_msg>Тесты для: forumGetUsers (fix #4)<commit_after>package tests

import (
	"github.com/bozaro/tech-db-forum/generated/client"
	"github.com/bozaro/tech-db-forum/generated/client/operations"
	"github.com/bozaro/tech-db-forum/generated/models"
	"math/rand"
	"sort"
	"time"
)

func init() {
	Register(Checker{
		Name:        "forum_get_users_simple",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersSimple),
		Deps: []string{
			"post_create_simple",
		},
	})
	Register(Checker{
		Name:        "forum_get_users_notfound",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersNotFound),
		Deps: []string{
			"post_create_simple",
		},
	})
	Register(Checker{
		Name:        "forum_get_users_empty",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersEmpty),
		Deps: []string{
			"post_create_simple",
		},
	})
	Register(Checker{
		Name:        "forum_get_users_vote",
		Description: "",
		FnCheck:     Modifications(CheckForumGetUsersVote),
		Deps: []string{
			"post_create_simple",
			"thread_create_vote_simple",
		},
	})
}

func CheckForumGetUsersSimple(c *client.Forum, m *Modify) {
	forum := CreateForum(c, nil, nil)
	threads := []*models.Thread{}
	users := []*models.User{}
	created := time.Now()
	created.Round(time.Millisecond)

	forum_users := map[string]*models.User{}
	// Пост, который не участвует в данном форуме
	CreatePost(c, nil, nil)
	// Пользователи
	for i := 0; i < 8; i++ {
		user := CreateUser(c, nil)
		users = append(users, user)
	}
	// Ветви
	for i := 0; i < 4; i++ {
		user := users[i/2]
		thread := CreateThread(c, nil, forum, user)
		threads = append(threads, thread)
		forum_users[user.Nickname] = user
	}
	// Посты
	for i := 0; i < 10; i++ {
		user := users[i%(len(users)-1)+1]
		thread := threads[rand.Intn(len(threads))]
		post := RandomPost()
		post.Author = user.Nickname
		CreatePost(c, post, thread)
		forum_users[user.Nickname] = user
	}

	// Список пользователей
	all_expected := []models.User{}
	for _, user := range forum_users {
		all_expected = append(all_expected, *user)
	}
	sort.Sort(UserByNickname(all_expected))

	var desc *bool

	// Desc
	small := time.Millisecond
	switch m.Int(3) {
	case 1:
		v := bool(true)
		small = -small
		desc = &v
		sort.Sort(sort.Reverse(UserByNickname(all_expected)))
	case 2:
		v := bool(false)
		desc = &v
	}

	// Check read all
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithDesc(desc).
		WithContext(Expected(200, &all_expected, nil)))

	// Check read by 4 records
	limit := int32(4)
	for n := 0; n < len(all_expected); n += int(limit) - 1 {
		m := n + int(limit)
		if m > len(all_expected) {
			m = len(all_expected)
		}
		expected := all_expected[n:m]
		var since *string = nil
		if n > 0 {
			since = &all_expected[n-1].Nickname
		}
		c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
			WithSlug(forum.Slug).
			WithLimit(&limit).
			WithDesc(desc).
			WithSince(since).
			WithContext(Expected(200, &expected, nil)))
	}

	// Check read after all
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(&limit).
		WithDesc(desc).
		WithSince(&all_expected[len(all_expected)-1].Nickname).
		WithContext(Expected(200, &[]models.Thread{}, nil)))
}

func CheckForumGetUsersEmpty(c *client.Forum, m *Modify) {
	var limit *int32
	var since *string
	var desc *bool

	forum := CreateForum(c, nil, nil)
	// Limit
	if m.Bool() {
		v := int32(10)
		limit = &v
	}
	// Since
	if m.Bool() {
		v := RandomNickname()
		since = &v
	}
	// Desc
	switch m.Int(3) {
	case 1:
		v := bool(true)
		desc = &v
	case 2:
		v := bool(false)
		desc = &v
	}

	// Check
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(limit).
		WithSince(since).
		WithDesc(desc).
		WithContext(Expected(200, &[]models.User{}, nil)))
}

func CheckForumGetUsersVote(c *client.Forum, m *Modify) {
	var limit *int32
	var since *string
	var desc *bool

	forum := CreateForum(c, nil, nil)

	user := CreateUser(c, nil)
	author := CreateUser(c, nil)
	thread := CreateThread(c, nil, forum, author)
	var err error
	// Like user1
	thread.Votes = 1
	_, err = c.Operations.ThreadVote(operations.NewThreadVoteParams().
		WithSlugOrID(m.SlugOrId(thread)).
		WithVote(&models.Vote{
			Nickname: user.Nickname,
			Voice:    1,
		}).
		WithContext(Expected(200, &thread, nil)))
	CheckNil(err)

	thread.Votes = 1
	CheckThread(c, thread)

	// Check
	c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(limit).
		WithSince(since).
		WithDesc(desc).
		WithContext(Expected(200, &[]models.User{*author}, nil)))
}

func CheckForumGetUsersNotFound(c *client.Forum, m *Modify) {
	var limit *int32
	var since *string
	var desc *bool

	forum := RandomForum()
	// Limit
	if m.Bool() {
		v := int32(10)
		limit = &v
	}
	// Since
	if m.Bool() {
		v := RandomNickname()
		since = &v
	}
	// Desc
	switch m.Int(3) {
	case 1:
		v := bool(true)
		desc = &v
	case 2:
		v := bool(false)
		desc = &v
	}

	// Check
	_, err := c.Operations.ForumGetUsers(operations.NewForumGetUsersParams().
		WithSlug(forum.Slug).
		WithLimit(limit).
		WithSince(since).
		WithDesc(desc).
		WithContext(Expected(404, nil, nil)))
	CheckIsType(operations.NewForumGetUsersNotFound(), err)
}
<|endoftext|>


<commit_before>package hoverfly

import (
	"fmt"
	"regexp"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/SpectoLabs/hoverfly/core/cache"
	"github.com/SpectoLabs/hoverfly/core/handlers/v1"
	"github.com/SpectoLabs/hoverfly/core/handlers/v2"
	"github.com/SpectoLabs/hoverfly/core/interfaces"
	"github.com/SpectoLabs/hoverfly/core/metrics"
	"github.com/SpectoLabs/hoverfly/core/models"
	"github.com/SpectoLabs/hoverfly/core/modes"
)

func (this Hoverfly) GetDestination() string {
	return this.Cfg.Destination
}

// UpdateDestination - updates proxy with new destination regexp
func (hf *Hoverfly) SetDestination(destination string) (err error) {
	_, err = regexp.Compile(destination)
	if err != nil {
		return fmt.Errorf("destination is not a valid regular expression string")
	}

	hf.mu.Lock()
	hf.StopProxy()
	hf.Cfg.Destination = destination
	err = hf.StartProxy()
	hf.mu.Unlock()
	return
}

func (this Hoverfly) GetMode() string {
	return this.Cfg.Mode
}

func (this *Hoverfly) SetMode(mode string) error {
	availableModes := map[string]bool{
		modes.Simulate:   true,
		modes.Capture:    true,
		modes.Modify:     true,
		modes.Synthesize: true,
	}

	if mode == "" || !availableModes[mode] {
		log.Error("Can't change mode to \"%d\"", mode)
		return fmt.Errorf("Not a valid mode")
	}

	if this.Cfg.Webserver && mode == modes.Capture {
		log.Error("Can't change mode to when configured as a webserver")
		return fmt.Errorf("Can't change mode to capture when configured as a webserver")
	}

	this.Cfg.SetMode(mode)
	return nil
}

func (hf Hoverfly) GetMiddleware() (string, string, string) {
	script, _ := hf.Cfg.Middleware.GetScript()
	return hf.Cfg.Middleware.Binary, script, hf.Cfg.Middleware.Remote
}

func (hf *Hoverfly) SetMiddleware(binary, script, remote string) error {
	newMiddleware := Middleware{}

	if binary == "" && script == "" && remote == "" {
		hf.Cfg.Middleware = newMiddleware
		return nil
	}

	if binary == "" && script != "" {
		return fmt.Errorf("Cannot run script with no binary")
	}

	err := newMiddleware.SetBinary(binary)
	if err != nil {
		return err
	}

	err = newMiddleware.SetScript(script)
	if err != nil {
		return nil
	}

	err = newMiddleware.SetRemote(remote)
	if err != nil {
		return err
	}

	testData := models.RequestResponsePair{
		Request: models.RequestDetails{
			Path:        "/",
			Method:      "GET",
			Destination: "www.test.com",
			Scheme:      "",
			Query:       "",
			Body:        "",
			Headers:     map[string][]string{"test_header": []string{"true"}},
		},
		Response: models.ResponseDetails{
			Status:  200,
			Body:    "ok",
			Headers: map[string][]string{"test_header": []string{"true"}},
		},
	}
	_, err = newMiddleware.Execute(testData)
	if err != nil {
		return err
	}

	hf.Cfg.Middleware = newMiddleware
	return nil
}

func (hf Hoverfly) GetRequestCacheCount() (int, error) {
	return hf.RequestCache.RecordsCount()
}

func (this Hoverfly) GetMetadataCache() cache.Cache {
	return this.MetadataCache
}

func (hf Hoverfly) DeleteRequestCache() error {
	return hf.RequestCache.DeleteData()
}

func (this Hoverfly) GetTemplates() v1.RequestTemplateResponsePairPayload {
	return this.RequestMatcher.TemplateStore.GetPayload()
}

func (this *Hoverfly) ImportTemplates(pairPayload v1.RequestTemplateResponsePairPayload) error {
	return this.RequestMatcher.TemplateStore.ImportPayloads(pairPayload)
}

func (this *Hoverfly) DeleteTemplateCache() {
	this.RequestMatcher.TemplateStore.Wipe()
}

func (hf *Hoverfly) GetResponseDelays() v1.ResponseDelayPayloadView {
	return hf.ResponseDelays.ConvertToResponseDelayPayloadView()
}

func (hf *Hoverfly) SetResponseDelays(payloadView v1.ResponseDelayPayloadView) error {
	err := models.ValidateResponseDelayPayload(payloadView)
	if err != nil {
		return err
	}

	var responseDelays models.ResponseDelayList

	for _, responseDelayView := range payloadView.Data {
		responseDelays = append(responseDelays, models.ResponseDelay{
			UrlPattern: responseDelayView.UrlPattern,
			HttpMethod: responseDelayView.HttpMethod,
			Delay:      responseDelayView.Delay,
		})
	}

	hf.ResponseDelays = &responseDelays
	return nil
}

func (hf *Hoverfly) DeleteResponseDelays() {
	hf.ResponseDelays = &models.ResponseDelayList{}
}

func (hf Hoverfly) GetStats() metrics.Stats {
	return hf.Counter.Flush()
}

func (hf Hoverfly) GetRecords() ([]v1.RequestResponsePairView, error) {
	records, err := hf.RequestCache.GetAllEntries()
	if err != nil {
		return nil, err
	}

	var pairViews []v1.RequestResponsePairView

	for _, v := range records {
		if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil {
			pairView := pair.ConvertToV1RequestResponsePairView()
			pairViews = append(pairViews, *pairView)
		} else {
			log.Error(err)
			return nil, err
		}
	}

	for _, v := range hf.RequestMatcher.TemplateStore {
		pairView := v.ConvertToV1RequestResponsePairView()
		pairViews = append(pairViews, pairView)
	}

	return pairViews, nil

}

func (hf Hoverfly) GetSimulation() (v2.SimulationView, error) {
	records, err := hf.RequestCache.GetAllEntries()
	if err != nil {
		return v2.SimulationView{}, err
	}

	pairViews := make([]v2.RequestResponsePairView, 0)

	for _, v := range records {
		if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil {
			pairView := pair.ConvertToRequestResponsePairView()
			pairViews = append(pairViews, pairView)
		} else {
			log.Error(err)
			return v2.SimulationView{}, err
		}
	}

	for _, v := range hf.Simulation.Templates {
		pairViews = append(pairViews, v.ConvertToRequestResponsePairView())
	}

	responseDelays := hf.ResponseDelays.ConvertToResponseDelayPayloadView()

	return v2.SimulationView{
		MetaView: v2.MetaView{
			HoverflyVersion: hf.version,
			SchemaVersion:   "v1",
			TimeExported:    time.Now().Format(time.RFC3339),
		},
		DataView: v2.DataView{
			RequestResponsePairs: pairViews,
			GlobalActions: v2.GlobalActionsView{
				Delays: responseDelays.Data,
			},
		},
	}, nil
}

func (this *Hoverfly) PutSimulation(simulationView v2.SimulationView) error {
	requestResponsePairViews := make([]interfaces.RequestResponsePair, len(simulationView.RequestResponsePairs))
	for i, v := range simulationView.RequestResponsePairs {
		requestResponsePairViews[i] = v
	}

	err := this.ImportRequestResponsePairViews(requestResponsePairViews)
	if err != nil {
		return err
	}

	err = this.SetResponseDelays(v1.ResponseDelayPayloadView{Data: simulationView.GlobalActions.Delays})
	if err != nil {
		return err
	}

	return nil
}

func (this *Hoverfly) DeleteSimulation() {
	this.DeleteTemplateCache()
	this.DeleteResponseDelays()
	this.DeleteRequestCache()
}

func (this Hoverfly) GetVersion() string {
	return this.version
}

func (this Hoverfly) GetUpstreamProxy() string {
	return this.Cfg.UpstreamProxy
}
<commit_msg>Updated GetRecords() to use new Simulation store<commit_after>package hoverfly

import (
	"fmt"
	"regexp"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/SpectoLabs/hoverfly/core/cache"
	"github.com/SpectoLabs/hoverfly/core/handlers/v1"
	"github.com/SpectoLabs/hoverfly/core/handlers/v2"
	"github.com/SpectoLabs/hoverfly/core/interfaces"
	"github.com/SpectoLabs/hoverfly/core/metrics"
	"github.com/SpectoLabs/hoverfly/core/models"
	"github.com/SpectoLabs/hoverfly/core/modes"
)

func (this Hoverfly) GetDestination() string {
	return this.Cfg.Destination
}

// UpdateDestination - updates proxy with new destination regexp
func (hf *Hoverfly) SetDestination(destination string) (err error) {
	_, err = regexp.Compile(destination)
	if err != nil {
		return fmt.Errorf("destination is not a valid regular expression string")
	}

	hf.mu.Lock()
	hf.StopProxy()
	hf.Cfg.Destination = destination
	err = hf.StartProxy()
	hf.mu.Unlock()
	return
}

func (this Hoverfly) GetMode() string {
	return this.Cfg.Mode
}

func (this *Hoverfly) SetMode(mode string) error {
	availableModes := map[string]bool{
		modes.Simulate:   true,
		modes.Capture:    true,
		modes.Modify:     true,
		modes.Synthesize: true,
	}

	if mode == "" || !availableModes[mode] {
		log.Error("Can't change mode to \"%d\"", mode)
		return fmt.Errorf("Not a valid mode")
	}

	if this.Cfg.Webserver && mode == modes.Capture {
		log.Error("Can't change mode to when configured as a webserver")
		return fmt.Errorf("Can't change mode to capture when configured as a webserver")
	}

	this.Cfg.SetMode(mode)
	return nil
}

func (hf Hoverfly) GetMiddleware() (string, string, string) {
	script, _ := hf.Cfg.Middleware.GetScript()
	return hf.Cfg.Middleware.Binary, script, hf.Cfg.Middleware.Remote
}

func (hf *Hoverfly) SetMiddleware(binary, script, remote string) error {
	newMiddleware := Middleware{}

	if binary == "" && script == "" && remote == "" {
		hf.Cfg.Middleware = newMiddleware
		return nil
	}

	if binary == "" && script != "" {
		return fmt.Errorf("Cannot run script with no binary")
	}

	err := newMiddleware.SetBinary(binary)
	if err != nil {
		return err
	}

	err = newMiddleware.SetScript(script)
	if err != nil {
		return nil
	}

	err = newMiddleware.SetRemote(remote)
	if err != nil {
		return err
	}

	testData := models.RequestResponsePair{
		Request: models.RequestDetails{
			Path:        "/",
			Method:      "GET",
			Destination: "www.test.com",
			Scheme:      "",
			Query:       "",
			Body:        "",
			Headers:     map[string][]string{"test_header": []string{"true"}},
		},
		Response: models.ResponseDetails{
			Status:  200,
			Body:    "ok",
			Headers: map[string][]string{"test_header": []string{"true"}},
		},
	}
	_, err = newMiddleware.Execute(testData)
	if err != nil {
		return err
	}

	hf.Cfg.Middleware = newMiddleware
	return nil
}

func (hf Hoverfly) GetRequestCacheCount() (int, error) {
	return hf.RequestCache.RecordsCount()
}

func (this Hoverfly) GetMetadataCache() cache.Cache {
	return this.MetadataCache
}

func (hf Hoverfly) DeleteRequestCache() error {
	return hf.RequestCache.DeleteData()
}

func (this Hoverfly) GetTemplates() v1.RequestTemplateResponsePairPayload {
	return this.RequestMatcher.TemplateStore.GetPayload()
}

func (this *Hoverfly) ImportTemplates(pairPayload v1.RequestTemplateResponsePairPayload) error {
	return this.RequestMatcher.TemplateStore.ImportPayloads(pairPayload)
}

func (this *Hoverfly) DeleteTemplateCache() {
	this.RequestMatcher.TemplateStore.Wipe()
}

func (hf *Hoverfly) GetResponseDelays() v1.ResponseDelayPayloadView {
	return hf.ResponseDelays.ConvertToResponseDelayPayloadView()
}

func (hf *Hoverfly) SetResponseDelays(payloadView v1.ResponseDelayPayloadView) error {
	err := models.ValidateResponseDelayPayload(payloadView)
	if err != nil {
		return err
	}

	var responseDelays models.ResponseDelayList

	for _, responseDelayView := range payloadView.Data {
		responseDelays = append(responseDelays, models.ResponseDelay{
			UrlPattern: responseDelayView.UrlPattern,
			HttpMethod: responseDelayView.HttpMethod,
			Delay:      responseDelayView.Delay,
		})
	}

	hf.ResponseDelays = &responseDelays
	return nil
}

func (hf *Hoverfly) DeleteResponseDelays() {
	hf.ResponseDelays = &models.ResponseDelayList{}
}

func (hf Hoverfly) GetStats() metrics.Stats {
	return hf.Counter.Flush()
}

func (hf Hoverfly) GetRecords() ([]v1.RequestResponsePairView, error) {
	records, err := hf.RequestCache.GetAllEntries()
	if err != nil {
		return nil, err
	}

	var pairViews []v1.RequestResponsePairView

	for _, v := range records {
		if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil {
			pairView := pair.ConvertToV1RequestResponsePairView()
			pairViews = append(pairViews, *pairView)
		} else {
			log.Error(err)
			return nil, err
		}
	}

	for _, v := range hf.Simulation.Templates {
		pairView := v.ConvertToV1RequestResponsePairView()
		pairViews = append(pairViews, pairView)
	}

	return pairViews, nil

}

func (hf Hoverfly) GetSimulation() (v2.SimulationView, error) {
	records, err := hf.RequestCache.GetAllEntries()
	if err != nil {
		return v2.SimulationView{}, err
	}

	pairViews := make([]v2.RequestResponsePairView, 0)

	for _, v := range records {
		if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil {
			pairView := pair.ConvertToRequestResponsePairView()
			pairViews = append(pairViews, pairView)
		} else {
			log.Error(err)
			return v2.SimulationView{}, err
		}
	}

	for _, v := range hf.Simulation.Templates {
		pairViews = append(pairViews, v.ConvertToRequestResponsePairView())
	}

	responseDelays := hf.ResponseDelays.ConvertToResponseDelayPayloadView()

	return v2.SimulationView{
		MetaView: v2.MetaView{
			HoverflyVersion: hf.version,
			SchemaVersion:   "v1",
			TimeExported:    time.Now().Format(time.RFC3339),
		},
		DataView: v2.DataView{
			RequestResponsePairs: pairViews,
			GlobalActions: v2.GlobalActionsView{
				Delays: responseDelays.Data,
			},
		},
	}, nil
}

func (this *Hoverfly) PutSimulation(simulationView v2.SimulationView) error {
	requestResponsePairViews := make([]interfaces.RequestResponsePair, len(simulationView.RequestResponsePairs))
	for i, v := range simulationView.RequestResponsePairs {
		requestResponsePairViews[i] = v
	}

	err := this.ImportRequestResponsePairViews(requestResponsePairViews)
	if err != nil {
		return err
	}

	err = this.SetResponseDelays(v1.ResponseDelayPayloadView{Data: simulationView.GlobalActions.Delays})
	if err != nil {
		return err
	}

	return nil
}

func (this *Hoverfly) DeleteSimulation() {
	this.DeleteTemplateCache()
	this.DeleteResponseDelays()
	this.DeleteRequestCache()
}

func (this Hoverfly) GetVersion() string {
	return this.version
}

func (this Hoverfly) GetUpstreamProxy() string {
	return this.Cfg.UpstreamProxy
}
<|endoftext|>


<commit_before>package influxql_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/influxdata/influxdb/influxql"
)

func TestQueryManager_AttachQuery(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, _, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	defer qm.KillQuery(qid)

	if qid != 1 {
		t.Errorf("incorrect query id: exp=1 got=%d", qid)
	}
}

func TestQueryManager_KillQuery(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, ch, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	qm.KillQuery(qid)

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Error("detaching the query did not close the channel after 100 milliseconds")
	}

	if err := qm.KillQuery(qid); err == nil || err.Error() != fmt.Sprintf("no such query id: %d", qid) {
		t.Errorf("incorrect error detaching query, got %s", err)
	}
}

func TestQueryManager_Interrupt(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	closing := make(chan struct{})
	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:       q,
		Database:    `mydb`,
		InterruptCh: closing,
	}

	_, ch, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	close(closing)

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Error("interrupting the query did not close the channel after 100 milliseconds")
	}
}

func TestQueryManager_Queries(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, _, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}

	queries := qm.Queries()
	if len(queries) != 1 {
		t.Errorf("expected 1 query, got %d", len(queries))
	} else {
		qi := queries[0]
		if qi.ID != qid {
			t.Errorf("query id: exp=%d got=%d", qid, qi.ID)
		}
		if qi.Query != `SELECT count(value) FROM cpu` {
			t.Errorf("query id: incorrect query string, got '%s'", qi.Query)
		}
		if qi.Database != "mydb" {
			t.Errorf("query id: incorrect database, got %s", qi.Database)
		}
	}

	qm.KillQuery(qid)
	queries = qm.Queries()
	if len(queries) != 0 {
		t.Errorf("expected 0 queries, got %d", len(queries))
	}
}

func TestQueryManager_Limit_Timeout(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
		Timeout:  time.Nanosecond,
	}

	_, ch, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}

	select {
	case <-ch:
	case <-time.After(time.Millisecond):
		t.Errorf("timeout has not killed the query")
	}
}

func TestQueryManager_Limit_ConcurrentQueries(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(1)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, _, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	defer qm.KillQuery(qid)

	_, _, err = qm.AttachQuery(&params)
	if err == nil || err != influxql.ErrMaxConcurrentQueriesReached {
		t.Errorf("unexpected error: %s", err)
	}
}
<commit_msg>Set the query manager timeout test to a higher value to avoid a false positive<commit_after>package influxql_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/influxdata/influxdb/influxql"
)

func TestQueryManager_AttachQuery(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, _, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	defer qm.KillQuery(qid)

	if qid != 1 {
		t.Errorf("incorrect query id: exp=1 got=%d", qid)
	}
}

func TestQueryManager_KillQuery(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, ch, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	qm.KillQuery(qid)

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Error("detaching the query did not close the channel after 100 milliseconds")
	}

	if err := qm.KillQuery(qid); err == nil || err.Error() != fmt.Sprintf("no such query id: %d", qid) {
		t.Errorf("incorrect error detaching query, got %s", err)
	}
}

func TestQueryManager_Interrupt(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	closing := make(chan struct{})
	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:       q,
		Database:    `mydb`,
		InterruptCh: closing,
	}

	_, ch, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	close(closing)

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Error("interrupting the query did not close the channel after 100 milliseconds")
	}
}

func TestQueryManager_Queries(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, _, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}

	queries := qm.Queries()
	if len(queries) != 1 {
		t.Errorf("expected 1 query, got %d", len(queries))
	} else {
		qi := queries[0]
		if qi.ID != qid {
			t.Errorf("query id: exp=%d got=%d", qid, qi.ID)
		}
		if qi.Query != `SELECT count(value) FROM cpu` {
			t.Errorf("query id: incorrect query string, got '%s'", qi.Query)
		}
		if qi.Database != "mydb" {
			t.Errorf("query id: incorrect database, got %s", qi.Database)
		}
	}

	qm.KillQuery(qid)
	queries = qm.Queries()
	if len(queries) != 0 {
		t.Errorf("expected 0 queries, got %d", len(queries))
	}
}

func TestQueryManager_Limit_Timeout(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(0)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
		Timeout:  time.Nanosecond,
	}

	_, ch, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}

	select {
	case <-ch:
	case <-time.After(time.Second):
		t.Errorf("timeout has not killed the query")
	}
}

func TestQueryManager_Limit_ConcurrentQueries(t *testing.T) {
	q, err := influxql.ParseQuery(`SELECT count(value) FROM cpu`)
	if err != nil {
		t.Fatal(err)
	}

	qm := influxql.DefaultQueryManager(1)
	params := influxql.QueryParams{
		Query:    q,
		Database: `mydb`,
	}

	qid, _, err := qm.AttachQuery(&params)
	if err != nil {
		t.Fatal(err)
	}
	defer qm.KillQuery(qid)

	_, _, err = qm.AttachQuery(&params)
	if err == nil || err != influxql.ErrMaxConcurrentQueriesReached {
		t.Errorf("unexpected error: %s", err)
	}
}
<|endoftext|>


<commit_before>package main

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

func getAuthHeader(username, org, passwd string) string {
	return "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s@%s:%s", username, org, passwd)))
}

func main() {
	vappids := []string{
		"2ce84708-cfba-46ad-aab7-f6202dab5d96",
		"07f40418-12a7-4fd9-94f5-fb10079c3025",
		"847bc0e6-524e-47bd-8cf2-50997ea71fcd",
		"68556ef0-8518-4e58-9d4d-6e63eea45bb7"}

	vUrl := "https://tech-cloud.microstrategy.com/api/vApp/vapp-" + vappids[0] + "/leaseSettingsSection/"
	header := http.Header{}
	header.Add("Authorization", getAuthHeader("qye", "technology", "mstr.1984"))
	header.Add("Accept", "application/*+xml;version=1.5")
	req, error := http.NewRequest("GET", vUrl, nil)
	if error != nil {
		panic(error)
	}
	req.Header = header
	res, error := http.DefaultClient.Do(req)
	if error != nil {
		panic(error)
	}

	str, error := ioutil.ReadAll(res.Body)
	if error != nil {
		panic(error)
	}
	fmt.Println(string(str))
}

//r = requests.post('https://tech-cloud.microstrategy.com/api/sessions', verify=False, headers=headers)
//headers = {'x-vcloud-authorization' : r.headers['x-vcloud-authorization'], 'Accept': 'application/*+xml;version=1.5'}
//
//vapp_uuids = [ '2ce84708-cfba-46ad-aab7-f6202dab5d96','07f40418-12a7-4fd9-94f5-fb10079c3025' , '847bc0e6-524e-47bd-8cf2-50997ea71fcd' , '68556ef0-8518-4e58-9d4d-6e63eea45bb7' ]
//
//for vapp_id in vapp_uuids:
//    v_url = 'https://tech-cloud.microstrategy.com/api/vApp/vapp-%s/leaseSettingsSection/' % vapp_id
//
//    r = requests.get(v_url, headers=headers, verify=False);
//    r = requests.put(v_url, headers=headers, verify=False, data=r.text)
//
//    v_url = etree.fromstring(r.text).get('href')
//
//    for i in xrange(60):
//        r = requests.get(v_url, headers=headers, verify=False);
//        if etree.fromstring(r.text).get('status') == 'success':
//            break
//        time.sleep(1)
//
<commit_msg>update<commit_after>package main

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

func getAuthHeader(username, org, passwd string) string {
	return "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s@%s:%s", username, org, passwd)))
}

func main() {
	vappids := []string{
		"2ce84708-cfba-46ad-aab7-f6202dab5d96",
		"07f40418-12a7-4fd9-94f5-fb10079c3025",
		"847bc0e6-524e-47bd-8cf2-50997ea71fcd",
		"68556ef0-8518-4e58-9d4d-6e63eea45bb7"}

	vUrl := "https://tech-cloud.microstrategy.com/api/vApp/vapp-" + vappids[0] + "/leaseSettingsSection/"
	header := http.Header{}
	header.Add("Authorization", getAuthHeader("", "technology", ""))
	header.Add("Accept", "application/*+xml;version=1.5")
	req, error := http.NewRequest("GET", vUrl, nil)
	if error != nil {
		panic(error)
	}
	req.Header = header
	res, error := http.DefaultClient.Do(req)
	if error != nil {
		panic(error)
	}

	str, error := ioutil.ReadAll(res.Body)
	if error != nil {
		panic(error)
	}
	fmt.Println(string(str))
}

//r = requests.post('https://tech-cloud.microstrategy.com/api/sessions', verify=False, headers=headers)
//headers = {'x-vcloud-authorization' : r.headers['x-vcloud-authorization'], 'Accept': 'application/*+xml;version=1.5'}
//
//vapp_uuids = [ '2ce84708-cfba-46ad-aab7-f6202dab5d96','07f40418-12a7-4fd9-94f5-fb10079c3025' , '847bc0e6-524e-47bd-8cf2-50997ea71fcd' , '68556ef0-8518-4e58-9d4d-6e63eea45bb7' ]
//
//for vapp_id in vapp_uuids:
//    v_url = 'https://tech-cloud.microstrategy.com/api/vApp/vapp-%s/leaseSettingsSection/' % vapp_id
//
//    r = requests.get(v_url, headers=headers, verify=False);
//    r = requests.put(v_url, headers=headers, verify=False, data=r.text)
//
//    v_url = etree.fromstring(r.text).get('href')
//
//    for i in xrange(60):
//        r = requests.get(v_url, headers=headers, verify=False);
//        if etree.fromstring(r.text).get('status') == 'success':
//            break
//        time.sleep(1)
//
<|endoftext|>


<commit_before>package main

import (
	"botbox/services/sandbox"
	"github.com/docker/engine-api/client"
	"log"
	"net/http"
)

// TODO: load the docker client settings from ENV variables
const DockerUserAgent = "botbox-game-1.0"
const DockerSocketPath = "unix:///var/run/docker.sock"
const DockerAPIVersion = "v1.23"
const ServerDockerFile = "server-image/"
const ClientDockerFile = "client-image/"

// Request to start a match. To create a listener, provide a cli interface to
// a Docker engine, and the HTTP response writer and reader. To start a match
// send a multipart/form request to the endpoint which contains a "server"
// entry which is a .zip file for the server and a "clients" entry which is
// a list of .zip files for each client.
// TODO: make this a transaction-like approach where if one part of the
// sandbox fails to start, we clean up what we made so there aren't a bunch of
// unused docker networks and containers floating around the host
func matchStarter(cli *client.Client, w http.ResponseWriter, r *http.Request) {
	log.Println("Received request!")
	// parse the request
	request, err := sandbox.FromHttp(r)
	if err != nil {
		log.Println("Error parsing request")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}
	defer request.Close()

	// setup the Sandbox
	netId, serverId, clientIds, err := sandbox.SetupSandbox(cli, request)
	if err != nil {
		log.Println("Error setting up sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	// start the server
	err = sandbox.StartSandbox(cli, serverId, clientIds)
	if err != nil {
		log.Println("Error starting sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	// Wait for the server to close, then destroy the sandbox
	err = sandbox.Wait(cli, serverId)
	if err != nil {
		log.Println("Error waiting for sandbox to close.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	// Log container logs
	err = sandbox.LogSandbox(cli, serverId, clientIds)
	if err != nil {
		log.Println("Error logging sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	err = sandbox.DestroySandbox(cli, netId, append(clientIds, serverId))
	if err != nil {
		log.Println("Error destroying sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}
}

func main() {
	defaultHeaders := map[string]string{"User-Agent": DockerUserAgent}
	cli, err := client.NewClient(DockerSocketPath, DockerAPIVersion, nil, defaultHeaders)
	if err != nil {
		panic(err)
	}
	// build the docker server images when the server starts up
	log.Println("Building botbox-server image")
	r, err := sandbox.BuildImage(cli, ServerDockerFile, sandbox.ServerImageName)
	if err != nil {
		panic(err)
	}
	log.Printf(string(r))
	// build the docker client images when the server starts up
	log.Println("Building botbox-client image")
	r, err = sandbox.BuildImage(cli, ClientDockerFile, sandbox.ClientImageName)
	if err != nil {
		panic(err)
	}
	log.Printf(string(r))

	log.Println("Waiting for connections")

	http.HandleFunc("/start", func(w http.ResponseWriter, r *http.Request) {
		matchStarter(cli, w, r)
	})
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Fix import for sandbox service<commit_after>package main

import (
	"github.com/crestonbunch/botbox/services/sandbox"
	"github.com/docker/engine-api/client"
	"log"
	"net/http"
)

// TODO: load the docker client settings from ENV variables
const DockerUserAgent = "botbox-game-1.0"
const DockerSocketPath = "unix:///var/run/docker.sock"
const DockerAPIVersion = "v1.23"
const ServerDockerFile = "server-image/"
const ClientDockerFile = "client-image/"

// Request to start a match. To create a listener, provide a cli interface to
// a Docker engine, and the HTTP response writer and reader. To start a match
// send a multipart/form request to the endpoint which contains a "server"
// entry which is a .zip file for the server and a "clients" entry which is
// a list of .zip files for each client.
// TODO: make this a transaction-like approach where if one part of the
// sandbox fails to start, we clean up what we made so there aren't a bunch of
// unused docker networks and containers floating around the host
func matchStarter(cli *client.Client, w http.ResponseWriter, r *http.Request) {
	log.Println("Received request!")
	// parse the request
	request, err := sandbox.FromHttp(r)
	if err != nil {
		log.Println("Error parsing request")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}
	defer request.Close()

	// setup the Sandbox
	netId, serverId, clientIds, err := sandbox.SetupSandbox(cli, request)
	if err != nil {
		log.Println("Error setting up sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	// start the server
	err = sandbox.StartSandbox(cli, serverId, clientIds)
	if err != nil {
		log.Println("Error starting sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	// Wait for the server to close, then destroy the sandbox
	err = sandbox.Wait(cli, serverId)
	if err != nil {
		log.Println("Error waiting for sandbox to close.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	// Log container logs
	err = sandbox.LogSandbox(cli, serverId, clientIds)
	if err != nil {
		log.Println("Error logging sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}

	err = sandbox.DestroySandbox(cli, netId, append(clientIds, serverId))
	if err != nil {
		log.Println("Error destroying sandbox.")
		log.Println(err)
		http.Error(w, err.Error(), 400)
		return
	}
}

func main() {
	defaultHeaders := map[string]string{"User-Agent": DockerUserAgent}
	cli, err := client.NewClient(DockerSocketPath, DockerAPIVersion, nil, defaultHeaders)
	if err != nil {
		panic(err)
	}
	// build the docker server images when the server starts up
	log.Println("Building botbox-server image")
	r, err := sandbox.BuildImage(cli, ServerDockerFile, sandbox.ServerImageName)
	if err != nil {
		panic(err)
	}
	log.Printf(string(r))
	// build the docker client images when the server starts up
	log.Println("Building botbox-client image")
	r, err = sandbox.BuildImage(cli, ClientDockerFile, sandbox.ClientImageName)
	if err != nil {
		panic(err)
	}
	log.Printf(string(r))

	log.Println("Waiting for connections")

	http.HandleFunc("/start", func(w http.ResponseWriter, r *http.Request) {
		matchStarter(cli, w, r)
	})
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>//  Copyright (c) 2019 Cisco and/or its affiliates.
//
//  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 e2e

import (
	"bufio"
	"bytes"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"testing"

	. "github.com/onsi/gomega"
)

func TestAgentCtlCommands(t *testing.T) {
	ctx := setupE2E(t)
	defer ctx.teardownE2E()

	var cmd *exec.Cmd
	var err error
	var stdout, stderr bytes.Buffer
	var matched bool

	// file created below is required to test `import` action
	err = createFileWithContent(
		"/tmp/config1",
		`config/vpp/v2/interfaces/tap1 {"name":"tap1", "type":"TAP", "enabled":true, "ip_addresses":["10.10.10.10/24"], "tap":{"version": "2"}}`,
	)
	Expect(err).To(BeNil(), "Failed to create file required by one of the tests")

	tests := []struct {
		name                 string
		cmd                  string
		expectErr            bool
		expectNotEmptyStdout bool
		expectStdout         string
		expectInStdout       string
		expectReStdout       string
		expectInStderr       string
	}{
		{
			name:                 "Check if executable is present",
			cmd:                  "--help",
			expectNotEmptyStdout: true,
		},
		{
			name:           "Test `dump all` action",
			cmd:            "dump all",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump vpp.*` action",
			cmd:            `dump vpp.*`,
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump` action with bad model",
			cmd:            "dump NoSuchModel",
			expectErr:      true,
			expectInStderr: "no models found for [\"NoSuchModel\"]",
		},
		{
			name:           "Test `dump` action with one bad model",
			cmd:            "dump NoSuchModel vpp.interfaces",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump --view=SB` action",
			cmd:            "dump vpp.interfaces --view=SB",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump --view=NB` action",
			cmd:            "dump vpp.interfaces --view=NB",
			expectReStdout: `KEY\s+VALUE\s+ORIGIN\s+METADATA`,
		},
		{
			name:           "Test `dump --view=cached` action",
			cmd:            "dump vpp.interfaces --view=cached",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump` with JSON format",
			cmd:            "dump vpp.interfaces -f=json",
			expectReStdout: `"Value": {\s+"name": "UNTAGGED-local0",`,
		},
		{
			name:           "Test `dump` with YAML format",
			cmd:            "dump vpp.interfaces -f=yaml",
			expectReStdout: `Value:\s+name: UNTAGGED-local0`,
		},

		{
			name:         "Test `dump` with custom format",
			cmd:          `dump vpp.interfaces -f "{{range.}}Name:{{.Value.Name}}{{end}}"`,
			expectStdout: `"Name:UNTAGGED-local0"`,
		},
		{
			name:                 "Test `generate` action",
			cmd:                  "generate vpp.interfaces",
			expectNotEmptyStdout: true,
		},
		{
			name:           "Test `generate` action with not exsiting model",
			cmd:            "generate NoSuchModel",
			expectErr:      true,
			expectInStderr: "no model found for: NoSuchModel",
		},
		{
			name:           "Test `generate` action to yaml",
			cmd:            "generate vpp.interfaces -f=yaml",
			expectInStdout: "type: UNDEFINED_TYPE",
		},
		{
			name:           "Test `generate` action to json",
			cmd:            "generate vpp.interfaces -f=json",
			expectInStdout: `"type": "UNDEFINED_TYPE",`,
		},
		{
			name:           "Test `generate` action to json (oneline)",
			cmd:            "generate vpp.interfaces -f=json --oneline",
			expectInStdout: `{"name":"","type":"UNDEFINED_TYPE",`,
		},
		/*{
			// This test depends on file (/tmp/config1) which was created before.
			name:           "Test `import` action",
			cmd:            "import /tmp/config1 --service-label vpp1",
			expectErr:      true,
			expectInStderr: "connecting to Etcd failed",
		},
		{
			// This test depends on file (/tmp/config1) which was created before.
			name:         "Test `import` action (grpc)",
			cmd:          "import /tmp/config1 --service-label vpp1 --grpc",
			expectStdout: "importing 1 key vals\n - /vnf-agent/vpp1/config/vpp/v2/interfaces/tap1\nsending via gRPC\n",
		},*/
		{
			name:           "Test `kvdb list` action",
			cmd:            "kvdb list",
			expectErr:      true,
			expectInStderr: "connecting to Etcd failed",
		},
		{
			name:           "Test `log list` action",
			cmd:            "log list",
			expectReStdout: `agent\s+info`,
		},
		{
			name:         "Test `log set` action",
			cmd:          "log set agent debug",
			expectStdout: "logger agent has been set to level debug\n",
		},
		{
			// This test depends on previous one.
			name:           "Test `log list` action",
			cmd:            "log list",
			expectReStdout: `agent\s+debug`,
		},
		{
			name:           "Test `model ls` action",
			cmd:            "model ls",
			expectReStdout: `linux.interfaces.interface\s+config\s+ligato.linux.interfaces.Interface`,
		},
		{
			name:           "Test `model inspect` action",
			cmd:            "model inspect vpp.interfaces",
			expectInStdout: `"KeyPrefix": "config/vpp/v2/interfaces/",`,
		},
		{
			name:           "Test `model inspect` action (no models)",
			cmd:            "model inspect NoSuchModel",
			expectErr:      true,
			expectInStderr: "no model found for provided prefix: NoSuchModel",
		},
		{
			name:           "Test `model inspect` action (multiple models)",
			cmd:            "model inspect vpp.",
			expectErr:      true,
			expectInStderr: "multiple models found with provided prefix: vpp.",
		},
		{
			name:           "Test `status` action",
			cmd:            "status",
			expectInStdout: `State: OK`,
		},
		{
			name:         "Test `status` action (with format)",
			cmd:          "status -f {{.AgentStatus.State}}",
			expectStdout: "OK",
		},
		{
			name:           "Test `values` action",
			cmd:            "values",
			expectReStdout: `vpp.interfaces\s+UNTAGGED-local0\s+obtained`,
		},
		/*{
			name:           "Test `values` action (with model)",
			cmd:            "values vpp.proxyarp-global",
			expectReStdout: `vpp.proxyarp-global\s+obtained `,
		},*/
		{
			name:           "Test `vpp info` action",
			cmd:            "vpp info",
			expectReStdout: `Version:\s+v\d{2}\.\d{2}`,
		},
		{
			name:           "Test `vpp cli` action",
			cmd:            "vpp cli sh int",
			expectReStdout: `local0\s+0\s+down\s+0/0/0/0`,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			// Reset both buffers to be empty before test
			stdout.Reset()
			stderr.Reset()

			// Run command
			cmd = exec.Command("/agentctl", strings.Split(test.cmd, " ")...)
			cmd.Stdout = &stdout
			cmd.Stderr = &stderr
			err = cmd.Run()

			if test.expectErr {
				Expect(err).To(Not(BeNil()),
					"Command `%s` should fail\n",
					test.cmd,
				)
			} else {
				Expect(err).To(BeNil(),
					"Command `%s` should not fail. Got err: %v\nStderr:\n%s\n",
					test.cmd, err, stderr.String(),
				)
			}

			// Check STDOUT:
			if test.expectNotEmptyStdout {
				Expect(stdout.Len()).To(Not(BeZero()),
					"Stdout should not be empty\n",
				)
			}

			if test.expectStdout != "" {
				Expect(stdout.String()).To(Equal(test.expectStdout),
					"Want stdout: \n%s\nGot stdout: \n%s\n",
					test.expectStdout, stdout.String(),
				)
			}

			if test.expectInStdout != "" {
				Expect(strings.Contains(stdout.String(), test.expectInStdout)).To(BeTrue(),
					"Want in stdout: \n%s\nGot stdout: \n%s\n",
					test.expectInStdout, stdout.String(),
				)
			}

			if test.expectReStdout != "" {
				matched, err = regexp.MatchString(test.expectReStdout, stdout.String())
				Expect(err).To(BeNil())
				Expect(matched).To(BeTrue(),
					"Want stdout to contain any match of the regular expression: \n`%s`\nGot stdout: \n%s\n",
					test.expectReStdout, stdout.String(),
				)
			}

			// Check STDERR:
			if test.expectInStderr != "" {
				Expect(strings.Contains(stderr.String(), test.expectInStderr)).To(BeTrue(),
					"Want in stderr: \n%s\nGot stderr: \n%s\n",
					test.expectInStderr, stderr.String(),
				)
			}
		})
	}
}

func createFileWithContent(path, content string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(f)
	_, err = w.WriteString(content)
	if err != nil {
		return err
	}
	w.Flush()

	return nil
}

func TestAgentCtlSecureGrpc(t *testing.T) {
	// WARNING: Do not use grpc connection created in `setupE2E` in
	// this test (though I don't know why you would but anyway).
	// By default `grpc.Dail` is non-blocking and connecting happens
	// in the background, so `setupE2E` function does not know about
	// any errors. With securing grpc on the agent (by replacing
	// grpc.conf with grpc-secure.conf) that client won't be able
	// to establish connection because it's not configured for this
	// secure case.

	t.Log("Replacing `GRPC_CONFIG` value with /etc/grpc-secure.conf")
	defer func(oldVal string) {
		t.Logf("Setting `GRPC_CONFIG` back to %q", oldVal)
		os.Setenv("GRPC_CONFIG", oldVal)
	}(os.Getenv("GRPC_CONFIG"))
	os.Setenv("GRPC_CONFIG", "/etc/grpc-secure.conf")

	ctx := setupE2E(t)
	defer ctx.teardownE2E()

	t.Log("Try without any TLS")
	stdout, stderr, err := ctx.execCmd(
		"/agentctl", "--debug", "dump", "vpp.interfaces",
	)
	Expect(err).To(Not(BeNil()), "Should fail\n")
	Expect(strings.Contains(stderr, "rpc error")).To(BeTrue(),
		"Want in stderr: \n\"rpc error\"\nGot stderr: \n%s\n", stderr,
	)
	t.Log("OK")

	t.Log("Try with TLS enabled via flag --tls, but without cert and key (note: server configured to check those files)")
	stdout, stderr, err = ctx.execCmd(
		"/agentctl", "--debug", "--tls", "dump", "vpp.interfaces",
	)
	Expect(err).To(Not(BeNil()), "Should fail\n")
	Expect(strings.Contains(stderr, "rpc error")).To(BeTrue(),
		"Want in stderr: \n\"rpc error\"\nGot stderr: \n%s\n", stderr,
	)
	t.Log("OK")

	t.Log("Try with fully configured TLS via config file")
	stdout, stderr, err = ctx.execCmd(
		"/agentctl", "--debug", "--config-dir=/etc/.agentctl", "dump", "vpp.interfaces",
	)
	Expect(err).To(BeNil(),
		"Should not fail. Got err: %v\nStderr:\n%s\n", err, stderr,
	)
	Expect(len(stdout)).To(Not(BeZero()))
	t.Log("OK")
}
<commit_msg>Refactor e2e test for agentctl commands<commit_after>//  Copyright (c) 2019 Cisco and/or its affiliates.
//
//  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 e2e

import (
	"bufio"
	"os"
	"regexp"
	"strings"
	"testing"

	. "github.com/onsi/gomega"
)

func TestAgentCtlCommands(t *testing.T) {
	ctx := setupE2E(t)
	defer ctx.teardownE2E()

	var err error
	var stdout, stderr string
	var matched bool

	// file created below is required to test `import` action
	err = createFileWithContent(
		"/tmp/config1",
		`config/vpp/v2/interfaces/tap1 {"name":"tap1", "type":"TAP", "enabled":true, "ip_addresses":["10.10.10.10/24"], "tap":{"version": "2"}}`,
	)
	Expect(err).To(BeNil(), "Failed to create file required by one of the tests")

	tests := []struct {
		name                 string
		cmd                  string
		expectErr            bool
		expectNotEmptyStdout bool
		expectStdout         string
		expectInStdout       string
		expectReStdout       string
		expectInStderr       string
	}{
		{
			name:                 "Check if executable is present",
			cmd:                  "--help",
			expectNotEmptyStdout: true,
		},
		{
			name:           "Test `dump all` action",
			cmd:            "dump all",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump vpp.*` action",
			cmd:            `dump vpp.*`,
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump` action with bad model",
			cmd:            "dump NoSuchModel",
			expectErr:      true,
			expectInStderr: "no models found for [\"NoSuchModel\"]",
		},
		{
			name:           "Test `dump` action with one bad model",
			cmd:            "dump NoSuchModel vpp.interfaces",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump --view=SB` action",
			cmd:            "dump vpp.interfaces --view=SB",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump --view=NB` action",
			cmd:            "dump vpp.interfaces --view=NB",
			expectReStdout: `KEY\s+VALUE\s+ORIGIN\s+METADATA`,
		},
		{
			name:           "Test `dump --view=cached` action",
			cmd:            "dump vpp.interfaces --view=cached",
			expectInStdout: "type: SOFTWARE_LOOPBACK",
		},
		{
			name:           "Test `dump` with JSON format",
			cmd:            "dump vpp.interfaces -f=json",
			expectReStdout: `"Value": {\s+"name": "UNTAGGED-local0",`,
		},
		{
			name:           "Test `dump` with YAML format",
			cmd:            "dump vpp.interfaces -f=yaml",
			expectReStdout: `Value:\s+name: UNTAGGED-local0`,
		},

		{
			name:         "Test `dump` with custom format",
			cmd:          `dump vpp.interfaces -f "{{range.}}Name:{{.Value.Name}}{{end}}"`,
			expectStdout: `"Name:UNTAGGED-local0"`,
		},
		{
			name:                 "Test `generate` action",
			cmd:                  "generate vpp.interfaces",
			expectNotEmptyStdout: true,
		},
		{
			name:           "Test `generate` action with not exsiting model",
			cmd:            "generate NoSuchModel",
			expectErr:      true,
			expectInStderr: "no model found for: NoSuchModel",
		},
		{
			name:           "Test `generate` action to yaml",
			cmd:            "generate vpp.interfaces -f=yaml",
			expectInStdout: "type: UNDEFINED_TYPE",
		},
		{
			name:           "Test `generate` action to json",
			cmd:            "generate vpp.interfaces -f=json",
			expectInStdout: `"type": "UNDEFINED_TYPE",`,
		},
		{
			name:           "Test `generate` action to json (oneline)",
			cmd:            "generate vpp.interfaces -f=json --oneline",
			expectInStdout: `{"name":"","type":"UNDEFINED_TYPE",`,
		},
		/*{
			// This test depends on file (/tmp/config1) which was created before.
			name:           "Test `import` action",
			cmd:            "import /tmp/config1 --service-label vpp1",
			expectErr:      true,
			expectInStderr: "connecting to Etcd failed",
		},
		{
			// This test depends on file (/tmp/config1) which was created before.
			name:         "Test `import` action (grpc)",
			cmd:          "import /tmp/config1 --service-label vpp1 --grpc",
			expectStdout: "importing 1 key vals\n - /vnf-agent/vpp1/config/vpp/v2/interfaces/tap1\nsending via gRPC\n",
		},*/
		{
			name:           "Test `kvdb list` action",
			cmd:            "kvdb list",
			expectErr:      true,
			expectInStderr: "connecting to Etcd failed",
		},
		{
			name:           "Test `log list` action",
			cmd:            "log list",
			expectReStdout: `agent\s+info`,
		},
		{
			name:         "Test `log set` action",
			cmd:          "log set agent debug",
			expectStdout: "logger agent has been set to level debug\n",
		},
		{
			// This test depends on previous one.
			name:           "Test `log list` action",
			cmd:            "log list",
			expectReStdout: `agent\s+debug`,
		},
		{
			name:           "Test `model ls` action",
			cmd:            "model ls",
			expectReStdout: `linux.interfaces.interface\s+config\s+ligato.linux.interfaces.Interface`,
		},
		{
			name:           "Test `model inspect` action",
			cmd:            "model inspect vpp.interfaces",
			expectInStdout: `"KeyPrefix": "config/vpp/v2/interfaces/",`,
		},
		{
			name:           "Test `model inspect` action (no models)",
			cmd:            "model inspect NoSuchModel",
			expectErr:      true,
			expectInStderr: "no model found for provided prefix: NoSuchModel",
		},
		{
			name:           "Test `model inspect` action (multiple models)",
			cmd:            "model inspect vpp.",
			expectErr:      true,
			expectInStderr: "multiple models found with provided prefix: vpp.",
		},
		{
			name:           "Test `status` action",
			cmd:            "status",
			expectInStdout: `State: OK`,
		},
		{
			name:         "Test `status` action (with format)",
			cmd:          "status -f {{.AgentStatus.State}}",
			expectStdout: "OK",
		},
		{
			name:           "Test `values` action",
			cmd:            "values",
			expectReStdout: `vpp.interfaces\s+UNTAGGED-local0\s+obtained`,
		},
		/*{
			name:           "Test `values` action (with model)",
			cmd:            "values vpp.proxyarp-global",
			expectReStdout: `vpp.proxyarp-global\s+obtained `,
		},*/
		{
			name:           "Test `vpp info` action",
			cmd:            "vpp info",
			expectReStdout: `Version:\s+v\d{2}\.\d{2}`,
		},
		{
			name:           "Test `vpp cli` action",
			cmd:            "vpp cli sh int",
			expectReStdout: `local0\s+0\s+down\s+0/0/0/0`,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			stdout, stderr, err = ctx.execCmd("/agentctl", strings.Split(test.cmd, " ")...)

			if test.expectErr {
				Expect(err).To(Not(BeNil()),
					"Command `%s` should fail\n",
					test.cmd,
				)
			} else {
				Expect(err).To(BeNil(),
					"Command `%s` should not fail. Got err: %v\nStderr:\n%s\n",
					test.cmd, err, stderr,
				)
			}

			// Check STDOUT:
			if test.expectNotEmptyStdout {
				Expect(len(stdout)).To(Not(BeZero()),
					"Stdout should not be empty\n",
				)
			}

			if test.expectStdout != "" {
				Expect(stdout).To(Equal(test.expectStdout),
					"Want stdout: \n%s\nGot stdout: \n%s\n",
					test.expectStdout, stdout,
				)
			}

			if test.expectInStdout != "" {
				Expect(strings.Contains(stdout, test.expectInStdout)).To(BeTrue(),
					"Want in stdout: \n%s\nGot stdout: \n%s\n",
					test.expectInStdout, stdout,
				)
			}

			if test.expectReStdout != "" {
				matched, err = regexp.MatchString(test.expectReStdout, stdout)
				Expect(err).To(BeNil())
				Expect(matched).To(BeTrue(),
					"Want stdout to contain any match of the regular expression: \n`%s`\nGot stdout: \n%s\n",
					test.expectReStdout, stdout,
				)
			}

			// Check STDERR:
			if test.expectInStderr != "" {
				Expect(strings.Contains(stderr, test.expectInStderr)).To(BeTrue(),
					"Want in stderr: \n%s\nGot stderr: \n%s\n",
					test.expectInStderr, stderr,
				)
			}
		})
	}
}

func createFileWithContent(path, content string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(f)
	_, err = w.WriteString(content)
	if err != nil {
		return err
	}
	w.Flush()

	return nil
}

func TestAgentCtlSecureGrpc(t *testing.T) {
	// WARNING: Do not use grpc connection created in `setupE2E` in
	// this test (though I don't know why you would but anyway).
	// By default `grpc.Dail` is non-blocking and connecting happens
	// in the background, so `setupE2E` function does not know about
	// any errors. With securing grpc on the agent (by replacing
	// grpc.conf with grpc-secure.conf) that client won't be able
	// to establish connection because it's not configured for this
	// secure case.

	t.Log("Replacing `GRPC_CONFIG` value with /etc/grpc-secure.conf")
	defer func(oldVal string) {
		t.Logf("Setting `GRPC_CONFIG` back to %q", oldVal)
		os.Setenv("GRPC_CONFIG", oldVal)
	}(os.Getenv("GRPC_CONFIG"))
	os.Setenv("GRPC_CONFIG", "/etc/grpc-secure.conf")

	ctx := setupE2E(t)
	defer ctx.teardownE2E()

	t.Log("Try without any TLS")
	stdout, stderr, err := ctx.execCmd(
		"/agentctl", "--debug", "dump", "vpp.interfaces",
	)
	Expect(err).To(Not(BeNil()), "Should fail\n")
	Expect(strings.Contains(stderr, "rpc error")).To(BeTrue(),
		"Want in stderr: \n\"rpc error\"\nGot stderr: \n%s\n", stderr,
	)
	t.Log("OK")

	t.Log("Try with TLS enabled via flag --tls, but without cert and key (note: server configured to check those files)")
	stdout, stderr, err = ctx.execCmd(
		"/agentctl", "--debug", "--tls", "dump", "vpp.interfaces",
	)
	Expect(err).To(Not(BeNil()), "Should fail\n")
	Expect(strings.Contains(stderr, "rpc error")).To(BeTrue(),
		"Want in stderr: \n\"rpc error\"\nGot stderr: \n%s\n", stderr,
	)
	t.Log("OK")

	t.Log("Try with fully configured TLS via config file")
	stdout, stderr, err = ctx.execCmd(
		"/agentctl", "--debug", "--config-dir=/etc/.agentctl", "dump", "vpp.interfaces",
	)
	Expect(err).To(BeNil(),
		"Should not fail. Got err: %v\nStderr:\n%s\n", err, stderr,
	)
	Expect(len(stdout)).To(Not(BeZero()))
	t.Log("OK")
}
<|endoftext|>


<commit_before>package crypto

import (
	"errors"

	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/ed25519"
)

const (
	// EntropySize defines the amount of entropy necessary to do secure
	// cryptographic operations, in bytes.
	EntropySize = ed25519.EntropySize

	// PublicKeySize defines the size of public keys in bytes.
	PublicKeySize = ed25519.PublicKeySize

	// SecretKeySize defines the size of secret keys in bytes.
	SecretKeySize = ed25519.SecretKeySize

	// SignatureSize defines the size of signatures in bytes.
	SignatureSize = ed25519.SignatureSize
)

type (
	// PublicKey is an object that can be used to verify signatures.
	PublicKey [PublicKeySize]byte

	// SecretKey can be used to sign data for the corresponding public key.
	SecretKey [SecretKeySize]byte

	// Signature proves that data was signed by the owner of a particular
	// public key's corresponding secret key.
	Signature [SignatureSize]byte
)

var (
	// errInvalidSignature is returned if a signature is provided that does not
	// match the data and public key.
	errInvalidSignature = errors.New("invalid signature")
)

// GenerateKeyPair creates a public-secret keypair that can be used to sign and verify
// messages.
func GenerateKeyPair() (sk SecretKey, pk PublicKey, err error) {
	return stdKeyGen.generate()
}

// GenerateKeyPairDeterministic generates keys deterministically using the input
// entropy. The input entropy must be 32 bytes in length.
func GenerateKeyPairDeterministic(entropy [EntropySize]byte) (SecretKey, PublicKey) {
	return stdKeyGen.generateDeterministic(entropy)
}

// SignHash signs a message using a secret key.
func SignHash(data Hash, sk SecretKey) (sig Signature, err error) {
	skNorm := [SecretKeySize]byte(sk)
	sig = *ed25519.Sign(&skNorm, data[:])
	return sig, nil
}

// VerifyHash uses a public key and input data to verify a signature.
func VerifyHash(data Hash, pk PublicKey, sig Signature) error {
	pkNorm := [PublicKeySize]byte(pk)
	sigNorm := [SignatureSize]byte(sig)
	verifies := ed25519.Verify(&pkNorm, data[:], &sigNorm)
	if !verifies {
		return errInvalidSignature
	}
	return nil
}

// SignObject encodes an object and its signature.
func SignObject(obj interface{}, sk SecretKey) []byte {
	b := encoding.Marshal(obj)
	sig, _ := SignHash(HashBytes(b), sk) // no error possible
	return encoding.MarshalAll(b, sig)
}

// VerifyObject decodes an object and verifies its signature.
func VerifyObject(data []byte, obj interface{}, pk PublicKey) error {
	// split data into encoded object and signature
	var objData []byte
	var sig Signature
	if err := encoding.UnmarshalAll(data, &objData, &sig); err != nil {
		return err
	}
	// verify the signature
	if err := VerifyHash(HashBytes(objData), pk, sig); err != nil {
		return err
	}
	// decode the object
	return encoding.Unmarshal(objData, obj)
}

// PublicKey returns the public key that corresponds to a secret key.
func (sk SecretKey) PublicKey() (pk PublicKey) {
	copy(pk[:], sk[SecretKeySize-PublicKeySize:])
	return
}
<commit_msg>restore Read/WriteSignedObject<commit_after>package crypto

import (
	"bytes"
	"errors"
	"io"

	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/ed25519"
)

const (
	// EntropySize defines the amount of entropy necessary to do secure
	// cryptographic operations, in bytes.
	EntropySize = ed25519.EntropySize

	// PublicKeySize defines the size of public keys in bytes.
	PublicKeySize = ed25519.PublicKeySize

	// SecretKeySize defines the size of secret keys in bytes.
	SecretKeySize = ed25519.SecretKeySize

	// SignatureSize defines the size of signatures in bytes.
	SignatureSize = ed25519.SignatureSize
)

type (
	// PublicKey is an object that can be used to verify signatures.
	PublicKey [PublicKeySize]byte

	// SecretKey can be used to sign data for the corresponding public key.
	SecretKey [SecretKeySize]byte

	// Signature proves that data was signed by the owner of a particular
	// public key's corresponding secret key.
	Signature [SignatureSize]byte
)

var (
	// errInvalidSignature is returned if a signature is provided that does not
	// match the data and public key.
	errInvalidSignature = errors.New("invalid signature")
)

// GenerateKeyPair creates a public-secret keypair that can be used to sign and verify
// messages.
func GenerateKeyPair() (sk SecretKey, pk PublicKey, err error) {
	return stdKeyGen.generate()
}

// GenerateKeyPairDeterministic generates keys deterministically using the input
// entropy. The input entropy must be 32 bytes in length.
func GenerateKeyPairDeterministic(entropy [EntropySize]byte) (SecretKey, PublicKey) {
	return stdKeyGen.generateDeterministic(entropy)
}

// SignHash signs a message using a secret key.
func SignHash(data Hash, sk SecretKey) (sig Signature, err error) {
	skNorm := [SecretKeySize]byte(sk)
	sig = *ed25519.Sign(&skNorm, data[:])
	return sig, nil
}

// VerifyHash uses a public key and input data to verify a signature.
func VerifyHash(data Hash, pk PublicKey, sig Signature) error {
	pkNorm := [PublicKeySize]byte(pk)
	sigNorm := [SignatureSize]byte(sig)
	verifies := ed25519.Verify(&pkNorm, data[:], &sigNorm)
	if !verifies {
		return errInvalidSignature
	}
	return nil
}

// WriteSignedObject writes a length-prefixed object followed by its signature.
func WriteSignedObject(w io.Writer, obj interface{}, sk SecretKey) error {
	encObj := encoding.Marshal(obj)
	sig, _ := SignHash(HashBytes(encObj), sk) // no error possible
	return encoding.NewEncoder(w).EncodeAll(encObj, sig)
}

// ReadSignedObject reads a length-prefixed object followed by its signature,
// and verifies the signature.
func ReadSignedObject(r io.Reader, obj interface{}, maxLen uint64, pk PublicKey) error {
	// read the encoded object and signature
	var encObj []byte
	var sig Signature
	err := encoding.NewDecoder(r).DecodeAll(&encObj, &sig)
	if err != nil {
		return err
	}
	// verify the signature
	if err := VerifyHash(HashBytes(encObj), pk, sig); err != nil {
		return err
	}
	// decode the object
	return encoding.Unmarshal(encObj, obj)
}

// SignObject encodes an object and its signature.
func SignObject(obj interface{}, sk SecretKey) []byte {
	b := new(bytes.Buffer)
	WriteSignedObject(b, obj, sk) // no error possible with bytes.Buffer
	return b.Bytes()
}

// VerifyObject decodes an object and verifies its signature.
func VerifyObject(data []byte, obj interface{}, pk PublicKey) error {
	r := bytes.NewReader(data)
	// since object is already in memory, no need to enforce a maxLen
	return ReadSignedObject(r, obj, ^uint64(0), pk)
}

// PublicKey returns the public key that corresponds to a secret key.
func (sk SecretKey) PublicKey() (pk PublicKey) {
	copy(pk[:], sk[SecretKeySize-PublicKeySize:])
	return
}
<|endoftext|>


<commit_before>package espsdk

import (
	"encoding/json"
	"fmt"
	"time"

	log "github.com/Sirupsen/logrus"
)

// A Contribution is the metadata that represents a media asset from
// a contributor.
type Contribution struct {
	AdditionalFacialExpressions string            `json:"additional_facial_expressions,omitempty"`
	AlternateID                 string            `json:"alternate_id,omitempty"`
	CallForImage                bool              `json:"call_for_image,omitempty"`
	CameraShotDate              string            `json:"camera_shot_date,omitempty"`
	Caption                     string            `json:"caption,omitempty"`
	City                        string            `json:"city,omitempty"`
	CollectionCode              string            `json:"collection_code,omitempty"`
	ContentProviderName         string            `json:"content_provider_name,omitempty"`
	ContentProviderTitle        string            `json:"content_provider_title,omitempty"`
	ContentWarnings             string            `json:"content_warnings,omitempty"`
	Copyright                   string            `json:"copyright,omitempty"`
	CountryOfShoot              string            `json:"country_of_shoot,omitempty"`
	CreatedAt                   *time.Time        `json:"created_at,omitempty"`
	CreatedDate                 *time.Time        `json:"created_date,omitempty"`
	CreditLine                  string            `json:"credit_line,omitempty"`
	Errors                      map[string]string `json:"errors,omitempty"`
	EventID                     string            `json:"event_id,omitempty"`
	ExclusionRoutes             string            `json:"exclusion_routes,omitempty"`
	ExclusiveCoverage           bool              `json:"exclusive_coverage,omitempty"`
	ExternalFileLocation        string            `json:"external_file_location,omitempty"`
	ExtractedMetadataPresent    bool              `json:"extracted_metadata_present,omitempty"`
	FacialExpressions           string            `json:"facial_expressions,omitempty"`
	FileName                    string            `json:"file_name,omitempty"`
	FilePath                    string            `json:"file_path,omitempty"`
	FileUploaded                bool              `json:"file_uploaded,omitempty"`
	FinalBucket                 string            `json:"final_bucket,omitempty"`
	Headline                    string            `json:"headline,omitempty"`
	ID                          int               `json:"id,omitempty"`
	ImageHeight                 int               `json:"image_height,omitempty"`
	ImageWidth                  int               `json:"image_width,omitempty"`
	InactiveDate                string            `json:"inactive_date,omitempty"`
	InclusionRoutes             string            `json:"inclusion_routes,omitempty"`
	IptcCaptionWriter           string            `json:"iptc_caption_writer,omitempty"`
	IptcCategory                string            `json:"iptc_category,omitempty"`
	IptcSubjects                []string          `json:"iptc_subjects,omitempty"`
	Keywords                    []string          `json:"keywords,omitempty"`
	MasterID                    string            `json:"master_id,omitempty"`
	MediaType                   string            `json:"media_type,omitempty"`
	MetadataExtractionStartedAt *time.Time        `json:"metadata_extraction_started_at,omitempty"`
	MetadataExtractionTimeout   bool              `json:"metadata_extraction_timeout,omitempty"`
	MimeType                    string            `json:"mime_type,omitempty"`
	NumberOfPeople              string            `json:"string,omitempty"`
	PaidAssignment              bool              `json:"paid_assignment,omitempty"`
	PaidAssignmentID            string            `json:"paid_assignment_id,omitempty"`
	ParentSource                string            `json:"parent_source,omitempty"`
	PersonCompositions          string            `json:"person_compositions,omitempty"`
	Personalities               []string          `json:"personalities,omitempty"`
	PicscoutSuggestions         string            `json:"picscout_suggestions,omitempty"`
	ProvinceState               string            `json:"province_state,omitempty"`
	PublicistApprovalRequired   bool              `json:"publicist_approval_required,omitempty"`
	PublishedAt                 *time.Time        `json:"published_at,omitempty"`
	PulledReason                string            `json:"pulled_reason,omitempty"`
	Rank                        int               `json:"rank,omitempty"`
	ReadyForSale                bool              `json:"ready_for_sale,omitempty"`
	RecordedDate                string            `json:"recorded_date,omitempty"`
	RiskCategory                string            `json:"risk_category,omitempty"`
	ShotSpeed                   string            `json:"shot_speed,omitempty"`
	SiteDestination             []string          `json:"site_destination,omitempty"`
	Source                      string            `json:"source,omitempty"`
	SpecialInstructions         string            `json:"special_instructions,omitempty"`
	Status                      string            `json:"status,omitempty"`
	StorageURL                  string            `json:"storage_url,omitempty"`
	SubmissionBatchID           int               `json:"submission_batch_id,omitempty"`
	Submittable                 bool              `json:"submittable,omitempty"`
	SubmittedAt                 *time.Time        `json:"submitted_at,omitempty"`
	SubmittedToReviewAt         string            `json:"submitted_to_review_at,omitempty"`
	ThumbnailURL                string            `json:"thumbnail_url,omitempty"`
	UpdatedAt                   *time.Time        `json:"updated_at,omitempty"`
	UploadBucket                string            `json:"upload_bucket,omitempty"`
	UploadID                    string            `json:"upload_id,omitempty"`
	UserMetadataValid           bool              `json:"user_metadata_valid,omitempty"`
	VisualColor                 string            `json:"visual_color,omitempty"`
}

// Index requests a list of all Contributions associated with the specified
// Submission Batch.
func (c Contribution) Index(client *Client, batchID int) ContributionList {
	c.SubmissionBatchID = batchID
	return ContributionList{}.Unmarshal(client.get(c.Path()))
}

// Path returns the path for the contribution.
// If the Contribution has no ID, Path returns the root for all
// contributions for the Batch (the Contribution Index).
func (c Contribution) Path() string {
	bid := c.SubmissionBatchID
	if c.ID == 0 {
		return fmt.Sprintf("%s/%d/contributions", Batches, bid)
	}
	return fmt.Sprintf("%s/%d/contributions/%d", Batches, bid, c.ID)
}

// A ContributionUpdate contains a Contribution. This matches the
// structure of the JSON payload the API expects during a PUT.
type ContributionUpdate struct {
	Contribution Contribution `json:"contribution"`
}

// Marshal serializes a ContributionUpdate into a byte slice.
func (c ContributionUpdate) Marshal() ([]byte, error) { return indentedJSON(c) }

// Path returns the path of the contribution being updated.
func (c ContributionUpdate) Path() string { return c.Contribution.Path() }

// A ContributionList is a slice of zero or more Contributions.
type ContributionList []Contribution

// Unmarshal attempts to deserialize the provided JSON payload
// into the complete metadata returned by a request to the Index (GET all)
// API endpoint.
func (cl ContributionList) Unmarshal(payload []byte) ContributionList {
	var contributionList ContributionList
	if err := json.Unmarshal(payload, &contributionList); err != nil {
		log.Fatal(err)
	}
	return contributionList
}
<commit_msg>Contribution must be Serializable<commit_after>package espsdk

import (
	"encoding/json"
	"fmt"
	"time"

	log "github.com/Sirupsen/logrus"
)

// A Contribution is the metadata that represents a media asset from
// a contributor.
type Contribution struct {
	AdditionalFacialExpressions string            `json:"additional_facial_expressions,omitempty"`
	AlternateID                 string            `json:"alternate_id,omitempty"`
	CallForImage                bool              `json:"call_for_image,omitempty"`
	CameraShotDate              string            `json:"camera_shot_date,omitempty"`
	Caption                     string            `json:"caption,omitempty"`
	City                        string            `json:"city,omitempty"`
	CollectionCode              string            `json:"collection_code,omitempty"`
	ContentProviderName         string            `json:"content_provider_name,omitempty"`
	ContentProviderTitle        string            `json:"content_provider_title,omitempty"`
	ContentWarnings             string            `json:"content_warnings,omitempty"`
	Copyright                   string            `json:"copyright,omitempty"`
	CountryOfShoot              string            `json:"country_of_shoot,omitempty"`
	CreatedAt                   *time.Time        `json:"created_at,omitempty"`
	CreatedDate                 *time.Time        `json:"created_date,omitempty"`
	CreditLine                  string            `json:"credit_line,omitempty"`
	Errors                      map[string]string `json:"errors,omitempty"`
	EventID                     string            `json:"event_id,omitempty"`
	ExclusionRoutes             string            `json:"exclusion_routes,omitempty"`
	ExclusiveCoverage           bool              `json:"exclusive_coverage,omitempty"`
	ExternalFileLocation        string            `json:"external_file_location,omitempty"`
	ExtractedMetadataPresent    bool              `json:"extracted_metadata_present,omitempty"`
	FacialExpressions           string            `json:"facial_expressions,omitempty"`
	FileName                    string            `json:"file_name,omitempty"`
	FilePath                    string            `json:"file_path,omitempty"`
	FileUploaded                bool              `json:"file_uploaded,omitempty"`
	FinalBucket                 string            `json:"final_bucket,omitempty"`
	Headline                    string            `json:"headline,omitempty"`
	ID                          int               `json:"id,omitempty"`
	ImageHeight                 int               `json:"image_height,omitempty"`
	ImageWidth                  int               `json:"image_width,omitempty"`
	InactiveDate                string            `json:"inactive_date,omitempty"`
	InclusionRoutes             string            `json:"inclusion_routes,omitempty"`
	IptcCaptionWriter           string            `json:"iptc_caption_writer,omitempty"`
	IptcCategory                string            `json:"iptc_category,omitempty"`
	IptcSubjects                []string          `json:"iptc_subjects,omitempty"`
	Keywords                    []string          `json:"keywords,omitempty"`
	MasterID                    string            `json:"master_id,omitempty"`
	MediaType                   string            `json:"media_type,omitempty"`
	MetadataExtractionStartedAt *time.Time        `json:"metadata_extraction_started_at,omitempty"`
	MetadataExtractionTimeout   bool              `json:"metadata_extraction_timeout,omitempty"`
	MimeType                    string            `json:"mime_type,omitempty"`
	NumberOfPeople              string            `json:"string,omitempty"`
	PaidAssignment              bool              `json:"paid_assignment,omitempty"`
	PaidAssignmentID            string            `json:"paid_assignment_id,omitempty"`
	ParentSource                string            `json:"parent_source,omitempty"`
	PersonCompositions          string            `json:"person_compositions,omitempty"`
	Personalities               []string          `json:"personalities,omitempty"`
	PicscoutSuggestions         string            `json:"picscout_suggestions,omitempty"`
	ProvinceState               string            `json:"province_state,omitempty"`
	PublicistApprovalRequired   bool              `json:"publicist_approval_required,omitempty"`
	PublishedAt                 *time.Time        `json:"published_at,omitempty"`
	PulledReason                string            `json:"pulled_reason,omitempty"`
	Rank                        int               `json:"rank,omitempty"`
	ReadyForSale                bool              `json:"ready_for_sale,omitempty"`
	RecordedDate                string            `json:"recorded_date,omitempty"`
	RiskCategory                string            `json:"risk_category,omitempty"`
	ShotSpeed                   string            `json:"shot_speed,omitempty"`
	SiteDestination             []string          `json:"site_destination,omitempty"`
	Source                      string            `json:"source,omitempty"`
	SpecialInstructions         string            `json:"special_instructions,omitempty"`
	Status                      string            `json:"status,omitempty"`
	StorageURL                  string            `json:"storage_url,omitempty"`
	SubmissionBatchID           int               `json:"submission_batch_id,omitempty"`
	Submittable                 bool              `json:"submittable,omitempty"`
	SubmittedAt                 *time.Time        `json:"submitted_at,omitempty"`
	SubmittedToReviewAt         string            `json:"submitted_to_review_at,omitempty"`
	ThumbnailURL                string            `json:"thumbnail_url,omitempty"`
	UpdatedAt                   *time.Time        `json:"updated_at,omitempty"`
	UploadBucket                string            `json:"upload_bucket,omitempty"`
	UploadID                    string            `json:"upload_id,omitempty"`
	UserMetadataValid           bool              `json:"user_metadata_valid,omitempty"`
	VisualColor                 string            `json:"visual_color,omitempty"`
}

// Index requests a list of all Contributions associated with the specified
// Submission Batch.
func (c Contribution) Index(client *Client, batchID int) ContributionList {
	c.SubmissionBatchID = batchID
	return ContributionList{}.Unmarshal(client.get(c.Path()))
}

// Path returns the path for the contribution.
// If the Contribution has no ID, Path returns the root for all
// contributions for the Batch (the Contribution Index).
func (c Contribution) Path() string {
	bid := c.SubmissionBatchID
	if c.ID == 0 {
		return fmt.Sprintf("%s/%d/contributions", Batches, bid)
	}
	return fmt.Sprintf("%s/%d/contributions/%d", Batches, bid, c.ID)
}

// Marshal serializes the Contribution into a byte slice.
func (c Contribution) Marshal() ([]byte, error) { return indentedJSON(c) }

// A ContributionUpdate contains a Contribution. This matches the
// structure of the JSON payload the API expects during a PUT.
type ContributionUpdate struct {
	Contribution Contribution `json:"contribution"`
}

// Marshal serializes a ContributionUpdate into a byte slice.
func (c ContributionUpdate) Marshal() ([]byte, error) { return indentedJSON(c) }

// Path returns the path of the contribution being updated.
func (c ContributionUpdate) Path() string { return c.Contribution.Path() }

// A ContributionList is a slice of zero or more Contributions.
type ContributionList []Contribution

// Unmarshal attempts to deserialize the provided JSON payload
// into the complete metadata returned by a request to the Index (GET all)
// API endpoint.
func (cl ContributionList) Unmarshal(payload []byte) ContributionList {
	var contributionList ContributionList
	if err := json.Unmarshal(payload, &contributionList); err != nil {
		log.Fatal(err)
	}
	return contributionList
}
<|endoftext|>