<commit_before>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package appmenu

import (
	"github.com/richardwilkes/i18n"
	"github.com/richardwilkes/ui/app"
	"github.com/richardwilkes/ui/app/quit"
	"github.com/richardwilkes/ui/event"
	"github.com/richardwilkes/ui/keys"
	"github.com/richardwilkes/ui/menu"
	"runtime"
)

// Install adds a standard 'application' menu to the front of the menu bar.
func Install(bar menu.Bar) (appMenu menu.Menu, aboutItem menu.Item, prefsItem menu.Item) {
	name := app.AppName()
	appMenu = menu.NewMenu(name)

	aboutItem = menu.NewItem(i18n.Text("About ")+name, nil)
	appMenu.AppendItem(aboutItem)

	appMenu.AppendItem(menu.NewSeparator())
	prefsItem = menu.NewItemWithKey(i18n.Text("Preferences…"), keys.VK_Comma, nil)
	appMenu.AppendItem(prefsItem)

	if runtime.GOOS == "darwin" {
		appMenu.AppendItem(menu.NewSeparator())
		servicesMenu := menu.NewMenu(i18n.Text("Services"))
		appMenu.AppendMenu(servicesMenu)
		bar.SetupSpecialMenu(menu.ServicesMenu, servicesMenu)
	}

	appMenu.AppendItem(menu.NewSeparator())
	appMenu.AppendItem(menu.NewItemWithKey(i18n.Text("Hide ")+name, keys.VK_H, func(evt event.Event) { app.HideApp() }))
	appMenu.AppendItem(menu.NewItemWithKeyAndModifiers(i18n.Text("Hide Others"), keys.VK_H, keys.OptionModifier|keys.PlatformMenuModifier(), func(evt event.Event) { app.HideOtherApps() }))
	appMenu.AppendItem(menu.NewItem(i18n.Text("Show All"), func(evt event.Event) { app.ShowAllApps() }))

	appMenu.AppendItem(menu.NewSeparator())
	appMenu.AppendItem(menu.NewItemWithKey(i18n.Text("Quit ")+name, keys.VK_Q, func(evt event.Event) { quit.AttemptQuit() }))

	bar.InsertMenu(appMenu, 0)

	return appMenu, aboutItem, prefsItem
}
<commit_msg>Only add 'Hide Others' and 'Show All' for macOS<commit_after>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package appmenu

import (
	"github.com/richardwilkes/i18n"
	"github.com/richardwilkes/ui/app"
	"github.com/richardwilkes/ui/app/quit"
	"github.com/richardwilkes/ui/event"
	"github.com/richardwilkes/ui/keys"
	"github.com/richardwilkes/ui/menu"
	"runtime"
)

// Install adds a standard 'application' menu to the front of the menu bar.
func Install(bar menu.Bar) (appMenu menu.Menu, aboutItem menu.Item, prefsItem menu.Item) {
	name := app.AppName()
	appMenu = menu.NewMenu(name)

	aboutItem = menu.NewItem(i18n.Text("About ")+name, nil)
	appMenu.AppendItem(aboutItem)

	appMenu.AppendItem(menu.NewSeparator())
	prefsItem = menu.NewItemWithKey(i18n.Text("Preferences…"), keys.VK_Comma, nil)
	appMenu.AppendItem(prefsItem)

	if runtime.GOOS == "darwin" {
		appMenu.AppendItem(menu.NewSeparator())
		servicesMenu := menu.NewMenu(i18n.Text("Services"))
		appMenu.AppendMenu(servicesMenu)
		bar.SetupSpecialMenu(menu.ServicesMenu, servicesMenu)
	}

	appMenu.AppendItem(menu.NewSeparator())
	appMenu.AppendItem(menu.NewItemWithKey(i18n.Text("Hide ")+name, keys.VK_H, func(evt event.Event) { app.HideApp() }))
	if runtime.GOOS == "darwin" {
		appMenu.AppendItem(menu.NewItemWithKeyAndModifiers(i18n.Text("Hide Others"), keys.VK_H, keys.OptionModifier|keys.PlatformMenuModifier(), func(evt event.Event) { app.HideOtherApps() }))
		appMenu.AppendItem(menu.NewItem(i18n.Text("Show All"), func(evt event.Event) { app.ShowAllApps() }))
	}

	appMenu.AppendItem(menu.NewSeparator())
	appMenu.AppendItem(menu.NewItemWithKey(i18n.Text("Quit ")+name, keys.VK_Q, func(evt event.Event) { quit.AttemptQuit() }))

	bar.InsertMenu(appMenu, 0)

	return appMenu, aboutItem, prefsItem
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

var (
	urlRecords = config.ApiURL + "/domains/" + config.Domain + "/records/"
)

// Struct holding a single record response.
type Record struct {
	Content      string `json:"content"`
	DomainID     int    `json:"domain_id"`
	ID           int
	Name         string `json:"name"`
	RecordType   string `json:"record_type"`
	SystemRecord bool   `json:"system_record"`
	TTL          int
}

// Listing records returns a slice of records.
type MultipleRecords []map[string]Record

func listRecordsDomain(domain string) {
	// If domain is empty, use the value from the cli.
	isDomainEmpty(domain)

	r := setHeaders("GET", urlRecords, nil)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := MultipleRecords{}
	err = json.NewDecoder(response.Body).Decode(&dataResponse)
	if err != nil {
		log.Fatal("Decode-Body: ", err)
	}

	filterRecords(dataResponse)
}

func updateRecordDomain(domain, content, id string) {
	isDomainEmpty(domain)

	updateContent := make(map[string]string)
	updateContent["content"] = content
	data, err := json.Marshal(updateContent)
	d := strings.NewReader(string(data))

	url := urlRecords + id
	req := setHeaders("PUT", url, d)
	httpClient := http.Client{}

	response, err := httpClient.Do(req)
	if err != nil {
		log.Fatal("updateRecord-Do: ", err)
	}
	defer response.Body.Close()

	resp, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal("updateRecord-ReadAll: ", err)
	}
	validateRecordUpdate(resp)
}

func getRecordDomain(domain, id string) {
	isDomainEmpty(domain)

	url := urlRecords + id
	r := setHeaders("GET", url, nil)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := make(map[string]Record)
	err = json.NewDecoder(response.Body).Decode(&dataResponse)
	if err != nil {
		log.Fatal("getRecordDomain-Decode: ", err)
	}
	stdoutHeader()
	stdoutRecord(dataResponse["record"])
}

func deleteRecordDomain(domain, id string) {
	isDomainEmpty(domain)

	url := urlRecords + id
	r := setHeaders("DELETE", url, nil)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := make(map[string]string)
	err = json.NewDecoder(response.Body).Decode(&dataResponse)
	if err != nil {
		log.Fatal("deleteRecordDomain-Decode: ", err)
	}

	// In case the record ID does not exist
	if msg, ok := dataResponse["message"]; ok {
		fmt.Println("", msg)
	}
}

func createRecordDomain(domain, name, content, recordType string) {
	isDomainEmpty(domain)

	re := make(map[string]Record)
	record := Record{}
	record.Content = content
	record.RecordType = recordType
	record.Name = name
	re["record"] = record

	dataJSON, err := json.Marshal(re)
	data := strings.NewReader(string(dataJSON))
	if err != nil {
		log.Fatal("createRecordDomain-Marshal: ", err)
	}

	r := setHeaders("POST", urlRecords, data)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := make(map[string]Record)
	resp, err := ioutil.ReadAll(response.Body)

	// JSON response differs for success, failure
	// and record already defined.
	validateRecordUpdate(resp)
	isRecordDefined(resp)
	err = json.NewDecoder(strings.NewReader(string(resp))).Decode(&dataResponse)
	if err != nil {
		log.Fatal("createRecordDomain-Decode: ", err)
	}

	stdoutHeader()
	stdoutRecord(dataResponse["record"])
}
<commit_msg>URL inside api implementation<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

// Struct holding a single record response.
type Record struct {
	Content      string `json:"content"`
	DomainID     int    `json:"domain_id"`
	ID           int
	Name         string `json:"name"`
	RecordType   string `json:"record_type"`
	SystemRecord bool   `json:"system_record"`
	TTL          int
}

// Listing records returns a slice of records.
type MultipleRecords []map[string]Record

func listRecordsDomain(domain string) {
	// If domain is empty, use the value from the cli.
	isDomainEmpty(domain)
	urlRecords := config.ApiURL + "/domains/" + config.Domain + "/records/"

	r := setHeaders("GET", urlRecords, nil)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := MultipleRecords{}
	err = json.NewDecoder(response.Body).Decode(&dataResponse)
	if err != nil {
		log.Fatal("Decode-Body: ", err)
	}

	filterRecords(dataResponse)
}

func updateRecordDomain(domain, content, id string) {
	isDomainEmpty(domain)
	urlRecords := config.ApiURL + "/domains/" + config.Domain + "/records/"

	updateContent := make(map[string]string)
	updateContent["content"] = content
	data, err := json.Marshal(updateContent)
	d := strings.NewReader(string(data))

	url := urlRecords + id
	req := setHeaders("PUT", url, d)
	httpClient := http.Client{}

	response, err := httpClient.Do(req)
	if err != nil {
		log.Fatal("updateRecord-Do: ", err)
	}
	defer response.Body.Close()

	resp, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal("updateRecord-ReadAll: ", err)
	}
	validateRecordUpdate(resp)
}

func getRecordDomain(domain, id string) {
	isDomainEmpty(domain)
	urlRecords := config.ApiURL + "/domains/" + config.Domain + "/records/"

	url := urlRecords + id
	r := setHeaders("GET", url, nil)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := make(map[string]Record)
	err = json.NewDecoder(response.Body).Decode(&dataResponse)
	if err != nil {
		log.Fatal("getRecordDomain-Decode: ", err)
	}
	stdoutHeader()
	stdoutRecord(dataResponse["record"])
}

func deleteRecordDomain(domain, id string) {
	isDomainEmpty(domain)
	urlRecords := config.ApiURL + "/domains/" + config.Domain + "/records/"

	url := urlRecords + id
	r := setHeaders("DELETE", url, nil)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := make(map[string]string)
	err = json.NewDecoder(response.Body).Decode(&dataResponse)
	if err != nil {
		log.Fatal("deleteRecordDomain-Decode: ", err)
	}

	// In case the record ID does not exist
	if msg, ok := dataResponse["message"]; ok {
		fmt.Println("", msg)
	}
}

func createRecordDomain(domain, name, content, recordType string) {
	isDomainEmpty(domain)
	urlRecords := config.ApiURL + "/domains/" + config.Domain + "/records/"

	re := make(map[string]Record)
	record := Record{}
	record.Content = content
	record.RecordType = recordType
	record.Name = name
	re["record"] = record

	dataJSON, err := json.Marshal(re)
	data := strings.NewReader(string(dataJSON))
	if err != nil {
		log.Fatal("createRecordDomain-Marshal: ", err)
	}

	r := setHeaders("POST", urlRecords, data)
	httpClient := http.Client{}

	response, err := httpClient.Do(r)
	if err != nil {
		log.Fatal("HTTPClient: ", err)
	}
	defer response.Body.Close()

	dataResponse := make(map[string]Record)
	resp, err := ioutil.ReadAll(response.Body)

	// JSON response differs for success, failure
	// and record already defined.
	validateRecordUpdate(resp)
	isRecordDefined(resp)
	err = json.NewDecoder(strings.NewReader(string(resp))).Decode(&dataResponse)
	if err != nil {
		log.Fatal("createRecordDomain-Decode: ", err)
	}

	stdoutHeader()
	stdoutRecord(dataResponse["record"])
}
<|endoftext|>


<commit_before>package fosite

import (
	"net/url"
	"time"
)

// Request is an implementation of Requester
type Request struct {
	RequestedAt   time.Time   `json:"requestedAt" gorethink:"requestedAt"`
	Client        Client      `json:"client" gorethink:"client"`
	Scopes        Arguments   `json:"scopes" gorethink:"scopes"`
	GrantedScopes Arguments   `json:"grantedScopes" gorethink:"grantedScopes"`
	Form          url.Values  `json:"form" gorethink:"form"`
	Session       interface{} `json:"session" gorethink:"session"`
}

func NewRequest() *Request {
	return &Request{
		Client:      &DefaultClient{},
		Scopes:      Arguments{},
		Form:        url.Values{},
		RequestedAt: time.Now(),
	}
}

func (a *Request) GetRequestForm() url.Values {
	return a.Form
}

func (a *Request) GetRequestedAt() time.Time {
	return a.RequestedAt
}

func (a *Request) GetClient() Client {
	return a.Client
}

func (a *Request) GetScopes() Arguments {
	return a.Scopes
}

func (a *Request) SetScopes(s Arguments) {
	a.Scopes = s
}

func (a *Request) GetGrantedScopes() Arguments {
	return a.GrantedScopes
}

func (a *Request) GrantScope(scope string) {
	a.GrantedScopes = append(a.GrantedScopes, scope)
}

func (a *Request) SetSession(session interface{}) {
	a.Session = session
}

func (a *Request) GetSession() interface{} {
	return a.Session
}

func (a *Request) Merge(request Requester) {
	for _, scope := range request.GetScopes() {
		a.Scopes = append(a.Scopes, scope)
	}
	for _, scope := range request.GetGrantedScopes() {
		a.GrantedScopes = append(a.GrantedScopes, scope)
	}
	a.RequestedAt = request.GetRequestedAt()
	a.Client = request.GetClient()
	a.Session = request.GetSession()

	for k, v := range request.GetRequestForm() {
		a.Form[k] = v
	}
}
<commit_msg>core: request should return unique scopes<commit_after>package fosite

import (
	"net/url"
	"time"
)

// Request is an implementation of Requester
type Request struct {
	RequestedAt   time.Time   `json:"requestedAt" gorethink:"requestedAt"`
	Client        Client      `json:"client" gorethink:"client"`
	Scopes        Arguments   `json:"scopes" gorethink:"scopes"`
	GrantedScopes Arguments   `json:"grantedScopes" gorethink:"grantedScopes"`
	Form          url.Values  `json:"form" gorethink:"form"`
	Session       interface{} `json:"session" gorethink:"session"`
}

func NewRequest() *Request {
	return &Request{
		Client:      &DefaultClient{},
		Scopes:      Arguments{},
		Form:        url.Values{},
		RequestedAt: time.Now(),
	}
}

func (a *Request) GetRequestForm() url.Values {
	return a.Form
}

func (a *Request) GetRequestedAt() time.Time {
	return a.RequestedAt
}

func (a *Request) GetClient() Client {
	return a.Client
}

func (a *Request) GetScopes() Arguments {
	return unique(a.Scopes)
}

func (a *Request) SetScopes(s Arguments) {
	a.Scopes = s
}

func (a *Request) GetGrantedScopes() Arguments {
	return unique(a.GrantedScopes)
}

func (a *Request) GrantScope(scope string) {
	a.GrantedScopes = append(a.GrantedScopes, scope)
}

func (a *Request) SetSession(session interface{}) {
	a.Session = session
}

func (a *Request) GetSession() interface{} {
	return a.Session
}

func (a *Request) Merge(request Requester) {
	for _, scope := range request.GetScopes() {
		a.Scopes = append(a.Scopes, scope)
	}
	for _, scope := range request.GetGrantedScopes() {
		a.GrantedScopes = append(a.GrantedScopes, scope)
	}
	a.RequestedAt = request.GetRequestedAt()
	a.Client = request.GetClient()
	a.Session = request.GetSession()

	for k, v := range request.GetRequestForm() {
		a.Form[k] = v
	}
}

func unique(col []string) []string {
	m := map[string]struct{}{}
	for _, v := range col {
		if _, ok := m[v]; !ok {
			m[v] = struct{}{}
		}
	}
	list := make([]string, len(m))

	i := 0
	for v := range m {
		list[i] = v
		i++
	}
	return list
}
<|endoftext|>


<commit_before>package sftp

import (
	"io"
	"os"
	"path"
	"syscall"
)

type Request struct {
	// Get, Put, SetStat, Stat, Rename, Remove
	// Rmdir, Mkdir, List, Readlink, Symlink
	Method   string
	Filepath string
	Pflags   uint32
	Attrs    []byte // convert to sub-struct
	Target   string // for renames and sym-links
	// packet data
	pkt_id uint32
	data   []byte
	length uint32
	// reader/writer from handlers
	put_writer io.Writer
	get_reader io.Reader
}

// Here mainly to specify that Filepath is required
func newRequest(path string) *Request {
	request := &Request{Filepath: path}
	return request
}

// called from worker to handle packet/request
func (r *Request) handle(handlers Handlers) (resp_packet, error) {
	var err error
	var rpkt resp_packet
	switch r.Method {
	case "Get":
		rpkt, err = fileget(handlers.FileGet, r)
	case "Put":
		rpkt, err = fileput(handlers.FilePut, r)
	case "SetStat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
		rpkt, err = filecmd(handlers.FileCmd, r)
	case "List", "Stat", "Readlink", "Realpath":
		rpkt, err = fileinfo(handlers.FileInfo, r)
	}
	return rpkt, err
}

// wrap FileReader handler
func fileget(h FileReader, r *Request) (resp_packet, error) {
	if r.get_reader == nil {
		reader, err := h.Fileread(r)
		if err != nil { return nil, syscall.EBADF }
		r.get_reader = reader
	}
	reader := r.get_reader
	data := make([]byte, clamp(r.length, maxTxPacket))
	n, err := reader.Read(data)
	if err != nil && (err != io.EOF || n == 0) { return nil, err }
	return &sshFxpDataPacket{
		ID:     r.pkt_id,
		Length: uint32(n),
		Data:   data[:n],
	}, nil
}

// wrap FileWriter handler
func fileput(h FileWriter, r *Request) (resp_packet, error) {
	if r.put_writer == nil {
		writer, err := h.Filewrite(r)
		if err != nil { return nil, syscall.EBADF }
		r.put_writer = writer
	}
	writer := r.put_writer

	_, err := writer.Write(r.data)
	if err != nil { return nil, err }
	return &sshFxpStatusPacket{
		ID: r.pkt_id,
		StatusError: StatusError{
			Code: ssh_FX_OK,
		}}, nil
}

// wrap FileCmder handler
func filecmd(h FileCmder, r *Request) (resp_packet, error) {
	err := h.Filecmd(r)
	if err != nil { return nil, err }
	return &sshFxpStatusPacket{
		ID: r.pkt_id,
		StatusError: StatusError{
			Code: ssh_FX_OK,
		}}, nil
}

// wrap FileInfoer handler
func fileinfo(h FileInfoer, r *Request) (resp_packet, error) {
	finfo, err := h.Fileinfo(r)
	if err != nil { return nil, err }

	switch r.Method {
	case "List":
		dirname := path.Base(r.Filepath)
		ret := &sshFxpNamePacket{ID: r.pkt_id}
		for _, fi := range finfo {
			ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
				Name:     fi.Name(),
				LongName: runLs(dirname, fi),
				Attrs:    []interface{}{fi},
			})
		}
		return ret, nil
	case "Stat":
		if len(finfo) == 0 {
			err = &os.PathError{"stat", r.Filepath, syscall.ENOENT}
			return nil, err
		}
		return &sshFxpStatResponse{
			ID:   r.pkt_id,
			info: finfo[0],
		}, nil
	case "Readlink", "Realpath":
		if len(finfo) == 0 {
			err = &os.PathError{"readlink", r.Filepath, syscall.ENOENT}
			return nil, err
		}
		return &sshFxpNamePacket{
			ID: r.pkt_id,
			NameAttrs: []sshFxpNameAttr{{
				Name:     finfo[0].Name(),
				LongName: finfo[0].Name(),
				Attrs:    emptyFileStat,
			}},
		}, nil
	}
	return nil, err
}

// populate attributes of request object from packet data
func (r *Request) populate(p interface{}) {
	// r.Filepath should already be set
	switch p := p.(type) {
	case *sshFxpSetstatPacket:
		r.Method = "Setstat"
		r.Pflags = p.Flags
		r.Attrs = p.Attrs.([]byte)
		r.pkt_id = p.id()
	case *sshFxpFsetstatPacket:
		r.Method = "Setstat"
		r.Pflags = p.Flags
		r.Attrs = p.Attrs.([]byte)
		r.pkt_id = p.id()
	case *sshFxpRenamePacket:
		r.Method = "Rename"
		r.Target = p.Newpath
		r.pkt_id = p.id()
	case *sshFxpSymlinkPacket:
		r.Method = "Symlink"
		r.Target = p.Linkpath
		r.pkt_id = p.id()
	case *sshFxpReadPacket:
		r.Method = "Get"
		r.length = p.Len
		r.pkt_id = p.id()
	case *sshFxpWritePacket:
		r.Method = "Put"
		r.data = p.Data
		r.length = p.Length
		r.pkt_id = p.id()
	case *sshFxpReaddirPacket:
		r.Method = "List"
		r.pkt_id = p.id()
	case *sshFxpRemovePacket:
		r.Method = "Remove"
		r.pkt_id = p.id()
	case *sshFxpRealpathPacket:
		r.Method = "Realpath"
		r.pkt_id = p.id()
	case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
		r.Method = "Stat"
		r.pkt_id = p.(packet).id()
	case *sshFxpRmdirPacket:
		r.Method = "Rmdir"
		r.pkt_id = p.id()
	case *sshFxpReadlinkPacket:
		r.Method = "Readlink"
		r.pkt_id = p.id()
	case *sshFxpMkdirPacket:
		r.Method = "Mkdir"
		r.pkt_id = p.id()
		//r.Attrs are ignored in ./packet.go
	}
}
<commit_msg>save EOF state of readdir<commit_after>package sftp

import (
	"io"
	"os"
	"path"
	"syscall"
)

type Request struct {
	// Get, Put, SetStat, Stat, Rename, Remove
	// Rmdir, Mkdir, List, Readlink, Symlink
	Method   string
	Filepath string
	Pflags   uint32
	Attrs    []byte // convert to sub-struct
	Target   string // for renames and sym-links
	// packet data
	pkt_id uint32
	data   []byte
	length uint32
	// reader/writer from handlers
	put_writer io.Writer
	get_reader io.Reader
	eof        bool // hack for readdir to keep eof state
}

// Here mainly to specify that Filepath is required
func newRequest(path string) *Request {
	request := &Request{Filepath: path}
	return request
}

// called from worker to handle packet/request
func (r *Request) handle(handlers Handlers) (resp_packet, error) {
	var err error
	var rpkt resp_packet
	switch r.Method {
	case "Get":
		rpkt, err = fileget(handlers.FileGet, r)
	case "Put":
		rpkt, err = fileput(handlers.FilePut, r)
	case "SetStat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
		rpkt, err = filecmd(handlers.FileCmd, r)
	case "List", "Stat", "Readlink", "Realpath":
		rpkt, err = fileinfo(handlers.FileInfo, r)
	}
	return rpkt, err
}

// wrap FileReader handler
func fileget(h FileReader, r *Request) (resp_packet, error) {
	if r.get_reader == nil {
		reader, err := h.Fileread(r)
		if err != nil { return nil, syscall.EBADF }
		r.get_reader = reader
	}
	reader := r.get_reader
	data := make([]byte, clamp(r.length, maxTxPacket))
	n, err := reader.Read(data)
	if err != nil && (err != io.EOF || n == 0) { return nil, err }
	return &sshFxpDataPacket{
		ID:     r.pkt_id,
		Length: uint32(n),
		Data:   data[:n],
	}, nil
}

// wrap FileWriter handler
func fileput(h FileWriter, r *Request) (resp_packet, error) {
	if r.put_writer == nil {
		writer, err := h.Filewrite(r)
		if err != nil { return nil, syscall.EBADF }
		r.put_writer = writer
	}
	writer := r.put_writer

	_, err := writer.Write(r.data)
	if err != nil { return nil, err }
	return &sshFxpStatusPacket{
		ID: r.pkt_id,
		StatusError: StatusError{
			Code: ssh_FX_OK,
		}}, nil
}

// wrap FileCmder handler
func filecmd(h FileCmder, r *Request) (resp_packet, error) {
	err := h.Filecmd(r)
	if err != nil { return nil, err }
	return &sshFxpStatusPacket{
		ID: r.pkt_id,
		StatusError: StatusError{
			Code: ssh_FX_OK,
		}}, nil
}

// wrap FileInfoer handler
func fileinfo(h FileInfoer, r *Request) (resp_packet, error) {
	if r.eof { return nil, io.EOF }
	finfo, err := h.Fileinfo(r)
	if err != nil { return nil, err }

	switch r.Method {
	case "List":
		dirname := path.Base(r.Filepath)
		ret := &sshFxpNamePacket{ID: r.pkt_id}
		for _, fi := range finfo {
			ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
				Name:     fi.Name(),
				LongName: runLs(dirname, fi),
				Attrs:    []interface{}{fi},
			})
		}
		r.eof = true
		return ret, nil
	case "Stat":
		if len(finfo) == 0 {
			err = &os.PathError{"stat", r.Filepath, syscall.ENOENT}
			return nil, err
		}
		return &sshFxpStatResponse{
			ID:   r.pkt_id,
			info: finfo[0],
		}, nil
	case "Readlink", "Realpath":
		if len(finfo) == 0 {
			err = &os.PathError{"readlink", r.Filepath, syscall.ENOENT}
			return nil, err
		}
		return &sshFxpNamePacket{
			ID: r.pkt_id,
			NameAttrs: []sshFxpNameAttr{{
				Name:     finfo[0].Name(),
				LongName: finfo[0].Name(),
				Attrs:    emptyFileStat,
			}},
		}, nil
	}
	return nil, err
}

// populate attributes of request object from packet data
func (r *Request) populate(p interface{}) {
	// r.Filepath should already be set
	switch p := p.(type) {
	case *sshFxpSetstatPacket:
		r.Method = "Setstat"
		r.Pflags = p.Flags
		r.Attrs = p.Attrs.([]byte)
		r.pkt_id = p.id()
	case *sshFxpFsetstatPacket:
		r.Method = "Setstat"
		r.Pflags = p.Flags
		r.Attrs = p.Attrs.([]byte)
		r.pkt_id = p.id()
	case *sshFxpRenamePacket:
		r.Method = "Rename"
		r.Target = p.Newpath
		r.pkt_id = p.id()
	case *sshFxpSymlinkPacket:
		r.Method = "Symlink"
		r.Target = p.Linkpath
		r.pkt_id = p.id()
	case *sshFxpReadPacket:
		r.Method = "Get"
		r.length = p.Len
		r.pkt_id = p.id()
	case *sshFxpWritePacket:
		r.Method = "Put"
		r.data = p.Data
		r.length = p.Length
		r.pkt_id = p.id()
	case *sshFxpReaddirPacket:
		r.Method = "List"
		r.pkt_id = p.id()
	case *sshFxpRemovePacket:
		r.Method = "Remove"
		r.pkt_id = p.id()
	case *sshFxpRealpathPacket:
		r.Method = "Realpath"
		r.pkt_id = p.id()
	case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
		r.Method = "Stat"
		r.pkt_id = p.(packet).id()
	case *sshFxpRmdirPacket:
		r.Method = "Rmdir"
		r.pkt_id = p.id()
	case *sshFxpReadlinkPacket:
		r.Method = "Readlink"
		r.pkt_id = p.id()
	case *sshFxpMkdirPacket:
		r.Method = "Mkdir"
		r.pkt_id = p.id()
		//r.Attrs are ignored in ./packet.go
	}
}
<|endoftext|>


<commit_before>package webapi

import (
	"net/http"
)

type Request struct {
	http.Request
	params map[string]string
}

func WrapHttpRequest(r *http.Request) Request {
	params := make(map[string]string)
	return Request{*r, params}
}

func (r *Request) Param(name string) string {
	return r.params[name]
}
<commit_msg>Remove WrapHttpRequest function<commit_after>package webapi

import (
	"net/http"
)

type Request struct {
	http.Request
	params map[string]string
}

func (r *Request) Param(name string) string {
	return r.params[name]
}
<|endoftext|>


<commit_before>package goinsta

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type reqOptions struct {
	// Endpoint is the request path of instagram api
	Endpoint string

	// IsPost setted to true will send request with POST method.
	//
	// By default this option is false.
	IsPost bool

	// Query is the parameters of the request
	//
	// This parameters are independents of the request method (POST|GET)
	Query map[string]string
}

func (insta *Instagram) sendSimpleRequest(uri string, a ...interface{}) (body []byte, err error) {
	return insta.sendRequest(&reqOptions{
		Endpoint: fmt.Sprintf(uri, a...),
	})
}

func (inst *Instagram) sendRequest(o *reqOptions) (body []byte, err error) {
	if !inst.isLoggedIn && !o.IsLoggedIn {
		return nil, ErrLoggedOut
	}

	method := "GET"
	if o.IsPost {
		method = "POST"
	}

	u, err := url.Parse(goInstaAPIUrl + o.Endpoint)
	if err != nil {
		return nil, err
	}

	bf := bytes.NewBuffer([]byte{})

	q := u.Query()
	for k, v := range o.Query {
		q.Add(k, v)
	}

	if o.IsPost {
		bf.WriteString(q.Encode())
	} else {
		u.RawQuery = q.Encode()
	}

	var req *http.Request
	req, err = http.NewRequest(method, u.String(), bf)
	if err != nil {
		return
	}

	req.Header.Set("Connection", "close")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("Cookie2", "$Version=1")
	req.Header.Set("Accept-Language", "en-US")
	req.Header.Set("User-Agent", goInstaUserAgent)

	resp, err := client.Do(req)
	if err != nil {
		return body, err
	}
	defer resp.Body.Close()

	u, _ = url.Parse(goInstaAPIUrl)
	for _, value := range inst.c.Jar.Cookies(u) {
		if strings.Contains(value.Name, "csrftoken") {
			inst.token = value.Value
		}
	}

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	if resp.StatusCode != 200 {
		e := fmt.Errorf("Invalid status code %s", string(body))
		switch resp.StatusCode {
		case 400:
			e = ErrLoggedOut
		case 404:
			e = ErrNotFound
		}
		return nil, e
	}

	return body, err
}

func (insta *Instagram) prepareData(otherData ...map[string]interface{}) (string, error) {
	data := map[string]interface{}{
		"_uuid":      insta.uuid,
		"_uid":       insta.CurrentUser.ID,
		"_csrftoken": insta.token,
	}
	if len(otherData) > 0 {
		for i := range otherData {
			for key, value := range otherData[i] {
				data[key] = value
			}
		}
	}
	bytes, err := json.Marshal(data)
	return string(bytes), err
}
<commit_msg>Deleted uneeded len()<commit_after>package goinsta

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type reqOptions struct {
	// Endpoint is the request path of instagram api
	Endpoint string

	// IsPost setted to true will send request with POST method.
	//
	// By default this option is false.
	IsPost bool

	// Query is the parameters of the request
	//
	// This parameters are independents of the request method (POST|GET)
	Query map[string]string
}

func (insta *Instagram) sendSimpleRequest(uri string, a ...interface{}) (body []byte, err error) {
	return insta.sendRequest(&reqOptions{
		Endpoint: fmt.Sprintf(uri, a...),
	})
}

func (inst *Instagram) sendRequest(o *reqOptions) (body []byte, err error) {
	if !inst.isLoggedIn && !o.IsLoggedIn {
		return nil, ErrLoggedOut
	}

	method := "GET"
	if o.IsPost {
		method = "POST"
	}

	u, err := url.Parse(goInstaAPIUrl + o.Endpoint)
	if err != nil {
		return nil, err
	}

	bf := bytes.NewBuffer([]byte{})

	q := u.Query()
	for k, v := range o.Query {
		q.Add(k, v)
	}

	if o.IsPost {
		bf.WriteString(q.Encode())
	} else {
		u.RawQuery = q.Encode()
	}

	var req *http.Request
	req, err = http.NewRequest(method, u.String(), bf)
	if err != nil {
		return
	}

	req.Header.Set("Connection", "close")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("Cookie2", "$Version=1")
	req.Header.Set("Accept-Language", "en-US")
	req.Header.Set("User-Agent", goInstaUserAgent)

	resp, err := client.Do(req)
	if err != nil {
		return body, err
	}
	defer resp.Body.Close()

	u, _ = url.Parse(goInstaAPIUrl)
	for _, value := range inst.c.Jar.Cookies(u) {
		if strings.Contains(value.Name, "csrftoken") {
			inst.token = value.Value
		}
	}

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	if resp.StatusCode != 200 {
		e := fmt.Errorf("Invalid status code %s", string(body))
		switch resp.StatusCode {
		case 400:
			e = ErrLoggedOut
		case 404:
			e = ErrNotFound
		}
		return nil, e
	}

	return body, err
}

func (insta *Instagram) prepareData(otherData ...map[string]interface{}) (string, error) {
	data := map[string]interface{}{
		"_uuid":      insta.uuid,
		"_uid":       insta.CurrentUser.ID,
		"_csrftoken": insta.token,
	}
	for i := range otherData {
		for key, value := range otherData[i] {
			data[key] = value
		}
	}
	bytes, err := json.Marshal(data)
	return string(bytes), err
}
<|endoftext|>


<commit_before>package grequests

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"errors"
	"io"
	"mime"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	// Default value for net.Dialer Timeout
	dialTimeout = 30 * time.Second

	// Default value for net.Dialer KeepAlive
	dialKeepAlive = 30 * time.Second

	// Default value for http.Transport TLSHandshakeTimeout
	tslHandshakeTimeout = 10 * time.Second
)

// RequestOptions is the location that of where the data
type RequestOptions struct {

	// Data is a map of key values that will eventually convert into the query string of a GET request or the
	// body of a POST request.
	Data map[string]string

	// Params is a map of query strings that may be used within a GET request
	Params map[string]string

	// Files is where you can include files to upload. The use of this data structure is limited to POST requests
	File *FileUpload

	// JSON can be used when you wish to send JSON within the request body
	JSON interface{}

	// XML can be used if you wish to send XML within the request body
	XML interface{}

	// If you want to add custom HTTP headers to the request, this is your friend
	Headers map[string]string

	// InsecureSkipVerify is a flag that specifies if we should validate the server's TLS certificate. It should be noted that
	// Go's TLS verify mechanism doesn't validate if a certificate has been revoked
	InsecureSkipVerify bool

	// DisableCompression will disable gzip compression on requests
	DisableCompression bool

	// UserAgent allows you to set an arbitrary custom user agent
	UserAgent string

	// Auth allows you to specify a user name and password that you wish to use when requesting
	// the URL. It will use basic HTTP authentication formatting the username and password in base64
	// the format is []string{username, password}
	Auth []string

	// IsAjax is a flag that can be set to make the request appear to be generated by browser Javascript
	IsAjax bool

	// Cookies is an array of `http.Cookie` that allows you to attach cookies to your request
	Cookies []http.Cookie

	// Proxies is a map in the following format *protocol* => proxy address e.g http => http://127.0.0.1:8080
	Proxies map[string]*url.URL

	// TLSHandshakeTimeout specifies the maximum amount of time waiting to
	// wait for a TLS handshake. Zero means no timeout.
	TLSHandshakeTimeout time.Duration

	// DialTimeout is the maximum amount of time a dial will wait for
	// a connect to complete.
	DialTimeout time.Duration

	// KeepAlive specifies the keep-alive period for an active
	// network connection. If zero, keep-alives are not enabled.
	DialKeepAlive time.Duration
}

func doRequest(requestVerb, url string, ro *RequestOptions) (*Response, error) {
	return buildResponse(buildRequest(requestVerb, url, ro))
}

func doAsyncRequest(requestVerb, url string, ro *RequestOptions) chan *Response {
	responseChan := make(chan *Response, 1)
	go func() {
		resp, _ := doRequest(requestVerb, url, ro) // Error is already within the channel
		responseChan <- resp
	}()
	return responseChan
}

// buildRequest is where most of the magic happens for request processing
func buildRequest(httpMethod, url string, ro *RequestOptions) (*http.Response, error) {
	if ro == nil {
		ro = &RequestOptions{}
	}
	// Create our own HTTP client
	httpClient := buildHTTPClient(ro)

	// Build our URL
	var err error

	if ro.Params != nil {
		url, err = buildURLParams(url, ro.Params)

		if err != nil {
			return nil, err
		}

	}

	// Build the request
	req, err := buildHTTPRequest(httpMethod, url, ro)

	if err != nil {
		return nil, err
	}

	// Do we need to add any HTTP headers or Basic Auth?
	addHTTPHeaders(ro, req)

	addCookies(ro, req)

	return httpClient.Do(req)
}

func buildHTTPRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	if ro.JSON != nil {
		return createBasicJSONRequest(httpMethod, userURL, ro)
	}

	if ro.XML != nil {
		return createBasicXMLRequest(httpMethod, userURL, ro)
	}

	if ro.File != nil {
		return createFileUploadRequest(httpMethod, userURL, ro)
	}

	if ro.Data != nil {
		return createBasicRequest(httpMethod, userURL, ro)
	}

	return http.NewRequest(httpMethod, userURL, nil)
}

func createFileUploadRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	if httpMethod == "POST" {
		return createMultiPartPostRequest(httpMethod, userURL, ro)
	}

	// This may be a PUT or PATCH request so we will just put the raw
	// io.ReadCloser in the request body
	// and guess the MIME type from the file name

	// At the moment, we will only support 1 file upload as a time
	// when uploading using PUT or PATCH

	req, err := http.NewRequest(httpMethod, userURL, ro.File.FileContents)

	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", mime.TypeByExtension(ro.File.FileName))

	return req, nil

}

func createBasicXMLRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	tempBuffer := &bytes.Buffer{}

	if err := xml.NewEncoder(tempBuffer).Encode(ro.XML); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(httpMethod, userURL, tempBuffer)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/xml")

	return req, nil

}
func createMultiPartPostRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	requestBody := &bytes.Buffer{}

	multipartWriter := multipart.NewWriter(requestBody)
	writer, err := multipartWriter.CreateFormFile("file", ro.File.FileName)

	if err != nil {
		return nil, err
	}

	if ro.File.FileContents == nil {
		return nil, errors.New("pointer FileContents cannot be nil")
	}

	if _, err = io.Copy(writer, ro.File.FileContents); err != nil && err != io.EOF {
		return nil, err
	}

	defer ro.File.FileContents.Close()

	// Populate the other parts of the form (if there are any)
	for key, value := range ro.Data {
		multipartWriter.WriteField(key, value)
	}

	if err = multipartWriter.Close(); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(httpMethod, userURL, requestBody)

	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", multipartWriter.FormDataContentType())

	return req, err
}

func createBasicJSONRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {

	tempBuffer := &bytes.Buffer{}

	if err := json.NewEncoder(tempBuffer).Encode(ro.JSON); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(httpMethod, userURL, tempBuffer)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")

	return req, nil

}
func createBasicRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {

	req, err := http.NewRequest(httpMethod, userURL, strings.NewReader(encodePostValues(ro.Data)))

	if err != nil {
		return nil, err
	}
	// The content type must be set to a regular form
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	return req, nil
}

func encodePostValues(postValues map[string]string) string {
	urlValues := &url.Values{}

	for key, value := range postValues {
		urlValues.Set(key, value)
	}

	return urlValues.Encode() // This will sort all of the string values
}

// proxySettings will default to the default proxy settings if none are provided
// if settings are provided – they will override the environment variables
func (ro RequestOptions) proxySettings(req *http.Request) (*url.URL, error) {
	// No proxies – lets use the default
	if len(ro.Proxies) == 0 {
		return http.ProxyFromEnvironment(req)
	}

	// There was a proxy specified – do we support the protocol?
	if _, ok := ro.Proxies[req.URL.Scheme]; ok {
		return ro.Proxies[req.URL.Scheme], nil
	}

	// Proxies were specified but not for any protocol that we use
	return http.ProxyFromEnvironment(req)

}

// useDefaultClient will tell the "client creator" if a custom client is needed
// it checks the following items (and will create a custom client of these are)
// true
// 1. Do we want to accept invalid SSL certificates?
// 2. Do we want to disable compression?
// 3. Do we want a custom proxy?
// 4. Do we want to change the default timeout for TLS Handshake?
// 5. Do we want to change the default request timeout?
// 6. Do we want to change the default connection timeout?
func (ro RequestOptions) dontUseDefaultClient() bool {
	return ro.InsecureSkipVerify == true ||
		ro.DisableCompression == true ||
		len(ro.Proxies) != 0 ||
		ro.TLSHandshakeTimeout != 0 ||
		ro.DialTimeout != 0 ||
		ro.DialKeepAlive != 0
}

func buildHTTPClient(ro *RequestOptions) *http.Client {
	// Does the user want to change the defaults?
	if !ro.dontUseDefaultClient() {
		return http.DefaultClient
	}

	// Using the user config for tls timeout or default
	if ro.TLSHandshakeTimeout == 0 {
		ro.TLSHandshakeTimeout = tslHandshakeTimeout
	}

	// Using the user config for dial timeout or default
	if ro.DialTimeout == 0 {
		ro.DialTimeout = dialTimeout
	}

	// Using the user config for dial keep alive or default
	if ro.DialKeepAlive == 0 {
		ro.DialKeepAlive = dialKeepAlive
	}

	return &http.Client{
		Transport: &http.Transport{
			// These are borrowed from the default transporter
			Proxy: ro.proxySettings,
			Dial: (&net.Dialer{
				Timeout:   ro.DialTimeout,
				KeepAlive: ro.DialKeepAlive,
			}).Dial,
			TLSHandshakeTimeout: ro.TLSHandshakeTimeout,

			// Here comes the user settings
			TLSClientConfig:    &tls.Config{InsecureSkipVerify: ro.InsecureSkipVerify == true},
			DisableCompression: ro.DisableCompression,
		},
	}
}

// buildURLParams returns a URL with all of the params
// Note: This function will override current URL params if they contradict what is provided in the map
// That is what the "magic" is on the last line
func buildURLParams(userURL string, params map[string]string) (string, error) {
	parsedURL, err := url.Parse(userURL)

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

	parsedQuery, err := url.ParseQuery(parsedURL.RawQuery)

	for key, value := range params {
		parsedQuery.Set(key, value)
	}

	return strings.Join(
		[]string{strings.Replace(parsedURL.String(),
			"?"+parsedURL.RawQuery, "", -1),
			parsedQuery.Encode()},
		"?"), nil
}

// addHTTPHeaders adds any additional HTTP headers that need to be added are added here including:
// 1. Custom User agent
// 2. Authorization Headers
// 3. Any other header requested
func addHTTPHeaders(ro *RequestOptions, req *http.Request) {
	for key, value := range ro.Headers {
		req.Header.Set(key, value)
	}

	if ro.UserAgent != "" {
		req.Header.Set("User-Agent", ro.UserAgent)
	} else {
		req.Header.Set("User-Agent", localUserAgent)
	}

	if ro.Auth != nil {
		req.SetBasicAuth(ro.Auth[0], ro.Auth[1])
	}

	if ro.IsAjax == true {
		req.Header.Set("X-Requested-With", "XMLHttpRequest")
	}
}

func addCookies(ro *RequestOptions, req *http.Request) {
	for _, c := range ro.Cookies {
		req.AddCookie(&c)
	}
}
<commit_msg>minor improvements<commit_after>package grequests

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"errors"
	"io"
	"mime"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	// Default value for net.Dialer Timeout
	dialTimeout = 30 * time.Second

	// Default value for net.Dialer KeepAlive
	dialKeepAlive = 30 * time.Second

	// Default value for http.Transport TLSHandshakeTimeout
	tslHandshakeTimeout = 10 * time.Second
)

// RequestOptions is the location that of where the data
type RequestOptions struct {

	// Data is a map of key values that will eventually convert into the query string of a GET request or the
	// body of a POST request.
	Data map[string]string

	// Params is a map of query strings that may be used within a GET request
	Params map[string]string

	// Files is where you can include files to upload. The use of this data structure is limited to POST requests
	File *FileUpload

	// JSON can be used when you wish to send JSON within the request body
	JSON interface{}

	// XML can be used if you wish to send XML within the request body
	XML interface{}

	// If you want to add custom HTTP headers to the request, this is your friend
	Headers map[string]string

	// InsecureSkipVerify is a flag that specifies if we should validate the server's TLS certificate. It should be noted that
	// Go's TLS verify mechanism doesn't validate if a certificate has been revoked
	InsecureSkipVerify bool

	// DisableCompression will disable gzip compression on requests
	DisableCompression bool

	// UserAgent allows you to set an arbitrary custom user agent
	UserAgent string

	// Auth allows you to specify a user name and password that you wish to use when requesting
	// the URL. It will use basic HTTP authentication formatting the username and password in base64
	// the format is []string{username, password}
	Auth []string

	// IsAjax is a flag that can be set to make the request appear to be generated by browser Javascript
	IsAjax bool

	// Cookies is an array of `http.Cookie` that allows you to attach cookies to your request
	Cookies []http.Cookie

	// Proxies is a map in the following format *protocol* => proxy address e.g http => http://127.0.0.1:8080
	Proxies map[string]*url.URL

	// TLSHandshakeTimeout specifies the maximum amount of time waiting to
	// wait for a TLS handshake. Zero means no timeout.
	TLSHandshakeTimeout time.Duration

	// DialTimeout is the maximum amount of time a dial will wait for
	// a connect to complete.
	DialTimeout time.Duration

	// KeepAlive specifies the keep-alive period for an active
	// network connection. If zero, keep-alives are not enabled.
	DialKeepAlive time.Duration
}

func doRequest(requestVerb, url string, ro *RequestOptions) (*Response, error) {
	return buildResponse(buildRequest(requestVerb, url, ro))
}

func doAsyncRequest(requestVerb, url string, ro *RequestOptions) chan *Response {
	responseChan := make(chan *Response, 1)
	go func() {
		resp, _ := doRequest(requestVerb, url, ro) // Error is already within the channel
		responseChan <- resp
	}()
	return responseChan
}

// buildRequest is where most of the magic happens for request processing
func buildRequest(httpMethod, url string, ro *RequestOptions) (*http.Response, error) {
	if ro == nil {
		ro = &RequestOptions{}
	}
	// Create our own HTTP client
	httpClient := buildHTTPClient(ro)

	// Build our URL
	url, err := buildURLParams(url, ro.Params)

	if err != nil {
		return nil, err
	}

	// Build the request
	req, err := buildHTTPRequest(httpMethod, url, ro)

	if err != nil {
		return nil, err
	}

	// Do we need to add any HTTP headers or Basic Auth?
	addHTTPHeaders(ro, req)
	addCookies(ro, req)

	return httpClient.Do(req)
}

func buildHTTPRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	if ro.JSON != nil {
		return createBasicJSONRequest(httpMethod, userURL, ro)
	}

	if ro.XML != nil {
		return createBasicXMLRequest(httpMethod, userURL, ro)
	}

	if ro.File != nil {
		return createFileUploadRequest(httpMethod, userURL, ro)
	}

	if ro.Data != nil {
		return createBasicRequest(httpMethod, userURL, ro)
	}

	return http.NewRequest(httpMethod, userURL, nil)
}

func createFileUploadRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	if httpMethod == "POST" {
		return createMultiPartPostRequest(httpMethod, userURL, ro)
	}

	// This may be a PUT or PATCH request so we will just put the raw
	// io.ReadCloser in the request body
	// and guess the MIME type from the file name

	// At the moment, we will only support 1 file upload as a time
	// when uploading using PUT or PATCH

	req, err := http.NewRequest(httpMethod, userURL, ro.File.FileContents)

	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", mime.TypeByExtension(ro.File.FileName))

	return req, nil

}

func createBasicXMLRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	tempBuffer := &bytes.Buffer{}

	if err := xml.NewEncoder(tempBuffer).Encode(ro.XML); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(httpMethod, userURL, tempBuffer)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/xml")

	return req, nil

}
func createMultiPartPostRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {
	requestBody := &bytes.Buffer{}

	multipartWriter := multipart.NewWriter(requestBody)
	writer, err := multipartWriter.CreateFormFile("file", ro.File.FileName)

	if err != nil {
		return nil, err
	}

	if ro.File.FileContents == nil {
		return nil, errors.New("pointer FileContents cannot be nil")
	}

	if _, err = io.Copy(writer, ro.File.FileContents); err != nil && err != io.EOF {
		return nil, err
	}

	defer ro.File.FileContents.Close()

	// Populate the other parts of the form (if there are any)
	for key, value := range ro.Data {
		multipartWriter.WriteField(key, value)
	}

	if err = multipartWriter.Close(); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(httpMethod, userURL, requestBody)

	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", multipartWriter.FormDataContentType())

	return req, err
}

func createBasicJSONRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {

	tempBuffer := &bytes.Buffer{}

	if err := json.NewEncoder(tempBuffer).Encode(ro.JSON); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(httpMethod, userURL, tempBuffer)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")

	return req, nil

}
func createBasicRequest(httpMethod, userURL string, ro *RequestOptions) (*http.Request, error) {

	req, err := http.NewRequest(httpMethod, userURL, strings.NewReader(encodePostValues(ro.Data)))

	if err != nil {
		return nil, err
	}
	// The content type must be set to a regular form
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	return req, nil
}

func encodePostValues(postValues map[string]string) string {
	urlValues := &url.Values{}

	for key, value := range postValues {
		urlValues.Set(key, value)
	}

	return urlValues.Encode() // This will sort all of the string values
}

// proxySettings will default to the default proxy settings if none are provided
// if settings are provided – they will override the environment variables
func (ro RequestOptions) proxySettings(req *http.Request) (*url.URL, error) {
	// No proxies – lets use the default
	if len(ro.Proxies) == 0 {
		return http.ProxyFromEnvironment(req)
	}

	// There was a proxy specified – do we support the protocol?
	if _, ok := ro.Proxies[req.URL.Scheme]; ok {
		return ro.Proxies[req.URL.Scheme], nil
	}

	// Proxies were specified but not for any protocol that we use
	return http.ProxyFromEnvironment(req)

}

// useDefaultClient will tell the "client creator" if a custom client is needed
// it checks the following items (and will create a custom client of these are)
// true
// 1. Do we want to accept invalid SSL certificates?
// 2. Do we want to disable compression?
// 3. Do we want a custom proxy?
// 4. Do we want to change the default timeout for TLS Handshake?
// 5. Do we want to change the default request timeout?
// 6. Do we want to change the default connection timeout?
func (ro RequestOptions) dontUseDefaultClient() bool {
	return ro.InsecureSkipVerify == true ||
		ro.DisableCompression == true ||
		len(ro.Proxies) != 0 ||
		ro.TLSHandshakeTimeout != 0 ||
		ro.DialTimeout != 0 ||
		ro.DialKeepAlive != 0
}

func buildHTTPClient(ro *RequestOptions) *http.Client {
	// Does the user want to change the defaults?
	if !ro.dontUseDefaultClient() {
		return http.DefaultClient
	}

	// Using the user config for tls timeout or default
	if ro.TLSHandshakeTimeout == 0 {
		ro.TLSHandshakeTimeout = tslHandshakeTimeout
	}

	// Using the user config for dial timeout or default
	if ro.DialTimeout == 0 {
		ro.DialTimeout = dialTimeout
	}

	// Using the user config for dial keep alive or default
	if ro.DialKeepAlive == 0 {
		ro.DialKeepAlive = dialKeepAlive
	}

	return &http.Client{
		Transport: &http.Transport{
			// These are borrowed from the default transporter
			Proxy: ro.proxySettings,
			Dial: (&net.Dialer{
				Timeout:   ro.DialTimeout,
				KeepAlive: ro.DialKeepAlive,
			}).Dial,
			TLSHandshakeTimeout: ro.TLSHandshakeTimeout,

			// Here comes the user settings
			TLSClientConfig:    &tls.Config{InsecureSkipVerify: ro.InsecureSkipVerify},
			DisableCompression: ro.DisableCompression,
		},
	}
}

// buildURLParams returns a URL with all of the params
// Note: This function will override current URL params if they contradict what is provided in the map
// That is what the "magic" is on the last line
func buildURLParams(userURL string, params map[string]string) (string, error) {
	parsedURL, err := url.Parse(userURL)

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

	parsedQuery, err := url.ParseQuery(parsedURL.RawQuery)

	for key, value := range params {
		parsedQuery.Set(key, value)
	}

	return strings.Join(
		[]string{strings.Replace(parsedURL.String(),
			"?"+parsedURL.RawQuery, "", -1),
			parsedQuery.Encode()},
		"?"), nil
}

// addHTTPHeaders adds any additional HTTP headers that need to be added are added here including:
// 1. Custom User agent
// 2. Authorization Headers
// 3. Any other header requested
func addHTTPHeaders(ro *RequestOptions, req *http.Request) {
	for key, value := range ro.Headers {
		req.Header.Set(key, value)
	}

	if ro.UserAgent != "" {
		req.Header.Set("User-Agent", ro.UserAgent)
	} else {
		req.Header.Set("User-Agent", localUserAgent)
	}

	if ro.Auth != nil {
		req.SetBasicAuth(ro.Auth[0], ro.Auth[1])
	}

	if ro.IsAjax == true {
		req.Header.Set("X-Requested-With", "XMLHttpRequest")
	}
}

func addCookies(ro *RequestOptions, req *http.Request) {
	for _, c := range ro.Cookies {
		req.AddCookie(&c)
	}
}
<|endoftext|>


<commit_before>package canopus

import (
	. "github.com/zubairhamed/go-commons/network"
	"net"
	"strconv"
	"strings"
)

func NewRequest(messageType uint8, messageMethod CoapCode, messageId uint16) *CoapRequest {
	msg := NewMessage(messageType, messageMethod, messageId)
	msg.Token = []byte(GenerateToken(8))

	return &CoapRequest{
		msg: msg,
	}
}

func NewRequestFromMessage(msg *Message, attrs map[string]string, conn *net.UDPConn, addr *net.UDPAddr) *CoapRequest {
	return &CoapRequest{
		msg:   msg,
		attrs: attrs,
		conn:  conn,
		addr:  addr,
	}
}

type CoapRequest struct {
	msg   *Message
	attrs map[string]string
	conn  *net.UDPConn
	addr  *net.UDPAddr
}

func (c *CoapRequest) GetConnection() *net.UDPConn {
	return c.conn
}

func (c *CoapRequest) GetAddress() *net.UDPAddr {
	return c.addr
}

func (c *CoapRequest) GetAttributes() map[string]string {
	return c.attrs
}

func (c *CoapRequest) GetAttribute(o string) string {
	return c.attrs[o]
}

func (c *CoapRequest) GetAttributeAsInt(o string) int {
	attr := c.GetAttribute(o)
	i, _ := strconv.Atoi(attr)

	return i
}

func (c *CoapRequest) GetRequestURI() {

}

func (c *CoapRequest) GetOption() {

}

func (c *CoapRequest) GetOptions() {

}

func (c *CoapRequest) GetMessage() *Message {
	return c.msg
}

func (c *CoapRequest) GetMethod() {

}

func (c *CoapRequest) GetError() {

}

func (c *CoapRequest) SetStringPayload(s string) {
	c.msg.Payload = NewPlainTextPayload(s)
}

func (c *CoapRequest) SetRequestURI(uri string) {
	c.msg.AddOptions(NewPathOptions(uri))
}

func (c *CoapRequest) SetConfirmable(con bool) {
	if con {
		c.msg.MessageType = TYPE_CONFIRMABLE
	} else {
		c.msg.MessageType = TYPE_NONCONFIRMABLE
	}
}

func (c *CoapRequest) SetToken(t string) {
	c.msg.Token = []byte(t)
}

func (c *CoapRequest) IncrementMessageId() {
	c.msg.MessageId = c.msg.MessageId + 1
}

func (c *CoapRequest) GetUriQuery(q string) string {
	qs := c.GetMessage().GetOptionsAsString(OPTION_URI_QUERY)

	for _, o := range qs {
		ps := strings.Split(o, "=")
		if len(ps) == 2 {
			if ps[0] == q {
				return ps[1]
			}
		}
	}
	return ""
}

func (c *CoapRequest) SetUriQuery(k string, v string) {
	c.GetMessage().AddOption(OPTION_URI_QUERY, k+"="+v)
}
<commit_msg>CoapRequest#SetMediaType method<commit_after>package canopus

import (
	. "github.com/zubairhamed/go-commons/network"
	"net"
	"strconv"
	"strings"
)

func NewRequest(messageType uint8, messageMethod CoapCode, messageId uint16) *CoapRequest {
	msg := NewMessage(messageType, messageMethod, messageId)
	msg.Token = []byte(GenerateToken(8))

	return &CoapRequest{
		msg: msg,
	}
}

func NewRequestFromMessage(msg *Message, attrs map[string]string, conn *net.UDPConn, addr *net.UDPAddr) *CoapRequest {
	return &CoapRequest{
		msg:   msg,
		attrs: attrs,
		conn:  conn,
		addr:  addr,
	}
}

type CoapRequest struct {
	msg   *Message
	attrs map[string]string
	conn  *net.UDPConn
	addr  *net.UDPAddr
}

func (c *CoapRequest) SetMediaType(mt MediaType) {
	c.msg.AddOption(OPTION_CONTENT_FORMAT, mt)
}

func (c *CoapRequest) GetConnection() *net.UDPConn {
	return c.conn
}

func (c *CoapRequest) GetAddress() *net.UDPAddr {
	return c.addr
}

func (c *CoapRequest) GetAttributes() map[string]string {
	return c.attrs
}

func (c *CoapRequest) GetAttribute(o string) string {
	return c.attrs[o]
}

func (c *CoapRequest) GetAttributeAsInt(o string) int {
	attr := c.GetAttribute(o)
	i, _ := strconv.Atoi(attr)

	return i
}

func (c *CoapRequest) GetRequestURI() {

}

func (c *CoapRequest) GetOption() {

}

func (c *CoapRequest) GetOptions() {

}

func (c *CoapRequest) GetMessage() *Message {
	return c.msg
}

func (c *CoapRequest) GetMethod() {

}

func (c *CoapRequest) GetError() {

}

func (c *CoapRequest) SetStringPayload(s string) {
	c.msg.Payload = NewPlainTextPayload(s)
}

func (c *CoapRequest) SetRequestURI(uri string) {
	c.msg.AddOptions(NewPathOptions(uri))
}

func (c *CoapRequest) SetConfirmable(con bool) {
	if con {
		c.msg.MessageType = TYPE_CONFIRMABLE
	} else {
		c.msg.MessageType = TYPE_NONCONFIRMABLE
	}
}

func (c *CoapRequest) SetToken(t string) {
	c.msg.Token = []byte(t)
}

func (c *CoapRequest) IncrementMessageId() {
	c.msg.MessageId = c.msg.MessageId + 1
}

func (c *CoapRequest) GetUriQuery(q string) string {
	qs := c.GetMessage().GetOptionsAsString(OPTION_URI_QUERY)

	for _, o := range qs {
		ps := strings.Split(o, "=")
		if len(ps) == 2 {
			if ps[0] == q {
				return ps[1]
			}
		}
	}
	return ""
}

func (c *CoapRequest) SetUriQuery(k string, v string) {
	c.GetMessage().AddOption(OPTION_URI_QUERY, k+"="+v)
}
<|endoftext|>


<commit_before>package jsonapi

import (
	"net/http"
	"strconv"
	"strings"
)

// An Intent represents a valid combination of a request method and a URL pattern.
type Intent int

const (
	_ Intent = iota

	// ListResources is a variation of the following request:
	// GET /posts
	ListResources

	// FindResource is a variation of the following request:
	// GET /posts/1
	FindResource

	// CreateResource is a variation of the following request:
	// POST /posts
	CreateResource

	// UpdateResource is a variation of the following request:
	// PATCH /posts/1
	UpdateResource

	// DeleteResource is a variation of the following request:
	// DELETE /posts/1
	DeleteResource

	// GetRelatedResources is a variation of the following requests:
	// GET /posts/1/author
	// GET /posts/1/comments
	GetRelatedResources

	// GetRelationship is a variation of the following requests:
	// GET /posts/1/relationships/author
	// GET /posts/1/relationships/comments
	GetRelationship

	// SetRelationship is a variation of the following requests:
	// PATCH /posts/1/relationships/author.
	// PATCH /posts/1/relationships/comments.
	SetRelationship

	// AppendToRelationship is a variation of the following request:
	// POST /posts/1/relationships/comments
	AppendToRelationship

	// RemoveFromRelationship is a variation of the following request:
	// DELETE /posts/1/relationships/comments
	RemoveFromRelationship
)

// DocumentExpected returns whether the intent is expected to come with a
// JSON API document.
func (i Intent) DocumentExpected() bool {
	switch i {
	case CreateResource, UpdateResource, SetRelationship,
		AppendToRelationship, RemoveFromRelationship:
		return true
	}

	return false
}

// A Request contains all JSON API related information parsed from a low level
// request.
type Request struct {
	// The Original HTTP request.
	Request *http.Request

	// The parsed JSON API intent of the request.
	Intent Intent

	// The fragments parsed from the URL of the request.
	ResourceType    string
	ResourceID      string
	RelatedResource string
	Relationship    string

	// The requested resources to be included in the response.
	Include []string

	// The pagination details of the request. Zero values mean no pagination
	// details have been provided.
	PageNumber int
	PageSize   int

	// The sorting that has been requested.
	Sorting []string

	// The sparse fieldsets that have been requested.
	Fields map[string][]string

	// The filtering that has been requested.
	Filters map[string][]string
}

// ParseRequest will parse the passed request and return a new Request with the
// parsed data. It will return an error if the content type, request method or
// url is invalid.
//
// Note: The returned error can directly be written using WriteError.
func ParseRequest(req *http.Request, prefix string) (*Request, error) {
	// get method
	method := req.Method

	// set overridden method if available
	if req.Header.Get("X-HTTP-Method-Override") != "" {
		method = req.Header.Get("X-HTTP-Method-Override")
	}

	// map method to action
	if method != "GET" && method != "POST" && method != "PATCH" && method != "DELETE" {
		return nil, BadRequest("Unsupported method")
	}

	// allocate new request
	r := &Request{
		Request: req,
	}

	// check content type header
	contentType := req.Header.Get("Content-Type")
	if contentType != "" && contentType != MediaType {
		return nil, BadRequest("Invalid content type header")
	}

	// check accept header
	accept := req.Header.Get("Accept")
	if accept != MediaType {
		return nil, BadRequest("Invalid accept header")
	}

	// de-prefix and trim path
	url := strings.Trim(strings.TrimPrefix(req.URL.Path, prefix), "/")

	// split path
	segments := strings.Split(url, "/")
	if len(segments) == 0 || len(segments) > 4 {
		return nil, BadRequest("Invalid URL segment count")
	}

	// check for invalid segments
	for _, s := range segments {
		if s == "" {
			return nil, BadRequest("Found empty URL segments")
		}
	}

	// set resource
	r.ResourceType = segments[0]
	level := 1

	// set resource id
	if len(segments) > 1 {
		r.ResourceID = segments[1]
		level = 2
	}

	// set related resource
	if len(segments) == 3 && segments[2] != "relationships" {
		r.RelatedResource = segments[2]
		level = 3
	}

	// set relationship
	if len(segments) == 4 && segments[2] == "relationships" {
		r.Relationship = segments[3]
		level = 4
	}

	// final check
	if len(segments) > 2 && (r.RelatedResource == "" && r.Relationship == "") {
		return nil, BadRequest("Invalid URL relationship format")
	}

	// calculate intent
	switch method {
	case "GET":
		switch level {
		case 1:
			r.Intent = ListResources
		case 2:
			r.Intent = FindResource
		case 3:
			r.Intent = GetRelatedResources
		case 4:
			r.Intent = GetRelationship
		}
	case "POST":
		switch level {
		case 1:
			r.Intent = CreateResource
		case 4:
			r.Intent = AppendToRelationship
		}
	case "PATCH":
		switch level {
		case 2:
			r.Intent = UpdateResource
		case 4:
			r.Intent = SetRelationship
		}
	case "DELETE":
		switch level {
		case 2:
			r.Intent = DeleteResource
		case 4:
			r.Intent = RemoveFromRelationship
		}
	}

	// check intent
	if r.Intent == 0 {
		return nil, BadRequest("The URL and method combination is invalid")
	}

	// check if request should come with a document and has content type set
	if r.Intent.DocumentExpected() && contentType == "" {
		return nil, BadRequest("Missing content type header")
	}

	for key, values := range req.URL.Query() {
		// set included resources
		if key == "include" {
			for _, v := range values {
				r.Include = append(r.Include, strings.Split(v, ",")...)
			}

			continue
		}

		// set sorting
		if key == "sort" {
			for _, v := range values {
				r.Sorting = append(r.Sorting, strings.Split(v, ",")...)
			}

			continue
		}

		// set page number
		if key == "page[number]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[number]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[number]")
			}

			r.PageNumber = n
			continue
		}

		// set page size
		if key == "page[size]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[size]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[size]")
			}

			r.PageSize = n
			continue
		}

		// set sparse fields
		if strings.HasPrefix(key, "fields[") && strings.HasSuffix(key, "]") {
			if r.Fields == nil {
				r.Fields = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Fields[typ] = append(r.Fields[typ], strings.Split(v, ",")...)
			}
		}

		// set filters
		if strings.HasPrefix(key, "filter[") && strings.HasSuffix(key, "]") {
			if r.Filters == nil {
				r.Filters = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Filters[typ] = append(r.Filters[typ], strings.Split(v, ",")...)
			}
		}
	}

	// check page size
	if r.PageNumber > 0 && r.PageSize <= 0 {
		return nil, BadRequestParam("Missing page size", "page[number]")
	}

	// check page number
	if r.PageSize > 0 && r.PageNumber <= 0 {
		return nil, BadRequestParam("Missing page number", "page[size]")
	}

	return r, nil
}
<commit_msg>store prefix<commit_after>package jsonapi

import (
	"net/http"
	"strconv"
	"strings"
)

// An Intent represents a valid combination of a request method and a URL pattern.
type Intent int

const (
	_ Intent = iota

	// ListResources is a variation of the following request:
	// GET /posts
	ListResources

	// FindResource is a variation of the following request:
	// GET /posts/1
	FindResource

	// CreateResource is a variation of the following request:
	// POST /posts
	CreateResource

	// UpdateResource is a variation of the following request:
	// PATCH /posts/1
	UpdateResource

	// DeleteResource is a variation of the following request:
	// DELETE /posts/1
	DeleteResource

	// GetRelatedResources is a variation of the following requests:
	// GET /posts/1/author
	// GET /posts/1/comments
	GetRelatedResources

	// GetRelationship is a variation of the following requests:
	// GET /posts/1/relationships/author
	// GET /posts/1/relationships/comments
	GetRelationship

	// SetRelationship is a variation of the following requests:
	// PATCH /posts/1/relationships/author.
	// PATCH /posts/1/relationships/comments.
	SetRelationship

	// AppendToRelationship is a variation of the following request:
	// POST /posts/1/relationships/comments
	AppendToRelationship

	// RemoveFromRelationship is a variation of the following request:
	// DELETE /posts/1/relationships/comments
	RemoveFromRelationship
)

// DocumentExpected returns whether the intent is expected to come with a
// JSON API document.
func (i Intent) DocumentExpected() bool {
	switch i {
	case CreateResource, UpdateResource, SetRelationship,
		AppendToRelationship, RemoveFromRelationship:
		return true
	}

	return false
}

// A Request contains all JSON API related information parsed from a low level
// request.
type Request struct {
	// The Original HTTP request.
	Request *http.Request

	// The parsed JSON API intent of the request.
	Intent Intent

	// The fragments parsed from the URL of the request.
	Prefix          string
	ResourceType    string
	ResourceID      string
	RelatedResource string
	Relationship    string

	// The requested resources to be included in the response.
	Include []string

	// The pagination details of the request. Zero values mean no pagination
	// details have been provided.
	PageNumber int
	PageSize   int

	// The sorting that has been requested.
	Sorting []string

	// The sparse fieldsets that have been requested.
	Fields map[string][]string

	// The filtering that has been requested.
	Filters map[string][]string
}

// ParseRequest will parse the passed request and return a new Request with the
// parsed data. It will return an error if the content type, request method or
// url is invalid.
//
// Note: The returned error can directly be written using WriteError.
func ParseRequest(req *http.Request, prefix string) (*Request, error) {
	// get method
	method := req.Method

	// set overridden method if available
	if req.Header.Get("X-HTTP-Method-Override") != "" {
		method = req.Header.Get("X-HTTP-Method-Override")
	}

	// map method to action
	if method != "GET" && method != "POST" && method != "PATCH" && method != "DELETE" {
		return nil, BadRequest("Unsupported method")
	}

	// allocate new request
	r := &Request{
		Request: req,
		Prefix:  prefix,
	}

	// check content type header
	contentType := req.Header.Get("Content-Type")
	if contentType != "" && contentType != MediaType {
		return nil, BadRequest("Invalid content type header")
	}

	// check accept header
	accept := req.Header.Get("Accept")
	if accept != MediaType {
		return nil, BadRequest("Invalid accept header")
	}

	// de-prefix and trim path
	url := strings.Trim(strings.TrimPrefix(req.URL.Path, r.Prefix), "/")

	// split path
	segments := strings.Split(url, "/")
	if len(segments) == 0 || len(segments) > 4 {
		return nil, BadRequest("Invalid URL segment count")
	}

	// check for invalid segments
	for _, s := range segments {
		if s == "" {
			return nil, BadRequest("Found empty URL segments")
		}
	}

	// set resource
	r.ResourceType = segments[0]
	level := 1

	// set resource id
	if len(segments) > 1 {
		r.ResourceID = segments[1]
		level = 2
	}

	// set related resource
	if len(segments) == 3 && segments[2] != "relationships" {
		r.RelatedResource = segments[2]
		level = 3
	}

	// set relationship
	if len(segments) == 4 && segments[2] == "relationships" {
		r.Relationship = segments[3]
		level = 4
	}

	// final check
	if len(segments) > 2 && (r.RelatedResource == "" && r.Relationship == "") {
		return nil, BadRequest("Invalid URL relationship format")
	}

	// calculate intent
	switch method {
	case "GET":
		switch level {
		case 1:
			r.Intent = ListResources
		case 2:
			r.Intent = FindResource
		case 3:
			r.Intent = GetRelatedResources
		case 4:
			r.Intent = GetRelationship
		}
	case "POST":
		switch level {
		case 1:
			r.Intent = CreateResource
		case 4:
			r.Intent = AppendToRelationship
		}
	case "PATCH":
		switch level {
		case 2:
			r.Intent = UpdateResource
		case 4:
			r.Intent = SetRelationship
		}
	case "DELETE":
		switch level {
		case 2:
			r.Intent = DeleteResource
		case 4:
			r.Intent = RemoveFromRelationship
		}
	}

	// check intent
	if r.Intent == 0 {
		return nil, BadRequest("The URL and method combination is invalid")
	}

	// check if request should come with a document and has content type set
	if r.Intent.DocumentExpected() && contentType == "" {
		return nil, BadRequest("Missing content type header")
	}

	for key, values := range req.URL.Query() {
		// set included resources
		if key == "include" {
			for _, v := range values {
				r.Include = append(r.Include, strings.Split(v, ",")...)
			}

			continue
		}

		// set sorting
		if key == "sort" {
			for _, v := range values {
				r.Sorting = append(r.Sorting, strings.Split(v, ",")...)
			}

			continue
		}

		// set page number
		if key == "page[number]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[number]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[number]")
			}

			r.PageNumber = n
			continue
		}

		// set page size
		if key == "page[size]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[size]")
			}

			n, err := strconv.Atoi(values[0])
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[size]")
			}

			r.PageSize = n
			continue
		}

		// set sparse fields
		if strings.HasPrefix(key, "fields[") && strings.HasSuffix(key, "]") {
			if r.Fields == nil {
				r.Fields = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Fields[typ] = append(r.Fields[typ], strings.Split(v, ",")...)
			}
		}

		// set filters
		if strings.HasPrefix(key, "filter[") && strings.HasSuffix(key, "]") {
			if r.Filters == nil {
				r.Filters = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				r.Filters[typ] = append(r.Filters[typ], strings.Split(v, ",")...)
			}
		}
	}

	// check page size
	if r.PageNumber > 0 && r.PageSize <= 0 {
		return nil, BadRequestParam("Missing page size", "page[number]")
	}

	// check page number
	if r.PageSize > 0 && r.PageNumber <= 0 {
		return nil, BadRequestParam("Missing page number", "page[size]")
	}

	return r, nil
}
<|endoftext|>


<commit_before>package model

import (
	"testing"
	"time"

	"fmt"

	"strings"

	"github.com/evandroflores/claimr/messages"
	"github.com/stretchr/testify/assert"
)

func TestGetContainerNoTeam(t *testing.T) {
	container, err := GetContainer("", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestGetContainerNoChannel(t *testing.T) {
	container, err := GetContainer("TestTeam", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestGetContainerNoName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestGetContainerBigName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel",
		"LoremIpsumDolorSitAmetConsecteturAdipiscingElit")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-name-too-big"), MaxNameSize))
}

func TestGetContainerNotFound(t *testing.T) {
	containerName := "TestDoesNotExist"
	container, err := GetContainer("TestTeam", "TestChannel", containerName)

	assert.ObjectsAreEqual(Container{}, container)
	assert.NoError(t, err, fmt.Sprintf("Container %s not found", containerName))
}

func TestGetContainerIgnoreCase(t *testing.T) {
	containerName := "UPPERCASE_NAME"
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: containerName}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", containerName)

	assert.NoError(t, err2)
	assert.Equal(t, containerFromDB.Name, strings.ToLower(containerName))

	container.Delete()
}

func TestAddContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", "Name")

	assert.NoError(t, err2)
	assert.ObjectsAreEqual(container, containerFromDB)
}

func TestAddContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestAddContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestAddContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestAddContainerDuplicate(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.EqualError(t, err, messages.Get("same-name"))
}

func TestDeleteContainer(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "Name")
	assert.NoError(t, err)

	err2 := container.Delete()
	assert.NoError(t, err2)
}

func TestDeleteContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestDeleteContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestDeleteContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestDeleteContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Delete()
	assert.EqualError(t, err, messages.Get("container-not-found"))

}

func TestUpdateContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	container.InUseBy = "me"
	err2 := container.Update()
	assert.NoError(t, err2)

	containerUpdated := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}

	assert.ObjectsAreEqual(containerUpdated.InUseBy, "me")
	assert.ObjectsAreEqual(container, containerUpdated)
	container.Delete()
}

func TestUpdateContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestUpdateContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestUpdateContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestUpdateContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Update()
	assert.EqualError(t, err, messages.Get("container-not-found"))
}

func TestListContainers(t *testing.T) {
	names := [4]string{"A", "B", "C", "D"}
	for _, name := range names {
		container := Container{TeamID: "TestList", ChannelID: "TestChannel", Name: name}
		err := container.Add()
		assert.NoError(t, err)
	}

	containers, err2 := GetContainers("TestList", "TestChannel")
	assert.NoError(t, err2)

	assert.Len(t, containers, len(names))
	for idx, container := range containers {
		assert.ObjectsAreEqual(container.Name, names[idx])
		container.Delete()
	}
}

func TestListContainersValidateTeamID(t *testing.T) {
	containers, err := GetContainers("", "")
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestListContainersValidateChannelID(t *testing.T) {
	containers, err := GetContainers("TestTeam", "")
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestRemoveInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.ClearInUse()
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Empty(t, containerFromDB.InUseBy)
	assert.Empty(t, containerFromDB.InUseForReason)
	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}

func TestSetInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.SetInUse(user, reason)
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}

func TestInUseTextInvalid(t *testing.T) {
	container := Container{}
	assert.Equal(t, messages.Get("in-use-text-invalid"), container.InUseText("whatever"))
}

func TestInUseTextSimpleAvailable(t *testing.T) {
	container := Container{InUseBy: ""}
	assert.Equal(t, available, container.InUseText("simple"))
}

func TestInUseTextSimpleInUse(t *testing.T) {
	container := Container{InUseBy: "me"}
	assert.Equal(t, inUse, container.InUseText("simple"))
}

func TestInUseTextFullAvailable(t *testing.T) {
	container := Container{InUseBy: ""}
	assert.Equal(t, available, container.InUseText("full"))
}

func TestInUseTextFullInUse(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user}
	err := container.Add()
	assert.NoError(t, err)
	container, err2 := GetContainer(team, channel, containerName)
	assert.NoError(t, err2)

	expected := fmt.Sprintf(messages.Get("container-in-use-by-w-reason"), user, "", container.UpdatedAt.Format(time.RFC1123))

	assert.Equal(t, expected, container.InUseText("full"))
	container.Delete()
}

func TestInUseTextFullInUseWithReason(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	err := container.Add()
	assert.NoError(t, err)
	container, err2 := GetContainer(team, channel, containerName)
	assert.NoError(t, err2)

	expected := fmt.Sprintf(messages.Get("container-in-use-by-w-reason"), user, reason, container.UpdatedAt.Format(time.RFC1123))

	assert.Equal(t, expected, container.InUseText("full"))
	container.Delete()
}
<commit_msg>InUseText Full In Use with reason test fix<commit_after>package model

import (
	"testing"
	"time"

	"fmt"

	"strings"

	"github.com/evandroflores/claimr/messages"
	"github.com/stretchr/testify/assert"
)

func TestGetContainerNoTeam(t *testing.T) {
	container, err := GetContainer("", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestGetContainerNoChannel(t *testing.T) {
	container, err := GetContainer("TestTeam", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestGetContainerNoName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestGetContainerBigName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel",
		"LoremIpsumDolorSitAmetConsecteturAdipiscingElit")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-name-too-big"), MaxNameSize))
}

func TestGetContainerNotFound(t *testing.T) {
	containerName := "TestDoesNotExist"
	container, err := GetContainer("TestTeam", "TestChannel", containerName)

	assert.ObjectsAreEqual(Container{}, container)
	assert.NoError(t, err, fmt.Sprintf("Container %s not found", containerName))
}

func TestGetContainerIgnoreCase(t *testing.T) {
	containerName := "UPPERCASE_NAME"
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: containerName}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", containerName)

	assert.NoError(t, err2)
	assert.Equal(t, containerFromDB.Name, strings.ToLower(containerName))

	container.Delete()
}

func TestAddContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", "Name")

	assert.NoError(t, err2)
	assert.ObjectsAreEqual(container, containerFromDB)
}

func TestAddContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestAddContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestAddContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestAddContainerDuplicate(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.EqualError(t, err, messages.Get("same-name"))
}

func TestDeleteContainer(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "Name")
	assert.NoError(t, err)

	err2 := container.Delete()
	assert.NoError(t, err2)
}

func TestDeleteContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestDeleteContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestDeleteContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestDeleteContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Delete()
	assert.EqualError(t, err, messages.Get("container-not-found"))

}

func TestUpdateContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	container.InUseBy = "me"
	err2 := container.Update()
	assert.NoError(t, err2)

	containerUpdated := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}

	assert.ObjectsAreEqual(containerUpdated.InUseBy, "me")
	assert.ObjectsAreEqual(container, containerUpdated)
	container.Delete()
}

func TestUpdateContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
}

func TestUpdateContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
}

func TestUpdateContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "container name"))
}

func TestUpdateContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Update()
	assert.EqualError(t, err, messages.Get("container-not-found"))
}

func TestListContainers(t *testing.T) {
	names := [4]string{"A", "B", "C", "D"}
	for _, name := range names {
		container := Container{TeamID: "TestList", ChannelID: "TestChannel", Name: name}
		err := container.Add()
		assert.NoError(t, err)
	}

	containers, err2 := GetContainers("TestList", "TestChannel")
	assert.NoError(t, err2)

	assert.Len(t, containers, len(names))
	for idx, container := range containers {
		assert.ObjectsAreEqual(container.Name, names[idx])
		container.Delete()
	}
}

func TestListContainersValidateTeamID(t *testing.T) {
	containers, err := GetContainers("", "")
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "teamID"))
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestListContainersValidateChannelID(t *testing.T) {
	containers, err := GetContainers("TestTeam", "")
	assert.EqualError(t, err, fmt.Sprintf(messages.Get("field-required"), "channelID"))
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestRemoveInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.ClearInUse()
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Empty(t, containerFromDB.InUseBy)
	assert.Empty(t, containerFromDB.InUseForReason)
	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}

func TestSetInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.SetInUse(user, reason)
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}

func TestInUseTextInvalid(t *testing.T) {
	container := Container{}
	assert.Equal(t, messages.Get("in-use-text-invalid"), container.InUseText("whatever"))
}

func TestInUseTextSimpleAvailable(t *testing.T) {
	container := Container{InUseBy: ""}
	assert.Equal(t, available, container.InUseText("simple"))
}

func TestInUseTextSimpleInUse(t *testing.T) {
	container := Container{InUseBy: "me"}
	assert.Equal(t, inUse, container.InUseText("simple"))
}

func TestInUseTextFullAvailable(t *testing.T) {
	container := Container{InUseBy: ""}
	assert.Equal(t, available, container.InUseText("full"))
}

func TestInUseTextFullInUse(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user}
	err := container.Add()
	assert.NoError(t, err)
	container, err2 := GetContainer(team, channel, containerName)
	assert.NoError(t, err2)

	expected := fmt.Sprintf(messages.Get("container-in-use-by-w-reason"), user, "", container.UpdatedAt.Format(time.RFC1123))

	assert.Equal(t, expected, container.InUseText("full"))
	container.Delete()
}

func TestInUseTextFullInUseWithReason(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	err := container.Add()
	assert.NoError(t, err)
	container, err2 := GetContainer(team, channel, containerName)
	assert.NoError(t, err2)

	expected := fmt.Sprintf(messages.Get("container-in-use-by-w-reason"), user, fmt.Sprintf(" for %s", reason), container.UpdatedAt.Format(time.RFC1123))

	assert.Equal(t, expected, container.InUseText("full"))
	container.Delete()
}
<|endoftext|>