<commit_before>package db

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gocraft/dbr"
)

const (
	DefaultSelectLimit = 200
)

func GetLimit(n uint64) uint64 {
	if n < 0 {
		n = 0
	}
	if n > DefaultSelectLimit {
		n = DefaultSelectLimit
	}
	return n
}

func GetOffset(n uint64) uint64 {
	if n < 0 {
		n = 0
	}
	return n
}

type Database struct {
	*dbr.Session
}

type SelectQuery struct {
	*dbr.SelectBuilder
}

type InsertQuery struct {
	*dbr.InsertBuilder
}

type DeleteQuery struct {
	*dbr.DeleteBuilder
}

type UpdateQuery struct {
	*dbr.UpdateBuilder
}

// SelectQuery
// Example: Select().From().Where().Limit().Offset().OrderDir().Load()
//          Select().From().Where().Limit().Offset().OrderDir().LoadOne()
//          Select().From().Where().Count()
//          SelectAll().From().Where().Limit().Offset().OrderDir().Load()
//          SelectAll().From().Where().Limit().Offset().OrderDir().LoadOne()
//          SelectAll().From().Where().Count()

func (db *Database) Select(columns ...string) *SelectQuery {
	return &SelectQuery{db.Session.Select(columns...)}
}

func (db *Database) SelectAll(columns ...string) *SelectQuery {
	return &SelectQuery{db.Session.Select("*")}
}

func (b *SelectQuery) From(table string) *SelectQuery {
	b.SelectBuilder.From(table)
	return b
}

func (b *SelectQuery) Where(query interface{}, value ...interface{}) *SelectQuery {
	b.SelectBuilder.Where(query, value...)
	return b
}

func (b *SelectQuery) Limit(n uint64) *SelectQuery {
	n = GetLimit(n)
	b.SelectBuilder.Limit(n)
	return b
}

func (b *SelectQuery) Offset(n uint64) *SelectQuery {
	n = GetLimit(n)
	b.SelectBuilder.Offset(n)
	return b
}

func (b *SelectQuery) OrderDir(col string, isAsc bool) *SelectQuery {
	b.SelectBuilder.OrderDir(col, isAsc)
	return b
}

func (b *SelectQuery) Load(value interface{}) (int, error) {
	return b.SelectBuilder.Load(value)
}

func (b *SelectQuery) LoadOne(value interface{}) error {
	return b.SelectBuilder.LoadOne(value)
}

func (b *SelectQuery) Count() (count uint32, err error) {
	// cache SelectStmt
	selectStmt := b.SelectStmt
	b.SelectStmt = dbr.Select("count(*)").From(selectStmt.Table)
	for _, cond := range selectStmt.WhereCond {
		b.Where(cond)
	}
	err = b.LoadOne(&count)
	// fallback SelectStmt
	b.SelectStmt = selectStmt
	return
}

// InsertQuery
// Example: InsertInto().Columns().Record().Exec()

func (db *Database) InsertInto(table string) *InsertQuery {
	return &InsertQuery{db.Session.InsertInto(table)}
}

func (b *InsertQuery) Exec() (sql.Result, error) {
	return b.InsertBuilder.Exec()
}

func (b *InsertQuery) Columns(columns ...string) *InsertQuery {
	b.InsertBuilder.Columns(columns...)
	return b
}

func (b *InsertQuery) Record(structValue interface{}) *InsertQuery {
	b.InsertBuilder.Record(structValue)
	return b
}

// DeleteQuery
// Example: DeleteFrom().Where().Limit().Exec()

func (db *Database) DeleteFrom(table string) *DeleteQuery {
	return &DeleteQuery{db.Session.DeleteFrom(table)}
}

func (b *DeleteQuery) Where(query interface{}, value ...interface{}) *DeleteQuery {
	b.DeleteBuilder.Where(query, value...)
	return b
}

func (b *DeleteQuery) Limit(n uint64) *DeleteQuery {
	b.DeleteBuilder.Limit(n)
	return b
}

func (b *DeleteQuery) Exec() (sql.Result, error) {
	return b.DeleteBuilder.Exec()
}

// UpdateQuery
// Example: Update().Set().Where().Exec()

func (db *Database) Update(table string) *UpdateQuery {
	return &UpdateQuery{db.Session.Update(table)}
}

func (b *UpdateQuery) Exec() (sql.Result, error) {
	return b.UpdateBuilder.Exec()
}

func (b *UpdateQuery) Set(column string, value interface{}) *UpdateQuery {
	b.UpdateBuilder.Set(column, value)
	return b
}

func (b *UpdateQuery) SetMap(m map[string]interface{}) *UpdateQuery {
	b.UpdateBuilder.SetMap(m)
	return b
}

func (b *UpdateQuery) Where(query interface{}, value ...interface{}) *UpdateQuery {
	b.UpdateBuilder.Where(query, value...)
	return b
}

func (b *UpdateQuery) Limit(n uint64) *UpdateQuery {
	b.UpdateBuilder.Limit(n)
	return b
}
<commit_msg>add db selectbysql func<commit_after>package db

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gocraft/dbr"
)

const (
	DefaultSelectLimit = 200
)

func GetLimit(n uint64) uint64 {
	if n < 0 {
		n = 0
	}
	if n > DefaultSelectLimit {
		n = DefaultSelectLimit
	}
	return n
}

func GetOffset(n uint64) uint64 {
	if n < 0 {
		n = 0
	}
	return n
}

type Database struct {
	*dbr.Session
}

type SelectQuery struct {
	*dbr.SelectBuilder
}

type InsertQuery struct {
	*dbr.InsertBuilder
}

type DeleteQuery struct {
	*dbr.DeleteBuilder
}

type UpdateQuery struct {
	*dbr.UpdateBuilder
}

// SelectQuery
// Example: Select().From().Where().Limit().Offset().OrderDir().Load()
//          Select().From().Where().Limit().Offset().OrderDir().LoadOne()
//          Select().From().Where().Count()
//          SelectAll().From().Where().Limit().Offset().OrderDir().Load()
//          SelectAll().From().Where().Limit().Offset().OrderDir().LoadOne()
//          SelectAll().From().Where().Count()

func (db *Database) Select(columns ...string) *SelectQuery {
	return &SelectQuery{db.Session.Select(columns...)}
}

func (db *Database) SelectBySql(query string, value ...interface{}) *SelectQuery {
	return &SelectQuery{db.Session.SelectBySql(query, value...)}
}

func (db *Database) SelectAll(columns ...string) *SelectQuery {
	return &SelectQuery{db.Session.Select("*")}
}

func (b *SelectQuery) From(table string) *SelectQuery {
	b.SelectBuilder.From(table)
	return b
}

func (b *SelectQuery) Where(query interface{}, value ...interface{}) *SelectQuery {
	b.SelectBuilder.Where(query, value...)
	return b
}

func (b *SelectQuery) Limit(n uint64) *SelectQuery {
	n = GetLimit(n)
	b.SelectBuilder.Limit(n)
	return b
}

func (b *SelectQuery) Offset(n uint64) *SelectQuery {
	n = GetLimit(n)
	b.SelectBuilder.Offset(n)
	return b
}

func (b *SelectQuery) OrderDir(col string, isAsc bool) *SelectQuery {
	b.SelectBuilder.OrderDir(col, isAsc)
	return b
}

func (b *SelectQuery) Load(value interface{}) (int, error) {
	return b.SelectBuilder.Load(value)
}

func (b *SelectQuery) LoadOne(value interface{}) error {
	return b.SelectBuilder.LoadOne(value)
}

func (b *SelectQuery) Count() (count uint32, err error) {
	// cache SelectStmt
	selectStmt := b.SelectStmt
	b.SelectStmt = dbr.Select("count(*)").From(selectStmt.Table)
	for _, cond := range selectStmt.WhereCond {
		b.Where(cond)
	}
	err = b.LoadOne(&count)
	// fallback SelectStmt
	b.SelectStmt = selectStmt
	return
}

// InsertQuery
// Example: InsertInto().Columns().Record().Exec()

func (db *Database) InsertInto(table string) *InsertQuery {
	return &InsertQuery{db.Session.InsertInto(table)}
}

func (b *InsertQuery) Exec() (sql.Result, error) {
	return b.InsertBuilder.Exec()
}

func (b *InsertQuery) Columns(columns ...string) *InsertQuery {
	b.InsertBuilder.Columns(columns...)
	return b
}

func (b *InsertQuery) Record(structValue interface{}) *InsertQuery {
	b.InsertBuilder.Record(structValue)
	return b
}

// DeleteQuery
// Example: DeleteFrom().Where().Limit().Exec()

func (db *Database) DeleteFrom(table string) *DeleteQuery {
	return &DeleteQuery{db.Session.DeleteFrom(table)}
}

func (b *DeleteQuery) Where(query interface{}, value ...interface{}) *DeleteQuery {
	b.DeleteBuilder.Where(query, value...)
	return b
}

func (b *DeleteQuery) Limit(n uint64) *DeleteQuery {
	b.DeleteBuilder.Limit(n)
	return b
}

func (b *DeleteQuery) Exec() (sql.Result, error) {
	return b.DeleteBuilder.Exec()
}

// UpdateQuery
// Example: Update().Set().Where().Exec()

func (db *Database) Update(table string) *UpdateQuery {
	return &UpdateQuery{db.Session.Update(table)}
}

func (b *UpdateQuery) Exec() (sql.Result, error) {
	return b.UpdateBuilder.Exec()
}

func (b *UpdateQuery) Set(column string, value interface{}) *UpdateQuery {
	b.UpdateBuilder.Set(column, value)
	return b
}

func (b *UpdateQuery) SetMap(m map[string]interface{}) *UpdateQuery {
	b.UpdateBuilder.SetMap(m)
	return b
}

func (b *UpdateQuery) Where(query interface{}, value ...interface{}) *UpdateQuery {
	b.UpdateBuilder.Where(query, value...)
	return b
}

func (b *UpdateQuery) Limit(n uint64) *UpdateQuery {
	b.UpdateBuilder.Limit(n)
	return b
}
<|endoftext|>


<commit_before>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"bytes"
	"fmt"
	"net/url"
)

// RepositoriesService handles communication with the repositories related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type RepositoriesService struct {
	client *Client
}

// TreeNode represents a GitLab repository file or directory.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type TreeNode struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
	Path string `json:"path"`
	Mode string `json:"mode"`
}

func (t TreeNode) String() string {
	return Stringify(t)
}

// ListTreeOptions represents the available ListTree() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
type ListTreeOptions struct {
	ListOptions
	Path      *string `url:"path,omitempty" json:"path,omitempty"`
	Ref       *string `url:"ref,omitempty" json:"ref,omitempty"`
	Recursive *bool   `url:"recursive,omitempty" json:"recursive,omitempty"`
}

// ListTree gets a list of repository files and directories in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...OptionFunc) ([]*TreeNode, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/tree", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var t []*TreeNode
	resp, err := s.client.Do(req, &t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, err
}

// RawFileContent gets the raw file contents for a file by commit SHA and path
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#raw-file-content
func (s *RepositoriesService) RawFileContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s", url.QueryEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// RawBlobContent gets the raw file contents for a blob by blob SHA.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#raw-blob-content
func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", url.QueryEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// ArchiveOptions represents the available Archive() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
type ArchiveOptions struct {
	SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
}

// Archive gets an archive of the repository.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/archive", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// Compare represents the result of a comparison of branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type Compare struct {
	Commit         *Commit   `json:"commit"`
	Commits        []*Commit `json:"commits"`
	Diffs          []*Diff   `json:"diffs"`
	CompareTimeout bool      `json:"compare_timeout"`
	CompareSameRef bool      `json:"compare_same_ref"`
}

func (c Compare) String() string {
	return Stringify(c)
}

// CompareOptions represents the available Compare() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type CompareOptions struct {
	From     *string `url:"from,omitempty" json:"from,omitempty"`
	To       *string `url:"to,omitempty" json:"to,omitempty"`
	Straight *bool   `url:"straight,omitempty" json:"straight,omitempty"`
}

// Compare compares branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...OptionFunc) (*Compare, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/compare", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Compare)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// Contributor represents a GitLap contributor.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type Contributor struct {
	Name      string `json:"name,omitempty"`
	Email     string `json:"email,omitempty"`
	Commits   int    `json:"commits,omitempty"`
	Additions int    `json:"additions,omitempty"`
	Deletions int    `json:"deletions,omitempty"`
}

func (c Contributor) String() string {
	return Stringify(c)
}

// ListContributorsOptions represents the available ListContributors() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type ListContributorsOptions ListOptions

// Contributors gets the repository contributors list.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...OptionFunc) ([]*Contributor, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/contributors", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var c []*Contributor
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// MergeBaseOptions represents the available MergeBase() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
type MergeBaseOptions struct {
	Ref []string `url:"refs[],omitempty" json:"refs,omitempty"`
}

// MergeBase gets the common ancestor for 2 refs (commit SHAs, branch
// names or tags).
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
func (s *RepositoriesService) MergeBase(pid interface{}, opt *MergeBaseOptions, options ...OptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merge_base", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}
<commit_msg>fix godoc for RepositoriesService.RawFileContent (#527)<commit_after>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"bytes"
	"fmt"
	"net/url"
)

// RepositoriesService handles communication with the repositories related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type RepositoriesService struct {
	client *Client
}

// TreeNode represents a GitLab repository file or directory.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type TreeNode struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
	Path string `json:"path"`
	Mode string `json:"mode"`
}

func (t TreeNode) String() string {
	return Stringify(t)
}

// ListTreeOptions represents the available ListTree() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
type ListTreeOptions struct {
	ListOptions
	Path      *string `url:"path,omitempty" json:"path,omitempty"`
	Ref       *string `url:"ref,omitempty" json:"ref,omitempty"`
	Recursive *bool   `url:"recursive,omitempty" json:"recursive,omitempty"`
}

// ListTree gets a list of repository files and directories in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...OptionFunc) ([]*TreeNode, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/tree", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var t []*TreeNode
	resp, err := s.client.Do(req, &t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, err
}

// GetBlob gets information about blob in repository like size and content.
// Note that blob content is Base64 encoded.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-a-blob-from-repository
func (s *RepositoriesService) GetBlob(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s", url.QueryEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// RawBlobContent gets the raw file contents for a blob by blob SHA.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#raw-blob-content
func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", url.QueryEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// ArchiveOptions represents the available Archive() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
type ArchiveOptions struct {
	SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
}

// Archive gets an archive of the repository.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/archive", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// Compare represents the result of a comparison of branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type Compare struct {
	Commit         *Commit   `json:"commit"`
	Commits        []*Commit `json:"commits"`
	Diffs          []*Diff   `json:"diffs"`
	CompareTimeout bool      `json:"compare_timeout"`
	CompareSameRef bool      `json:"compare_same_ref"`
}

func (c Compare) String() string {
	return Stringify(c)
}

// CompareOptions represents the available Compare() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type CompareOptions struct {
	From     *string `url:"from,omitempty" json:"from,omitempty"`
	To       *string `url:"to,omitempty" json:"to,omitempty"`
	Straight *bool   `url:"straight,omitempty" json:"straight,omitempty"`
}

// Compare compares branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...OptionFunc) (*Compare, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/compare", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Compare)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// Contributor represents a GitLap contributor.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type Contributor struct {
	Name      string `json:"name,omitempty"`
	Email     string `json:"email,omitempty"`
	Commits   int    `json:"commits,omitempty"`
	Additions int    `json:"additions,omitempty"`
	Deletions int    `json:"deletions,omitempty"`
}

func (c Contributor) String() string {
	return Stringify(c)
}

// ListContributorsOptions represents the available ListContributors() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type ListContributorsOptions ListOptions

// Contributors gets the repository contributors list.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...OptionFunc) ([]*Contributor, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/contributors", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var c []*Contributor
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// MergeBaseOptions represents the available MergeBase() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
type MergeBaseOptions struct {
	Ref []string `url:"refs[],omitempty" json:"refs,omitempty"`
}

// MergeBase gets the common ancestor for 2 refs (commit SHAs, branch
// names or tags).
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
func (s *RepositoriesService) MergeBase(pid interface{}, opt *MergeBaseOptions, options ...OptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merge_base", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

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


<commit_before>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"bytes"
	"fmt"
	"io"
)

// RepositoriesService handles communication with the repositories related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type RepositoriesService struct {
	client *Client
}

// TreeNode represents a GitLab repository file or directory.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type TreeNode struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
	Path string `json:"path"`
	Mode string `json:"mode"`
}

func (t TreeNode) String() string {
	return Stringify(t)
}

// ListTreeOptions represents the available ListTree() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
type ListTreeOptions struct {
	ListOptions
	Path      *string `url:"path,omitempty" json:"path,omitempty"`
	Ref       *string `url:"ref,omitempty" json:"ref,omitempty"`
	Recursive *bool   `url:"recursive,omitempty" json:"recursive,omitempty"`
}

// ListTree gets a list of repository files and directories in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...OptionFunc) ([]*TreeNode, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/tree", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var t []*TreeNode
	resp, err := s.client.Do(req, &t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, err
}

// Blob gets information about blob in repository like size and content. Note
// that blob content is Base64 encoded.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-a-blob-from-repository
func (s *RepositoriesService) Blob(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s", pathEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// RawBlobContent gets the raw file contents for a blob by blob SHA.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#raw-blob-content
func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", pathEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// ArchiveOptions represents the available Archive() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
type ArchiveOptions struct {
	Format *string    `url:"-" json:"-"`
	SHA    *string    `url:"sha,omitempty" json:"sha,omitempty"`
	Writer *io.Writer `url:"-" json:"-"`
}

// Archive gets an archive of the repository.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/archive", pathEscape(project))

	// Set an optional format for the archive.
	if opt != nil && opt.Format != nil {
		u = fmt.Sprintf("%s.%s", u, *opt.Format)
	}

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	var resp *Response
	if opt != nil && opt.Writer != nil {
		resp, err = s.client.Do(req, opt.Writer)
	} else {
		resp, err = s.client.Do(req, &b)
	}

	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// Compare represents the result of a comparison of branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type Compare struct {
	Commit         *Commit   `json:"commit"`
	Commits        []*Commit `json:"commits"`
	Diffs          []*Diff   `json:"diffs"`
	CompareTimeout bool      `json:"compare_timeout"`
	CompareSameRef bool      `json:"compare_same_ref"`
}

func (c Compare) String() string {
	return Stringify(c)
}

// CompareOptions represents the available Compare() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type CompareOptions struct {
	From     *string `url:"from,omitempty" json:"from,omitempty"`
	To       *string `url:"to,omitempty" json:"to,omitempty"`
	Straight *bool   `url:"straight,omitempty" json:"straight,omitempty"`
}

// Compare compares branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...OptionFunc) (*Compare, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/compare", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Compare)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// Contributor represents a GitLap contributor.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type Contributor struct {
	Name      string `json:"name,omitempty"`
	Email     string `json:"email,omitempty"`
	Commits   int    `json:"commits,omitempty"`
	Additions int    `json:"additions,omitempty"`
	Deletions int    `json:"deletions,omitempty"`
}

func (c Contributor) String() string {
	return Stringify(c)
}

// ListContributorsOptions represents the available ListContributors() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type ListContributorsOptions ListOptions

// Contributors gets the repository contributors list.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...OptionFunc) ([]*Contributor, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/contributors", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var c []*Contributor
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// MergeBaseOptions represents the available MergeBase() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
type MergeBaseOptions struct {
	Ref []string `url:"refs[],omitempty" json:"refs,omitempty"`
}

// MergeBase gets the common ancestor for 2 refs (commit SHAs, branch
// names or tags).
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
func (s *RepositoriesService) MergeBase(pid interface{}, opt *MergeBaseOptions, options ...OptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merge_base", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}
<commit_msg>Create a dedicated funtion for streaming an archive<commit_after>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"bytes"
	"fmt"
	"io"
)

// RepositoriesService handles communication with the repositories related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type RepositoriesService struct {
	client *Client
}

// TreeNode represents a GitLab repository file or directory.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
type TreeNode struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
	Path string `json:"path"`
	Mode string `json:"mode"`
}

func (t TreeNode) String() string {
	return Stringify(t)
}

// ListTreeOptions represents the available ListTree() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
type ListTreeOptions struct {
	ListOptions
	Path      *string `url:"path,omitempty" json:"path,omitempty"`
	Ref       *string `url:"ref,omitempty" json:"ref,omitempty"`
	Recursive *bool   `url:"recursive,omitempty" json:"recursive,omitempty"`
}

// ListTree gets a list of repository files and directories in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...OptionFunc) ([]*TreeNode, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/tree", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var t []*TreeNode
	resp, err := s.client.Do(req, &t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, err
}

// Blob gets information about blob in repository like size and content. Note
// that blob content is Base64 encoded.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-a-blob-from-repository
func (s *RepositoriesService) Blob(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s", pathEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// RawBlobContent gets the raw file contents for a blob by blob SHA.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#raw-blob-content
func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", pathEscape(project), sha)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// ArchiveOptions represents the available Archive() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
type ArchiveOptions struct {
	Format *string `url:"-" json:"-"`
	SHA    *string `url:"sha,omitempty" json:"sha,omitempty"`
}

// Archive gets an archive of the repository.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...OptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/archive", pathEscape(project))

	// Set an optional format for the archive.
	if opt != nil && opt.Format != nil {
		u = fmt.Sprintf("%s.%s", u, *opt.Format)
	}

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var b bytes.Buffer
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b.Bytes(), resp, err
}

// StreamArchive streams an archive of the repository to the provided
// io.Writer.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
func (s *RepositoriesService) StreamArchive(pid interface{}, w io.Writer, opt *ArchiveOptions, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/archive", pathEscape(project))

	// Set an optional format for the archive.
	if opt != nil && opt.Format != nil {
		u = fmt.Sprintf("%s.%s", u, *opt.Format)
	}

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, w)
}

// Compare represents the result of a comparison of branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type Compare struct {
	Commit         *Commit   `json:"commit"`
	Commits        []*Commit `json:"commits"`
	Diffs          []*Diff   `json:"diffs"`
	CompareTimeout bool      `json:"compare_timeout"`
	CompareSameRef bool      `json:"compare_same_ref"`
}

func (c Compare) String() string {
	return Stringify(c)
}

// CompareOptions represents the available Compare() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
type CompareOptions struct {
	From     *string `url:"from,omitempty" json:"from,omitempty"`
	To       *string `url:"to,omitempty" json:"to,omitempty"`
	Straight *bool   `url:"straight,omitempty" json:"straight,omitempty"`
}

// Compare compares branches, tags or commits.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...OptionFunc) (*Compare, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/compare", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Compare)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// Contributor represents a GitLap contributor.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type Contributor struct {
	Name      string `json:"name,omitempty"`
	Email     string `json:"email,omitempty"`
	Commits   int    `json:"commits,omitempty"`
	Additions int    `json:"additions,omitempty"`
	Deletions int    `json:"deletions,omitempty"`
}

func (c Contributor) String() string {
	return Stringify(c)
}

// ListContributorsOptions represents the available ListContributors() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
type ListContributorsOptions ListOptions

// Contributors gets the repository contributors list.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...OptionFunc) ([]*Contributor, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/contributors", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var c []*Contributor
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, err
}

// MergeBaseOptions represents the available MergeBase() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
type MergeBaseOptions struct {
	Ref []string `url:"refs[],omitempty" json:"refs,omitempty"`
}

// MergeBase gets the common ancestor for 2 refs (commit SHAs, branch
// names or tags).
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/repositories.html#merge-base
func (s *RepositoriesService) MergeBase(pid interface{}, opt *MergeBaseOptions, options ...OptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merge_base", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

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


<commit_before>package jsonapi

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestParseRequestError(t *testing.T) {
	invalidAccept := constructRequest("GET", "")
	invalidAccept.Header.Set("Accept", "foo")

	invalidContentType := constructRequest("GET", "")
	invalidContentType.Header.Set("Content-Type", "foo")

	list := []*http.Request{
		invalidAccept,
		invalidContentType,
		constructRequest("PUT", ""),
		constructRequest("GET", ""),
		constructRequest("POST", ""),
		constructRequest("GET", "/"),
		constructRequest("GET", "foo/bar/baz/qux"),
		constructRequest("GET", "foo/bar/baz/qux/quux"),
		constructRequest("GET", "foo?page[number]=bar"),
		constructRequest("GET", "foo?page[size]=bar"),
		constructRequest("GET", "foo?page[number]=1"),
		constructRequest("GET", "foo?page[size]=1"),
		constructRequest("GET", "foo?page[number]=bar&page[number]=baz"),
		constructRequest("GET", "foo?page[size]=bar&page[size]=baz"),
	}

	for _, r := range list {
		req, err := ParseRequest(r, "")
		assert.Error(t, err)
		assert.Nil(t, req)
	}
}

func TestParseRequestMethodOverride(t *testing.T) {
	r := constructRequest("GET", "foo")
	r.Header.Set("Content-Type", ContentType)
	r.Header.Set("X-HTTP-Method-Override", "PATCH")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Update,
		Target:       ResourceCollection,
		ResourceType: "foo",
	}, req)
}

func TestParseRequestPrefix(t *testing.T) {
	r := constructRequest("GET", "foo/bar")

	req, err := ParseRequest(r, "foo/")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "bar",
	}, req)
}

func TestParseRequestResource(t *testing.T) {
	r := constructRequest("GET", "foo")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
	}, req)
}

func TestParseRequestResourceID(t *testing.T) {
	r := constructRequest("GET", "foo/1")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       SingleResource,
		ResourceType: "foo",
		ResourceID:   "1",
	}, req)
}

func TestParseRequestRelatedResource(t *testing.T) {
	r := constructRequest("GET", "foo/1/bar")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:          Fetch,
		Target:          RelatedResource,
		ResourceType:    "foo",
		ResourceID:      "1",
		RelatedResource: "bar",
	}, req)
}

func TestParseRequestRelationship(t *testing.T) {
	r := constructRequest("GET", "foo/1/relationships/bar")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       Relationship,
		ResourceType: "foo",
		ResourceID:   "1",
		Relationship: "bar",
	}, req)
}

func TestParseRequestInclude(t *testing.T) {
	r1 := constructRequest("GET", "foo?include=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Include:      []string{"bar", "baz"},
	}, req)

	r2 := constructRequest("GET", "foo?include=bar&include=baz,qux")

	req, err = ParseRequest(r2, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Include:      []string{"bar", "baz", "qux"},
	}, req)
}

func TestParseRequestSorting(t *testing.T) {
	r1 := constructRequest("GET", "foo?sort=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Sorting:      []string{"bar", "baz"},
	}, req)

	r2 := constructRequest("GET", "foo?sort=bar&sort=baz,qux")

	req, err = ParseRequest(r2, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Sorting:      []string{"bar", "baz", "qux"},
	}, req)
}

func TestParseRequestPage(t *testing.T) {
	r1 := constructRequest("GET", "foo?page[number]=1&page[size]=2")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		PageNumber:   1,
		PageSize:     2,
	}, req)
}

func TestParseRequestFields(t *testing.T) {
	r1 := constructRequest("GET", "foo?fields[foo]=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Fields: map[string][]string{
			"foo": {"bar", "baz"},
		},
	}, req)
}

func TestParseRequestFilters(t *testing.T) {
	r1 := constructRequest("GET", "foo?filter[foo]=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Filters: map[string][]string{
			"foo": {"bar", "baz"},
		},
	}, req)
}

func BenchmarkParseRequest(b *testing.B) {
	r := constructRequest("GET", "foo/1")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		ParseRequest(r, "")
	}
}

func BenchmarkParseRequestFilterAndSort(b *testing.B) {
	r := constructRequest("GET", "foo/1?sort=bar&filter[baz]=qux")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		ParseRequest(r, "")
	}
}
<commit_msg>use valid pattern<commit_after>package jsonapi

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestParseRequestError(t *testing.T) {
	invalidAccept := constructRequest("GET", "")
	invalidAccept.Header.Set("Accept", "foo")

	invalidContentType := constructRequest("GET", "")
	invalidContentType.Header.Set("Content-Type", "foo")

	list := []*http.Request{
		invalidAccept,
		invalidContentType,
		constructRequest("PUT", ""),
		constructRequest("GET", ""),
		constructRequest("POST", ""),
		constructRequest("GET", "/"),
		constructRequest("GET", "foo/bar/baz/qux"),
		constructRequest("GET", "foo/bar/baz/qux/quux"),
		constructRequest("GET", "foo?page[number]=bar"),
		constructRequest("GET", "foo?page[size]=bar"),
		constructRequest("GET", "foo?page[number]=1"),
		constructRequest("GET", "foo?page[size]=1"),
		constructRequest("GET", "foo?page[number]=bar&page[number]=baz"),
		constructRequest("GET", "foo?page[size]=bar&page[size]=baz"),
	}

	for _, r := range list {
		req, err := ParseRequest(r, "")
		assert.Error(t, err)
		assert.Nil(t, req)
	}
}

func TestParseRequestMethodOverride(t *testing.T) {
	r := constructRequest("GET", "foo/1")
	r.Header.Set("Content-Type", ContentType)
	r.Header.Set("X-HTTP-Method-Override", "PATCH")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Update,
		Target:       SingleResource,
		ResourceType: "foo",
		ResourceID:   "1",
	}, req)
}

func TestParseRequestPrefix(t *testing.T) {
	r := constructRequest("GET", "foo/bar")

	req, err := ParseRequest(r, "foo/")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "bar",
	}, req)
}

func TestParseRequestResource(t *testing.T) {
	r := constructRequest("GET", "foo")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
	}, req)
}

func TestParseRequestResourceID(t *testing.T) {
	r := constructRequest("GET", "foo/1")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       SingleResource,
		ResourceType: "foo",
		ResourceID:   "1",
	}, req)
}

func TestParseRequestRelatedResource(t *testing.T) {
	r := constructRequest("GET", "foo/1/bar")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:          Fetch,
		Target:          RelatedResource,
		ResourceType:    "foo",
		ResourceID:      "1",
		RelatedResource: "bar",
	}, req)
}

func TestParseRequestRelationship(t *testing.T) {
	r := constructRequest("GET", "foo/1/relationships/bar")

	req, err := ParseRequest(r, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       Relationship,
		ResourceType: "foo",
		ResourceID:   "1",
		Relationship: "bar",
	}, req)
}

func TestParseRequestInclude(t *testing.T) {
	r1 := constructRequest("GET", "foo?include=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Include:      []string{"bar", "baz"},
	}, req)

	r2 := constructRequest("GET", "foo?include=bar&include=baz,qux")

	req, err = ParseRequest(r2, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Include:      []string{"bar", "baz", "qux"},
	}, req)
}

func TestParseRequestSorting(t *testing.T) {
	r1 := constructRequest("GET", "foo?sort=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Sorting:      []string{"bar", "baz"},
	}, req)

	r2 := constructRequest("GET", "foo?sort=bar&sort=baz,qux")

	req, err = ParseRequest(r2, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Sorting:      []string{"bar", "baz", "qux"},
	}, req)
}

func TestParseRequestPage(t *testing.T) {
	r1 := constructRequest("GET", "foo?page[number]=1&page[size]=2")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		PageNumber:   1,
		PageSize:     2,
	}, req)
}

func TestParseRequestFields(t *testing.T) {
	r1 := constructRequest("GET", "foo?fields[foo]=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Fields: map[string][]string{
			"foo": {"bar", "baz"},
		},
	}, req)
}

func TestParseRequestFilters(t *testing.T) {
	r1 := constructRequest("GET", "foo?filter[foo]=bar,baz")

	req, err := ParseRequest(r1, "")
	assert.NoError(t, err)
	assert.Equal(t, &Request{
		Action:       Fetch,
		Target:       ResourceCollection,
		ResourceType: "foo",
		Filters: map[string][]string{
			"foo": {"bar", "baz"},
		},
	}, req)
}

func BenchmarkParseRequest(b *testing.B) {
	r := constructRequest("GET", "foo/1")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		ParseRequest(r, "")
	}
}

func BenchmarkParseRequestFilterAndSort(b *testing.B) {
	r := constructRequest("GET", "foo/1?sort=bar&filter[baz]=qux")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		ParseRequest(r, "")
	}
}
<|endoftext|>


<commit_before>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"time"

	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/streadway/amqp"
)

var (
	PopularPostKey = "popularpost"
)

type Controller struct {
	log   logging.Logger
	redis *redis.RedisSession
}

func (t *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func New(log logging.Logger, redis *redis.RedisSession) *Controller {
	return &Controller{
		log:   log,
		redis: redis,
	}
}

func (f *Controller) InteractionSaved(i *models.Interaction) error {
	return f.handleInteraction(1, i)
}

func (f *Controller) InteractionDeleted(i *models.Interaction) error {
	return f.handleInteraction(-1, i)
}

func (f *Controller) handleInteraction(incrementCount int, i *models.Interaction) error {
	cm, err := models.ChannelMessageById(i.MessageId)
	if err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if notEligibleForPopularPost(c, cm) {
		f.log.Error(fmt.Sprintf("Not eligible Interaction Id:%d", i.Id))
		return nil
	}

	_, err = f.redis.SortedSetIncrBy(GetDailyKey(c, cm, i), incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	return nil
}

func PreparePopularPostKey(group, channelName string, year, dateNumber int) string {
	return fmt.Sprintf(
		"%s:%s:%s:%s:%d:%d",
		config.MustGet().Environment,
		group,
		PopularPostKey,
		channelName,
		year,
		dateNumber,
	)
}

func GetDailyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	day := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		day = i.CreatedAt.UTC().YearDay()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now().UTC()
		day = now.YearDay()
		year, _, _ = now.Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, year, day)
}

//----------------------------------------------------------
// helpers
//----------------------------------------------------------

func notEligibleForPopularPost(c *models.Channel, cm *models.ChannelMessage) bool {
	if c.MetaBits.Is(models.Troll) {
		return true
	}

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return true
	}

	if cm.MetaBits.Is(models.Troll) {
		return true
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return true
	}

	return false
}
<commit_msg>popularpost: ignore likes for posts created more than 7 days ago<commit_after>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"time"

	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/streadway/amqp"
)

var (
	PopularPostKey = "popularpost"
)

type Controller struct {
	log   logging.Logger
	redis *redis.RedisSession
}

func (t *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func New(log logging.Logger, redis *redis.RedisSession) *Controller {
	return &Controller{
		log:   log,
		redis: redis,
	}
}

func (f *Controller) InteractionSaved(i *models.Interaction) error {
	return f.handleInteraction(1, i)
}

func (f *Controller) InteractionDeleted(i *models.Interaction) error {
	return f.handleInteraction(-1, i)
}

func (f *Controller) handleInteraction(incrementCount int, i *models.Interaction) error {
	cm, err := models.ChannelMessageById(i.MessageId)
	if err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if notEligibleForPopularPost(c, cm) {
		f.log.Error(fmt.Sprintf("Not eligible Interaction Id:%d", i.Id))
		return nil
	}

	if createdMoreThan7DaysAgo(cm.CreatedAt) {
		f.log.Debug(fmt.Sprintf("Post created more than 7 days ago: %v, %v", i.Id, i.CreatedAt))
		return nil
	}

	err = f.saveToDailyBucket(c, cm, i, incrementCount)
	if err != nil {
		return err
	}

	return nil
}

func (f *Controller) saveToDailyBucket(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction, incrementCount int) error {
	_, err := f.redis.SortedSetIncrBy(GetDailyKey(c, cm, i), incrementCount, i.MessageId)
	return err
}

func PreparePopularPostKey(group, channelName string, year, dateNumber int) string {
	return fmt.Sprintf(
		"%s:%s:%s:%s:%d:%d",
		config.MustGet().Environment,
		group,
		PopularPostKey,
		channelName,
		year,
		dateNumber,
	)
}

func GetDailyKey(c *models.Channel, cm *models.ChannelMessage, i *models.Interaction) string {
	day := 0
	year := 2014

	if !i.CreatedAt.IsZero() {
		day = i.CreatedAt.UTC().YearDay()
		year, _, _ = i.CreatedAt.UTC().Date()
	} else {
		// no need to convert it to UTC
		now := time.Now().UTC()
		day = now.YearDay()
		year, _, _ = now.Date()
	}

	return PreparePopularPostKey(c.GroupName, c.Name, year, day)
}

//----------------------------------------------------------
// helpers
//----------------------------------------------------------

func notEligibleForPopularPost(c *models.Channel, cm *models.ChannelMessage) bool {
	if c.MetaBits.Is(models.Troll) {
		return true
	}

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return true
	}

	if cm.MetaBits.Is(models.Troll) {
		return true
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return true
	}

	return false
}

func createdMoreThan7DaysAgo(createdAt time.Time) bool {
	delta := time.Now().Sub(createdAt)
	return delta.Hours()/24 > 7
}
<|endoftext|>


<commit_before>package brain_test

import (
	"fmt"
	"testing"

	"github.com/BytemarkHosting/bytemark-client/lib"
	brainRequests "github.com/BytemarkHosting/bytemark-client/lib/requests/brain"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil/assert"
)

func TestDeleteAPIKey(t *testing.T) {
	tests := []struct {
		name       string
		id         int
		labelOrID  string
		statusCode int
		shouldErr  bool
	}{
		{
			name:       "success",
			labelOrID:  "9",
			statusCode: 200,
			shouldErr:  false,
		}, {
			name:       "successjumanji",
			id:         229,
			labelOrID:  "jumanji",
			statusCode: 200,
			shouldErr:  false,
		}, {
			name:       "error 500",
			labelOrID:  "25",
			statusCode: 500,
			shouldErr:  true,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			rts := testutil.RequestTestSpec{
				Method:        "DELETE",
				URL:           fmt.Sprintf("/api_keys/%s", test.id),
				Endpoint:      lib.BrainEndpoint,
				StatusCode:    test.statusCode,
				AssertRequest: assert.BodyString(""),
			}
			rts.Run(t, test.name, true, func(client lib.Client) {
				err := brainRequests.DeleteAPIKey(client, test.labelOrID)
				if err != nil && !test.shouldErr {
					t.Errorf("Unexpected error: %v", err)
				} else if err == nil && test.shouldErr {
					t.Error("Error expected but not returned")
				}
			})
		})
	}
}
<commit_msg>fix delete api key test<commit_after>package brain_test

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

	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	brainRequests "github.com/BytemarkHosting/bytemark-client/lib/requests/brain"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil/assert"
)

func TestDeleteAPIKey(t *testing.T) {
	tests := []struct {
		name       string
		id         int
		labelOrID  string
		statusCode int
		shouldErr  bool
		apiKeys    brain.APIKeys
	}{
		{
			name:       "success",
			id:         9,
			labelOrID:  "9",
			statusCode: 200,
			shouldErr:  false,
		}, {
			name:       "successjumanji",
			id:         229,
			labelOrID:  "jumanji",
			statusCode: 200,
			shouldErr:  false,
			apiKeys: brain.APIKeys{{
				ID:    442,
				Label: "not-jumanji",
			}, {
				ID:    229,
				Label: "jumanji",
			}},
		}, {
			name:       "error 500",
			id:         25,
			labelOrID:  "25",
			statusCode: 500,
			shouldErr:  true,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			rts := testutil.RequestTestSpec{
				MuxHandlers: &testutil.MuxHandlers{
					Brain: testutil.Mux{
						fmt.Sprintf("/api_keys/%d", test.id): func(wr http.ResponseWriter, r *http.Request) {
							assert.BodyString("")(t, "", r)
							assert.Method("DELETE")(t, "", r)
							wr.WriteHeader(test.statusCode)
						},
						"/api_keys": func(wr http.ResponseWriter, r *http.Request) {
							assert.Method("GET")(t, "", r)
							bs, err := json.Marshal(test.apiKeys)
							if err != nil {
								t.Fatalf("json marshal failed: %s", err)
							}
							_, err = wr.Write(bs)
							if err != nil {
								t.Fatalf("http write failed %s", err)
							}
						},
					},
				},
			}
			rts.Run(t, test.name, true, func(client lib.Client) {
				err := brainRequests.DeleteAPIKey(client, test.labelOrID)
				if err != nil && !test.shouldErr {
					t.Errorf("Unexpected error: %v", err)
				} else if err == nil && test.shouldErr {
					t.Error("Error expected but not returned")
				}
			})
		})
	}
}
<|endoftext|>


<commit_before>package router

import (
	"log"

	"github.com/oursky/ourd/oddb"
)

// Payload is for passing payload to the actual handler
type Payload struct {
	// Map of params such as Auth, TimeSteam, version
	Meta map[string]interface{}
	// Map of action payload
	Data   map[string]interface{}
	DBConn oddb.Conn
}

// RouteAction must exist for every request
func (p *Payload) RouteAction() string {
	return p.Data["action"].(string)
}

// IsAuth tell the middleware is this payload is an auth request
func (p *Payload) IsAuth() bool {
	defer func() {
		if r := recover(); r != nil {
			log.Println("IsAuth recover")
		}
		return
	}()
	return p.Data["action"].(string)[0:5] == "auth:"
}

// AccessToken return the user input string
// TODO: accept all header, json payload, query string(in order)
func (p *Payload) AccessToken() string {
	var token interface{}
	token = p.Data["access_token"]
	switch token := token.(type) {
	default:
		return ""
	case string:
		return token
	}
}

// Response is interface for handler to write response to router
type Response struct {
	Meta       map[string]interface{} `json:"-"`
	Result     interface{}            `json:"result"`
	RequestID  string                 `json:"request_id,omitempty"`
	DatabaseID string                 `json:"database_id,omitempty"`
}
<commit_msg>Fix server panic if no route action is passed<commit_after>package router

import (
	"log"

	"github.com/oursky/ourd/oddb"
)

// Payload is for passing payload to the actual handler
type Payload struct {
	// Map of params such as Auth, TimeSteam, version
	Meta map[string]interface{}
	// Map of action payload
	Data   map[string]interface{}
	DBConn oddb.Conn
}

// RouteAction must exist for every request
func (p *Payload) RouteAction() string {
	actionStr, _ := p.Data["action"].(string)
	return actionStr
}

// IsAuth tell the middleware is this payload is an auth request
func (p *Payload) IsAuth() bool {
	defer func() {
		if r := recover(); r != nil {
			log.Println("IsAuth recover")
		}
		return
	}()
	return p.Data["action"].(string)[0:5] == "auth:"
}

// AccessToken return the user input string
// TODO: accept all header, json payload, query string(in order)
func (p *Payload) AccessToken() string {
	var token interface{}
	token = p.Data["access_token"]
	switch token := token.(type) {
	default:
		return ""
	case string:
		return token
	}
}

// Response is interface for handler to write response to router
type Response struct {
	Meta       map[string]interface{} `json:"-"`
	Result     interface{}            `json:"result"`
	RequestID  string                 `json:"request_id,omitempty"`
	DatabaseID string                 `json:"database_id,omitempty"`
}
<|endoftext|>


<commit_before>package router

import (
	"fmt"
)

type RoutingTableErrorCode int

const (
	_                                  = iota
	ServiceError RoutingTableErrorCode = iota
	LookupError

	UnknownClient

	MappingNotFoundError
)

type RoutingTableError struct {
	Code    RoutingTableErrorCode
	Message string
}

func (err RoutingTableError) Error() string {
	return fmt.Sprintf("%v (Routing Error Code: %d)", err.Message, err.Code)
}

func NewRoutingTableError(code RoutingTableErrorCode, message string) *RoutingTableError {
	return &RoutingTableError{Code: code, Message: message}
}

// A RoutingTable provides all core interfaces.
type RoutingTable interface {
	ClientTable
}

// Routing tables meeting the ClientTable spec answer basic questions about
// a client such as which server messages should be routed to for a given
// service and which message-flow front-end is handling communication with the
// client.
type ClientTable interface {
	// Get client information
	// Which message server handles communication for client.
	GetClientMessageServer(ClientID) (string, error)

	// Which server for service should messages from client be routed to.
	GetClientServiceServer(ClientID, ServiceID) (string, error)
}
<commit_msg>Convert table interface to return ServerID<commit_after>package router

import (
	"fmt"
)

type RoutingTableErrorCode int

const (
	_                                  = iota
	ServiceError RoutingTableErrorCode = iota
	LookupError

	UnknownClient

	MappingNotFoundError
)

type RoutingTableError struct {
	Code    RoutingTableErrorCode
	Message string
}

func (err RoutingTableError) Error() string {
	return fmt.Sprintf("%v (Routing Error Code: %d)", err.Message, err.Code)
}

func NewRoutingTableError(code RoutingTableErrorCode, message string) *RoutingTableError {
	return &RoutingTableError{Code: code, Message: message}
}

// A RoutingTable provides all core interfaces.
type RoutingTable interface {
	ClientTable
}

// Routing tables meeting the ClientTable spec answer basic questions about
// a client such as which server messages should be routed to for a given
// service and which message-flow front-end is handling communication with the
// client.
type ClientTable interface {
	// Get client information
	// Which message server handles communication for client.
	GetClientMessageServer(ClientID) (ServerID, error)

	// Which server for service should messages from client be routed to.
	GetClientServiceServer(ClientID, ServiceID) (ServerID, error)
}
<|endoftext|>


<commit_before>// Copyright 2013 Beego Web authors
//
// 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 routers

import (
	"net/http"

	"github.com/astaxie/beego/context"
	"github.com/beego/i18n"

	"github.com/beego/beeweb/models"
)

// DocsRouter serves about page.
type DocsRouter struct {
	baseRouter
}

// Get implemented Get method for DocsRouter.
func (this *DocsRouter) Get() {
	this.Data["IsDocs"] = true
	this.TplNames = "docs.html"

	dRoot := models.GetDocByLocale(this.Lang)

	if dRoot == nil {
		this.Abort("404")
		return
	}

	link := this.GetString(":all")

	var doc *models.DocNode
	if len(link) == 0 {
		if dRoot.Doc.HasContent() {
			doc = dRoot.Doc
		} else {
			this.Redirect("/docs/intro/", 302)
			return
		}
	} else {
		doc, _ = dRoot.GetNodeByLink(link)
		if doc == nil {
			doc, _ = dRoot.GetNodeByLink(link + "/")
			if doc != nil {
				this.Redirect("/docs/"+link+"/", 301)
				return
			}
		}
	}

	if doc == nil {
		this.Abort("404")
		return
	}

	this.Data["DocRoot"] = dRoot
	this.Data["Doc"] = doc
	this.Data["Title"] = doc.Name
	this.Data["Data"] = doc.GetContent()
}

func DocsStatic(ctx *context.Context) {
	uri := ctx.Input.Params[":all"]
	if len(uri) > 0 {
		lang := ctx.GetCookie("lang")
		if !i18n.IsExist(lang) {
			lang = "en-US"
		}
		http.ServeFile(ctx.ResponseWriter, ctx.Request, "docs/"+lang+"/"+"images/"+uri)
	}
}
<commit_msg>Fixed bug: serve images no different according to language option<commit_after>// Copyright 2013 Beego Web authors
//
// 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 routers

import (
	"io"
	"os"

	"github.com/astaxie/beego/context"
	"github.com/beego/i18n"

	"github.com/beego/beeweb/models"
)

// DocsRouter serves about page.
type DocsRouter struct {
	baseRouter
}

// Get implemented Get method for DocsRouter.
func (this *DocsRouter) Get() {
	this.Data["IsDocs"] = true
	this.TplNames = "docs.html"

	dRoot := models.GetDocByLocale(this.Lang)

	if dRoot == nil {
		this.Abort("404")
		return
	}

	link := this.GetString(":all")

	var doc *models.DocNode
	if len(link) == 0 {
		if dRoot.Doc.HasContent() {
			doc = dRoot.Doc
		} else {
			this.Redirect("/docs/intro/", 302)
			return
		}
	} else {
		doc, _ = dRoot.GetNodeByLink(link)
		if doc == nil {
			doc, _ = dRoot.GetNodeByLink(link + "/")
			if doc != nil {
				this.Redirect("/docs/"+link+"/", 301)
				return
			}
		}
	}

	if doc == nil {
		this.Abort("404")
		return
	}

	this.Data["DocRoot"] = dRoot
	this.Data["Doc"] = doc
	this.Data["Title"] = doc.Name
	this.Data["Data"] = doc.GetContent()
}

func DocsStatic(ctx *context.Context) {
	if uri := ctx.Input.Params[":all"]; len(uri) > 0 {
		lang := ctx.GetCookie("lang")
		if !i18n.IsExist(lang) {
			lang = "en-US"
		}

		f, err := os.Open("docs/" + lang + "/" + "images/" + uri)
		if err != nil {
			ctx.WriteString(err.Error())
			return
		}
		defer f.Close()

		_, err = io.Copy(ctx.ResponseWriter, f)
		if err != nil {
			ctx.WriteString(err.Error())
			return
		}
	}
}
<|endoftext|>


<commit_before>package mounttable

import (
	"errors"
	"net"
	"path"
	"strconv"
	"strings"

	"veyron/lib/glob"

	"veyron2/ipc"
	"veyron2/naming"
	"veyron2/rt"
	"veyron2/security"
	"veyron2/services/mounttable"
	"veyron2/vlog"

	"code.google.com/p/mdns"
)

const addressPrefix = "address:"

// neighborhood defines a set of machines on the same multicast media.
type neighborhood struct {
	mdns   *mdns.MDNS
	nelems int
}

type neighborhoodService struct {
	name  string
	elems []string
	nh    *neighborhood
}

func getPort(address string) uint16 {
	epAddr, _ := naming.SplitAddressName(address)

	ep, err := rt.R().NewEndpoint(epAddr)
	if err != nil {
		return 0
	}
	addr := ep.Addr()
	if addr == nil {
		return 0
	}
	if addr.Network() != "tcp" {
		return 0
	}
	_, pstr, err := net.SplitHostPort(addr.String())
	if err != nil {
		return 0
	}
	port, err := strconv.ParseUint(pstr, 10, 16)
	if err != nil || port == 0 {
		return 0
	}
	return uint16(port)
}

func newNeighborhoodServer(host string, addresses []string, loopback bool) (*neighborhood, error) {
	// Create the TXT contents with addresses to announce. Also pick up a port number.
	var txt []string
	var port uint16
	for _, addr := range addresses {
		txt = append(txt, addressPrefix+addr)
		if port == 0 {
			port = getPort(addr)
		}
	}
	if txt == nil {
		return nil, errors.New("neighborhood passed no useful addresses")
	}
	if port == 0 {
		return nil, errors.New("neighborhood couldn't determine a port to use")
	}

	// Start up MDNS, subscribe to the veyron service, and add us as a veyron service provider.
	mdns, err := mdns.NewMDNS(host, "", "", loopback, false)
	if err != nil {
		vlog.Errorf("mdns startup failed: %s", err)
		return nil, err
	}
	vlog.VI(2).Infof("listening for service veyron on port %d", port)
	mdns.SubscribeToService("veyron")
	mdns.AddService("veyron", "", port, txt...)

	nh := &neighborhood{
		mdns: mdns,
	}
	return nh, nil
}

// NewLoopbackNeighborhoodServer creates a new instance of a neighborhood server on loopback interfaces for testing.
func NewLoopbackNeighborhoodServer(host string, addresses ...string) (*neighborhood, error) {
	return newNeighborhoodServer(host, addresses, true)
}

// NewNeighborhoodServer creates a new instance of a neighborhood server.
func NewNeighborhoodServer(host string, addresses ...string) (*neighborhood, error) {
	return newNeighborhoodServer(host, addresses, false)
}

// Lookup implements ipc.Dispatcher.Lookup.
func (nh *neighborhood) Lookup(name string) (ipc.Invoker, security.Authorizer, error) {
	vlog.VI(1).Infof("*********************LookupServer '%s'\n", name)
	elems := strings.Split(name, "/")[nh.nelems:]
	if name == "" {
		elems = nil
	}
	ns := &neighborhoodService{
		name:  name,
		elems: elems,
		nh:    nh,
	}
	return ipc.ReflectInvoker(mounttable.NewServerMountTable(ns)), nh, nil
}

func (nh *neighborhood) Authorize(context security.Context) error {
	// TODO(rthellend): Figure out whether it's OK to accept all requests
	// unconditionally.
	return nil
}

// Stop performs cleanup.
func (nh *neighborhood) Stop() {
	nh.mdns.Stop()
}

// neighbor returns the MountedServers for a particular neighbor.
func (nh *neighborhood) neighbor(instance string) []mounttable.MountedServer {
	var reply []mounttable.MountedServer
	si := nh.mdns.ResolveInstance(instance, "veyron")

	// Use a map to dedup any addresses seen
	addrMap := make(map[string]uint32)

	// Look for any TXT records with addresses.
	for _, rr := range si.TxtRRs {
		for _, s := range rr.Txt {
			if !strings.HasPrefix(s, addressPrefix) {
				continue
			}
			addr := s[len(addressPrefix):]
			addrMap[addr] = rr.Header().Ttl
		}
	}
	for addr, ttl := range addrMap {
		reply = append(reply, mounttable.MountedServer{addr, ttl})
	}

	if reply != nil {
		return reply
	}

	// If we didn't get any direct endpoints, make some up from the target and port.
	// TODO(rthellend): Do we need the code below? If we haven't received
	// any results at this point, it would seem to indicate a bug somewhere
	// because NeighborhoodServer won't start without at least one address.
	for _, rr := range si.SrvRRs {
		ips, ttl := nh.mdns.ResolveAddress(rr.Target)
		for _, ip := range ips {
			addr := net.JoinHostPort(ip.String(), strconv.Itoa(int(rr.Port)))
			ep := naming.FormatEndpoint("tcp", addr)
			reply = append(reply, mounttable.MountedServer{naming.JoinAddressName(ep, ""), ttl})
		}
	}
	return reply
}

// neighbors returns all neighbors and their MountedServer structs.
func (nh *neighborhood) neighbors() map[string][]mounttable.MountedServer {
	neighbors := make(map[string][]mounttable.MountedServer, 0)
	members := nh.mdns.ServiceDiscovery("veyron")
	for _, m := range members {
		if neighbor := nh.neighbor(m.Name); neighbor != nil {
			neighbors[m.Name] = neighbor
		}
	}
	vlog.VI(2).Infof("members %v neighbors %v", members, neighbors)
	return neighbors
}

// ResolveStep implements ResolveStep
func (ns *neighborhoodService) ResolveStep(_ ipc.ServerContext) (servers []mounttable.MountedServer, suffix string, err error) {
	nh := ns.nh
	vlog.VI(2).Infof("ResolveStep %v\n", ns.elems)
	if len(ns.elems) == 0 {
		//nothing can be mounted at the root
		return nil, "", naming.ErrNoSuchNameRoot
	}

	// We can only resolve the first element and it always refers to a mount table (for now).
	neighbor := nh.neighbor(ns.elems[0])
	if neighbor == nil {
		return nil, "", naming.ErrNoSuchName
	}
	return neighbor, path.Join(ns.elems[1:]...), nil
}

// Mount not implemented.
func (*neighborhoodService) Mount(_ ipc.ServerContext, server string, ttlsecs uint32) error {
	return errors.New("this server does not implement Mount")
}

// Unmount not implemented.
func (*neighborhoodService) Unmount(_ ipc.ServerContext, _ string) error {
	return errors.New("this server does not implement Unmount")
}

// Glob implements Glob
func (ns *neighborhoodService) Glob(_ ipc.ServerContext, pattern string, reply mounttable.GlobableServiceGlobStream) error {
	g, err := glob.Parse(pattern)
	if err != nil {
		return err
	}

	// return all neighbors that match the first element of the pattern.
	nh := ns.nh

	switch len(ns.elems) {
	case 0:
		for k, n := range nh.neighbors() {
			if ok, _ := g.MatchInitialSegment(k); !ok {
				continue
			}
			if err := reply.Send(mounttable.MountEntry{Name: k, Servers: n}); err != nil {
				return err
			}
		}
		return nil
	case 1:
		neighbor := nh.neighbor(ns.elems[0])
		if neighbor == nil {
			return naming.ErrNoSuchName
		}
		return reply.Send(mounttable.MountEntry{Name: "", Servers: neighbor})
	default:
		return naming.ErrNoSuchName
	}
}
<commit_msg>Follow network config changes.<commit_after>package mounttable

import (
	"errors"
	"net"
	"path"
	"strconv"
	"strings"

	"veyron/lib/glob"
	"veyron/runtimes/google/lib/netconfig"

	"veyron2/ipc"
	"veyron2/naming"
	"veyron2/rt"
	"veyron2/security"
	"veyron2/services/mounttable"
	"veyron2/vlog"

	"code.google.com/p/mdns"
)

const addressPrefix = "address:"

// neighborhood defines a set of machines on the same multicast media.
type neighborhood struct {
	mdns   *mdns.MDNS
	nelems int
	nw     netconfig.NetConfigWatcher
}

type neighborhoodService struct {
	name  string
	elems []string
	nh    *neighborhood
}

func getPort(address string) uint16 {
	epAddr, _ := naming.SplitAddressName(address)

	ep, err := rt.R().NewEndpoint(epAddr)
	if err != nil {
		return 0
	}
	addr := ep.Addr()
	if addr == nil {
		return 0
	}
	if addr.Network() != "tcp" {
		return 0
	}
	_, pstr, err := net.SplitHostPort(addr.String())
	if err != nil {
		return 0
	}
	port, err := strconv.ParseUint(pstr, 10, 16)
	if err != nil || port == 0 {
		return 0
	}
	return uint16(port)
}

func newNeighborhoodServer(host string, addresses []string, loopback bool) (*neighborhood, error) {
	// Create the TXT contents with addresses to announce. Also pick up a port number.
	var txt []string
	var port uint16
	for _, addr := range addresses {
		txt = append(txt, addressPrefix+addr)
		if port == 0 {
			port = getPort(addr)
		}
	}
	if txt == nil {
		return nil, errors.New("neighborhood passed no useful addresses")
	}
	if port == 0 {
		return nil, errors.New("neighborhood couldn't determine a port to use")
	}

	// Start up MDNS, subscribe to the veyron service, and add us as a veyron service provider.
	mdns, err := mdns.NewMDNS(host, "", "", loopback, false)
	if err != nil {
		vlog.Errorf("mdns startup failed: %s", err)
		return nil, err
	}
	vlog.VI(2).Infof("listening for service veyron on port %d", port)
	mdns.SubscribeToService("veyron")
	mdns.AddService("veyron", "", port, txt...)

	nh := &neighborhood{
		mdns: mdns,
	}

	// Watch the network configuration so that we can make MDNS reattach to
	// interfaces when the network changes.
	nh.nw, err = netconfig.NewNetConfigWatcher()
	if err != nil {
		vlog.Errorf("nighborhood can't watch network: %s", err)
		return nh, nil
	}
	go func() {
		if _, ok := <-nh.nw.Channel(); !ok {
			return
		}
		if _, err := nh.mdns.ScanInterfaces(); err != nil {
			vlog.Errorf("nighborhood can't scan interfaces: %s", err)
		}
	}()

	return nh, nil
}

// NewLoopbackNeighborhoodServer creates a new instance of a neighborhood server on loopback interfaces for testing.
func NewLoopbackNeighborhoodServer(host string, addresses ...string) (*neighborhood, error) {
	return newNeighborhoodServer(host, addresses, true)
}

// NewNeighborhoodServer creates a new instance of a neighborhood server.
func NewNeighborhoodServer(host string, addresses ...string) (*neighborhood, error) {
	return newNeighborhoodServer(host, addresses, false)
}

// Lookup implements ipc.Dispatcher.Lookup.
func (nh *neighborhood) Lookup(name string) (ipc.Invoker, security.Authorizer, error) {
	vlog.VI(1).Infof("*********************LookupServer '%s'\n", name)
	elems := strings.Split(name, "/")[nh.nelems:]
	if name == "" {
		elems = nil
	}
	ns := &neighborhoodService{
		name:  name,
		elems: elems,
		nh:    nh,
	}
	return ipc.ReflectInvoker(mounttable.NewServerMountTable(ns)), nh, nil
}

func (nh *neighborhood) Authorize(context security.Context) error {
	// TODO(rthellend): Figure out whether it's OK to accept all requests
	// unconditionally.
	return nil
}

// Stop performs cleanup.
func (nh *neighborhood) Stop() {
	if nh.nw != nil {
		nh.nw.Stop()
	}
	nh.mdns.Stop()
}

// neighbor returns the MountedServers for a particular neighbor.
func (nh *neighborhood) neighbor(instance string) []mounttable.MountedServer {
	var reply []mounttable.MountedServer
	si := nh.mdns.ResolveInstance(instance, "veyron")

	// Use a map to dedup any addresses seen
	addrMap := make(map[string]uint32)

	// Look for any TXT records with addresses.
	for _, rr := range si.TxtRRs {
		for _, s := range rr.Txt {
			if !strings.HasPrefix(s, addressPrefix) {
				continue
			}
			addr := s[len(addressPrefix):]
			addrMap[addr] = rr.Header().Ttl
		}
	}
	for addr, ttl := range addrMap {
		reply = append(reply, mounttable.MountedServer{addr, ttl})
	}

	if reply != nil {
		return reply
	}

	// If we didn't get any direct endpoints, make some up from the target and port.
	// TODO(rthellend): Do we need the code below? If we haven't received
	// any results at this point, it would seem to indicate a bug somewhere
	// because NeighborhoodServer won't start without at least one address.
	for _, rr := range si.SrvRRs {
		ips, ttl := nh.mdns.ResolveAddress(rr.Target)
		for _, ip := range ips {
			addr := net.JoinHostPort(ip.String(), strconv.Itoa(int(rr.Port)))
			ep := naming.FormatEndpoint("tcp", addr)
			reply = append(reply, mounttable.MountedServer{naming.JoinAddressName(ep, ""), ttl})
		}
	}
	return reply
}

// neighbors returns all neighbors and their MountedServer structs.
func (nh *neighborhood) neighbors() map[string][]mounttable.MountedServer {
	neighbors := make(map[string][]mounttable.MountedServer, 0)
	members := nh.mdns.ServiceDiscovery("veyron")
	for _, m := range members {
		if neighbor := nh.neighbor(m.Name); neighbor != nil {
			neighbors[m.Name] = neighbor
		}
	}
	vlog.VI(2).Infof("members %v neighbors %v", members, neighbors)
	return neighbors
}

// ResolveStep implements ResolveStep
func (ns *neighborhoodService) ResolveStep(_ ipc.ServerContext) (servers []mounttable.MountedServer, suffix string, err error) {
	nh := ns.nh
	vlog.VI(2).Infof("ResolveStep %v\n", ns.elems)
	if len(ns.elems) == 0 {
		//nothing can be mounted at the root
		return nil, "", naming.ErrNoSuchNameRoot
	}

	// We can only resolve the first element and it always refers to a mount table (for now).
	neighbor := nh.neighbor(ns.elems[0])
	if neighbor == nil {
		return nil, "", naming.ErrNoSuchName
	}
	return neighbor, path.Join(ns.elems[1:]...), nil
}

// Mount not implemented.
func (*neighborhoodService) Mount(_ ipc.ServerContext, server string, ttlsecs uint32) error {
	return errors.New("this server does not implement Mount")
}

// Unmount not implemented.
func (*neighborhoodService) Unmount(_ ipc.ServerContext, _ string) error {
	return errors.New("this server does not implement Unmount")
}

// Glob implements Glob
func (ns *neighborhoodService) Glob(_ ipc.ServerContext, pattern string, reply mounttable.GlobableServiceGlobStream) error {
	g, err := glob.Parse(pattern)
	if err != nil {
		return err
	}

	// return all neighbors that match the first element of the pattern.
	nh := ns.nh

	switch len(ns.elems) {
	case 0:
		for k, n := range nh.neighbors() {
			if ok, _ := g.MatchInitialSegment(k); !ok {
				continue
			}
			if err := reply.Send(mounttable.MountEntry{Name: k, Servers: n}); err != nil {
				return err
			}
		}
		return nil
	case 1:
		neighbor := nh.neighbor(ns.elems[0])
		if neighbor == nil {
			return naming.ErrNoSuchName
		}
		return reply.Send(mounttable.MountEntry{Name: "", Servers: neighbor})
	default:
		return naming.ErrNoSuchName
	}
}
<|endoftext|>