<commit_before>package client

import (
	"bufio"
	"bytes"
	"io"
	"reflect"
	"strings"
	"testing"
)

// assert that client.client implements client.Client
var _ Client = new(client)

// assert that client.Request and client.Response implement client.Message
// var _ Message = new(Request)
var _ Message = new(Response)

func TestNewClient(t *testing.T) {
	var b bytes.Buffer
	var r io.ReadWriter = &b
	var _ Client = NewClient(r)
}

func b(s string) *bufio.Reader { return bufio.NewReader(strings.NewReader(s)) }

var sendRequestTests = []struct {
	Request
	expected string
}{
	{Request{
		Method:  "GET",
		URI:     "/",
		Version: HTTP_1_1,
		// no body
	},
		"GET / HTTP/1.1\r\n\r\n",
	},
	{Request{
		Method:  "GET",
		URI:     "/",
		Version: HTTP_1_1,
		Body:    b("Hello world!"),
	},
		"GET / HTTP/1.1\r\n\r\nHello world!",
	},
	{Request{
		Method:  "GET",
		URI:     "/",
		Version: HTTP_1_1,
		Body:    b("Hello world!"),
		Headers: []Header{{
			Key: "Host", Value: "localhost",
		}},
	},
		"GET / HTTP/1.1\r\nHost: localhost\r\n\r\nHello world!",
	},
}

func TestClientSendRequest(t *testing.T) {
	for _, tt := range sendRequestTests {
		var b bytes.Buffer
		client := NewClient(&b)
		if err := client.WriteRequest(&tt.Request); err != nil {
			t.Fatalf("client.SendRequest(): %v", err)
		}
		if actual := b.String(); actual != tt.expected {
			t.Errorf("client.SendRequest(): expected %q, got %q", tt.expected, actual)
		}
	}
}

var readResponseTests = []struct {
	data string
	*Response
	err error
}{
	{"HTTP/1.0 200 OK\r\n\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
		},
		nil},
	{"HTTP/1.0 200 OK\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
		},
		io.EOF},
	{"HTTP/1.1 404 Not found\r\n\r\n",
		&Response{
			Version: HTTP_1_1,
			Status:  Status{404, "Not found"},
		},
		nil},
	{"HTTP/1.1 404 Not found\r\n",
		&Response{
			Version: HTTP_1_1,
			Status:  Status{404, "Not found"},
		}, io.EOF},
	{"HTTP/1.0 200 OK\r\nHost: localhost\r\n\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
			Headers: []Header{{"Host", "localhost"}},
		}, nil},
	{"HTTP/1.1 200 OK\r\nHost: localhost\r\n",
		&Response{
			Version: HTTP_1_1,
			Status:  Status{200, "OK"},
			Headers: []Header{{"Host", "localhost"}},
		}, io.EOF},
	{"HTTP/1.0 200 OK\r\nHost: localhost\r\nConnection : close\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
			Headers: []Header{{"Host", "localhost"}, {"Connection", "close"}},
		}, io.EOF},
}

func TestClientReadResponse(t *testing.T) {
	for _, tt := range readResponseTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if resp.Version != tt.Response.Version || resp.Status != tt.Response.Status {
			t.Errorf("client.ReadResponse(%q): expected %q %q, got %q %q", tt.data, tt.Response.Version, tt.Response.Status, resp.Version, resp.Status)
			continue
		}
		if !reflect.DeepEqual(tt.Response.Headers, resp.Headers) || err != tt.err {
			t.Errorf("client.ReadResponse(%q): expected %v %v, got %v %v", tt.data, tt.Response.Headers, tt.err, resp.Headers, err)
		}
		if err != nil {
			continue
		}
		var buf bytes.Buffer
		var expected, actual string
		if tt.Response.Body != nil {
			_, err = io.Copy(&buf, tt.Response.Body)
			expected = buf.String()
		}
		if resp.Body != nil {
			_, err = io.Copy(&buf, resp.Body)
			actual = buf.String()
		}
		if actual != expected || err != tt.err {
			t.Errorf("client.ReadResponse(%q): expected %q %v, got %q %v", tt.data, expected, tt.err, actual, err)
		}
	}
}

var responseContentLengthTests = []struct {
	data     string
	expected int64
}{
	{"HTTP/1.0 200 OK\r\n\r\n", -1},
	{"HTTP/1.0 200 OK\r\n\r\n ", -1},
	{"HTTP/1.0 200 OK\r\nContent-Length: 1\r\n\r\n ", 1},
	{"HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n", 0},
	{"HTTP/1.0 200 OK\r\nContent-Length: 4294967296\r\n\r\n", 4294967296},
}

func TestResponseContentLength(t *testing.T) {
	for _, tt := range responseContentLengthTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if err != nil {
			t.Fatal(err)
		}
		if actual := resp.ContentLength(); actual != tt.expected {
			t.Errorf("ReadResponse(%q): ContentLength: expected %d got %d", tt.data, tt.expected, actual)
		}
	}
}

var closeRequestedTests = []struct {
	data     string
	expected bool
}{
	{"HTTP/1.0 200 OK\r\n\r\nfoo", false},
	{"HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nfoo", true},
	{"HTTP/1.1 200 OK\r\n\r\nfoo", false},
	{"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nfoo", true},
}

func TestRequestCloseRequested(t *testing.T) {
	for _, tt := range closeRequestedTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if err != nil {
			t.Fatal(err)
		}
		if actual := resp.CloseRequested(); actual != tt.expected {
			t.Errorf("ReadResponse(%q): CloseRequested: expected %d got %d", tt.data, tt.expected, actual)
		}
	}
}

var transferEncodingTests = []struct {
	data     string
	expected string
}{
	{"HTTP/1.0 200 OK\r\n\r\nfoo", "identity"},
	{"HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nfoo", "identity"},
	{"HTTP/1.0 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\nfoo", "chunked"},
	{"HTTP/1.1 200 OK\r\n\r\nfoo", "identity"},
	{"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nfoo", "identity"},
	{"HTTP/1.1 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\nfoo", "chunked"},
}

func TestTransferEncoding(t *testing.T) {
	for _, tt := range transferEncodingTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if err != nil {
			t.Fatal(err)
		}
		if actual := resp.TransferEncoding(); actual != tt.expected {
			t.Errorf("ReadResponse(%q): TransferEncoding: expected %d got %d", tt.data, tt.expected, actual)
		}
	}
}

var statusStringTests = []struct {
	Status
	expected string
}{
	{Status{200, "OK"}, "200 OK"},
	{Status{418, "I'm a teapot"}, "418 I'm a teapot"},
}

func TestStatusString(t *testing.T) {
	for _, tt := range statusStringTests {
		if actual := tt.Status.String(); actual != tt.expected {
			t.Errorf("Status{%d, %q}.String(): expected %q, got %q", tt.Status.Code, tt.Status.Reason, tt.expected, actual)
		}
	}
}

var versionStringTests = []struct {
	Version
	expected string
}{
	{Version{0, 9}, "HTTP/0.9"},
	{Version{1, 0}, "HTTP/1.0"},
	{Version{1, 1}, "HTTP/1.1"},
	{Version{2, 0}, "HTTP/2.0"},
}

func TestVersionString(t *testing.T) {
	for _, tt := range versionStringTests {
		if actual := tt.Version.String(); actual != tt.expected {
			t.Errorf("Version{%d, %d}.String(): expected %q, got %q", tt.Version.major, tt.Version.minor, tt.expected, actual)
		}
	}
}
<commit_msg>More coverage<commit_after>package client

import (
	"bufio"
	"bytes"
	"io"
	"reflect"
	"strings"
	"testing"
)

// assert that client.client implements client.Client
var _ Client = new(client)

// assert that client.Request and client.Response implement client.Message
// var _ Message = new(Request)
var _ Message = new(Response)

func TestNewClient(t *testing.T) {
	var b bytes.Buffer
	var r io.ReadWriter = &b
	var _ Client = NewClient(r)
}

func b(s string) *bufio.Reader { return bufio.NewReader(strings.NewReader(s)) }

var sendRequestTests = []struct {
	Request
	expected string
}{
	{Request{
		Method:  "GET",
		URI:     "/",
		Version: HTTP_1_1,
		// no body
	},
		"GET / HTTP/1.1\r\n\r\n",
	},
	{Request{
		Method:  "GET",
		URI:     "/",
		Version: HTTP_1_1,
		Body:    b("Hello world!"),
	},
		"GET / HTTP/1.1\r\n\r\nHello world!",
	},
	{Request{
		Method:  "GET",
		URI:     "/",
		Version: HTTP_1_1,
		Body:    b("Hello world!"),
		Headers: []Header{{
			Key: "Host", Value: "localhost",
		}},
	},
		"GET / HTTP/1.1\r\nHost: localhost\r\n\r\nHello world!",
	},
}

func TestClientSendRequest(t *testing.T) {
	for _, tt := range sendRequestTests {
		var b bytes.Buffer
		client := NewClient(&b)
		if err := client.WriteRequest(&tt.Request); err != nil {
			t.Fatalf("client.SendRequest(): %v", err)
		}
		if actual := b.String(); actual != tt.expected {
			t.Errorf("client.SendRequest(): expected %q, got %q", tt.expected, actual)
		}
	}
}

var readResponseTests = []struct {
	data string
	*Response
	err error
}{
	{"HTTP/1.0 200 OK\r\n\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
		},
		nil},
	{"HTTP/1.0 200 OK\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
		},
		io.EOF},
	{"HTTP/1.1 404 Not found\r\n\r\n",
		&Response{
			Version: HTTP_1_1,
			Status:  Status{404, "Not found"},
		},
		nil},
	{"HTTP/1.1 404 Not found\r\n",
		&Response{
			Version: HTTP_1_1,
			Status:  Status{404, "Not found"},
		}, io.EOF},
	{"HTTP/1.0 200 OK\r\nHost: localhost\r\n\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
			Headers: []Header{{"Host", "localhost"}},
		}, nil},
	{"HTTP/1.1 200 OK\r\nHost: localhost\r\n",
		&Response{
			Version: HTTP_1_1,
			Status:  Status{200, "OK"},
			Headers: []Header{{"Host", "localhost"}},
		}, io.EOF},
	{"HTTP/1.0 200 OK\r\nHost: localhost\r\nConnection : close\r\n",
		&Response{
			Version: HTTP_1_0,
			Status:  Status{200, "OK"},
			Headers: []Header{{"Host", "localhost"}, {"Connection", "close"}},
		}, io.EOF},
}

func TestClientReadResponse(t *testing.T) {
	for _, tt := range readResponseTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if resp.Version != tt.Response.Version || resp.Status != tt.Response.Status {
			t.Errorf("client.ReadResponse(%q): expected %q %q, got %q %q", tt.data, tt.Response.Version, tt.Response.Status, resp.Version, resp.Status)
			continue
		}
		if !reflect.DeepEqual(tt.Response.Headers, resp.Headers) || err != tt.err {
			t.Errorf("client.ReadResponse(%q): expected %v %v, got %v %v", tt.data, tt.Response.Headers, tt.err, resp.Headers, err)
		}
		if err != nil {
			continue
		}
		var buf bytes.Buffer
		var expected, actual string
		if tt.Response.Body != nil {
			_, err = io.Copy(&buf, tt.Response.Body)
			expected = buf.String()
		}
		if resp.Body != nil {
			_, err = io.Copy(&buf, resp.Body)
			actual = buf.String()
		}
		if actual != expected || err != tt.err {
			t.Errorf("client.ReadResponse(%q): expected %q %v, got %q %v", tt.data, expected, tt.err, actual, err)
		}
	}
}

var responseContentLengthTests = []struct {
	data     string
	expected int64
}{
	{"HTTP/1.0 200 OK\r\n\r\n", -1},
	{"HTTP/1.0 200 OK\r\n\r\n ", -1},
	{"HTTP/1.0 200 OK\r\nContent-Length: 1\r\n\r\n ", 1},
	{"HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n", 0},
	{"HTTP/1.0 200 OK\r\nContent-Length: 4294967296\r\n\r\n", 4294967296},
	{"HTTP/1.0 200 OK\r\nContent-Length: seven\r\n\r\n", -1},
}

func TestResponseContentLength(t *testing.T) {
	for _, tt := range responseContentLengthTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if err != nil {
			t.Fatal(err)
		}
		if actual := resp.ContentLength(); actual != tt.expected {
			t.Errorf("ReadResponse(%q): ContentLength: expected %d got %d", tt.data, tt.expected, actual)
		}
	}
}

var closeRequestedTests = []struct {
	data     string
	expected bool
}{
	{"HTTP/1.0 200 OK\r\n\r\nfoo", false},
	{"HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nfoo", true},
	{"HTTP/1.1 200 OK\r\n\r\nfoo", false},
	{"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nfoo", true},
}

func TestRequestCloseRequested(t *testing.T) {
	for _, tt := range closeRequestedTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if err != nil {
			t.Fatal(err)
		}
		if actual := resp.CloseRequested(); actual != tt.expected {
			t.Errorf("ReadResponse(%q): CloseRequested: expected %d got %d", tt.data, tt.expected, actual)
		}
	}
}

var transferEncodingTests = []struct {
	data     string
	expected string
}{
	{"HTTP/1.0 200 OK\r\n\r\nfoo", "identity"},
	{"HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nfoo", "identity"},
	{"HTTP/1.0 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\nfoo", "chunked"},
	{"HTTP/1.1 200 OK\r\n\r\nfoo", "identity"},
	{"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nfoo", "identity"},
	{"HTTP/1.1 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\nfoo", "chunked"},
}

func TestTransferEncoding(t *testing.T) {
	for _, tt := range transferEncodingTests {
		client := &client{reader: reader{b(tt.data)}}
		resp, err := client.ReadResponse()
		if err != nil {
			t.Fatal(err)
		}
		if actual := resp.TransferEncoding(); actual != tt.expected {
			t.Errorf("ReadResponse(%q): TransferEncoding: expected %d got %d", tt.data, tt.expected, actual)
		}
	}
}

var statusStringTests = []struct {
	Status
	expected string
}{
	{Status{200, "OK"}, "200 OK"},
	{Status{418, "I'm a teapot"}, "418 I'm a teapot"},
}

func TestStatusString(t *testing.T) {
	for _, tt := range statusStringTests {
		if actual := tt.Status.String(); actual != tt.expected {
			t.Errorf("Status{%d, %q}.String(): expected %q, got %q", tt.Status.Code, tt.Status.Reason, tt.expected, actual)
		}
	}
}

var versionStringTests = []struct {
	Version
	expected string
}{
	{Version{0, 9}, "HTTP/0.9"},
	{Version{1, 0}, "HTTP/1.0"},
	{Version{1, 1}, "HTTP/1.1"},
	{Version{2, 0}, "HTTP/2.0"},
}

func TestVersionString(t *testing.T) {
	for _, tt := range versionStringTests {
		if actual := tt.Version.String(); actual != tt.expected {
			t.Errorf("Version{%d, %d}.String(): expected %q, got %q", tt.Version.major, tt.Version.minor, tt.expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>package lsl

import (
	"fmt"
	"io"
	"log"
	"path/filepath"

	"github.com/gobuffalo/packr"
	"github.com/yuin/gopher-lua"
	"layeh.com/gopher-luar"
)

var (
	luaBox     packr.Box
	uTestBox   packr.Box
	lslFrame   string
	uTestFrame string
)

func init() {
	var err error
	luaBox = packr.NewBox("./lua")
	lslFrame, err = luaBox.MustString("env.lua")
	if err != nil {
		log.Fatalf("Failed to load lua env %s", err)
	}
	uTestBox = packr.NewBox("./lua/u-test")
	uTestFrame, err = uTestBox.MustString("u-test.lua")
	if err != nil {
		log.Fatalf("Failed to test env %s", err)
	}
}

// OutputWriter ... function for writing
type OutputWriter func(txt string)

// InputReader ... function for reading input
type InputReader func() string

// LuaLog ...  Log capture system
type LuaLog struct {
	Output io.Writer
}

// Error ... Log error
func (ll *LuaLog) Error(txt string) {
	msg := fmt.Sprintf("ERROR: %s", txt)
	ll.Log(msg)
}

// Info ... Info log wrapper
func (ll *LuaLog) Info(txt string) {
	msg := fmt.Sprintf("INFO: %s", txt)
	ll.Log(msg)
}

// Debug ... Very low level effort
func (ll *LuaLog) Debug(txt string) {
	msg := fmt.Sprintf("DEBUG: %s", txt)
	ll.Log(msg)
}

// Log ... Just output some text
func (ll *LuaLog) Log(v interface{}) {
	msg := fmt.Sprintf("%v\n", v)
	log.Print(msg)
	//ll.Output.Write([]byte(msg))
}

// LuaLoader ... Simple loader system
type LuaLoader struct {
	State    *lua.LState
	Input    io.Reader
	Output   io.Writer
	Log      *LuaLog
	EnvMap   map[string]interface{}
	DslPath  string
	envBuilt bool
}

// NewLuaLoader ... creates a new LuaLoader
func NewLuaLoader(in io.Reader, out io.Writer, dslPath string) *LuaLoader {
	ll := &LuaLoader{State: lua.NewState(), Input: in, Output: out, Log: &LuaLog{out}, EnvMap: make(map[string]interface{}), DslPath: dslPath, envBuilt: false}
	return ll
}

// SetGlobalVar ... Push into enviroment
func (ll *LuaLoader) SetGlobalVar(n string, v interface{}) {
	// If already exists then remove it first
	if _, exists := ll.EnvMap[n]; exists {
		delete(ll.EnvMap, n)
	}
	ll.EnvMap[n] = v
}

func (ll *LuaLoader) BuildEnv() {
	// Don't build the more than once
	if ll.envBuilt {
		return
	}
	err := ll.State.DoString(lslFrame)
	if err != nil {
		log.Fatalf("Failed to load lsl framework %s", err)
	}
	// Log wrappers
	ll.SetGlobalVar("log_debug", luar.New(ll.State, ll.Log.Debug))
	ll.SetGlobalVar("log_info", luar.New(ll.State, ll.Log.Info))
	ll.SetGlobalVar("log_error", luar.New(ll.State, ll.Log.Error))
	ll.SetGlobalVar("log", luar.New(ll.State, ll.Log.Log))
	//ll.SetGlobalVar("print", luar.New(ll.State, ll.Log.Log))
	ll.State.SetGlobal("env_map", luar.New(ll.State, ll.EnvMap))
	ll.envBuilt = true
}

// Code ... Execute code path
func (ll *LuaLoader) Code(code string) error {
	ll.BuildEnv()
	return ll.State.CallByParam(lua.P{
		Fn:      ll.State.GetGlobal("run_code_with_env"),
		NRet:    1,
		Protect: true,
	}, lua.LString(code))
}

// File ... Runs a DSL file
func (ll *LuaLoader) File(path string) error {
	ll.BuildEnv()
	return ll.State.CallByParam(lua.P{
		Fn:      ll.State.GetGlobal("run_file_with_env"),
		NRet:    1,
		Protect: true,
	}, lua.LString(path))
}

// Test ... Execute test file in dsl mode
func (ll *LuaLoader) Test(path string) error {
	ll.State.SetGlobal("print", luar.New(ll.State, func(msg string) {
		ll.Output.Write([]byte(msg))
	}))

	ut, err := ll.State.LoadString(uTestFrame)
	if err != nil {
		log.Fatalf("Failed to load u-test framework %s", err)
	}
	ll.State.SetGlobal("utest", ut)
	ll.BuildEnv()
	testPath := filepath.Dir(path)
	return ll.State.CallByParam(lua.P{
		Fn:      ll.State.GetGlobal("run_test_with_env"),
		NRet:    1,
		Protect: true,
	}, lua.LString(path), lua.LString(testPath))
}

// Close ... End execution and exit
func (ll *LuaLoader) Close() {
	ll.State.Close()
}
<commit_msg>Fix test<commit_after>package lsl

import (
	"fmt"
	"io"
	"log"
	"path/filepath"

	"github.com/gobuffalo/packr"
	"github.com/yuin/gopher-lua"
	"layeh.com/gopher-luar"
)

var (
	luaBox     packr.Box
	uTestBox   packr.Box
	lslFrame   string
	uTestFrame string
)

func init() {
	var err error
	luaBox = packr.NewBox("./lua")
	lslFrame, err = luaBox.MustString("env.lua")
	if err != nil {
		log.Fatalf("Failed to load lua env %s", err)
	}
	uTestBox = packr.NewBox("./lua/u-test")
	uTestFrame, err = uTestBox.MustString("u-test.lua")
	if err != nil {
		log.Fatalf("Failed to test env %s", err)
	}
}

// OutputWriter ... function for writing
type OutputWriter func(txt string)

// InputReader ... function for reading input
type InputReader func() string

// LuaLog ...  Log capture system
type LuaLog struct {
	Output io.Writer
}

// Error ... Log error
func (ll *LuaLog) Error(txt string) {
	msg := fmt.Sprintf("ERROR: %s", txt)
	ll.Log(msg)
}

// Info ... Info log wrapper
func (ll *LuaLog) Info(txt string) {
	msg := fmt.Sprintf("INFO: %s", txt)
	ll.Log(msg)
}

// Debug ... Very low level effort
func (ll *LuaLog) Debug(txt string) {
	msg := fmt.Sprintf("DEBUG: %s", txt)
	ll.Log(msg)
}

// Log ... Just output some text
func (ll *LuaLog) Log(v interface{}) {
	msg := fmt.Sprintf("%v\n", v)
	log.Print(msg)
	ll.Output.Write([]byte(msg))
}

// LuaLoader ... Simple loader system
type LuaLoader struct {
	State    *lua.LState
	Input    io.Reader
	Output   io.Writer
	Log      *LuaLog
	EnvMap   map[string]interface{}
	DslPath  string
	envBuilt bool
}

// NewLuaLoader ... creates a new LuaLoader
func NewLuaLoader(in io.Reader, out io.Writer, dslPath string) *LuaLoader {
	ll := &LuaLoader{State: lua.NewState(), Input: in, Output: out, Log: &LuaLog{out}, EnvMap: make(map[string]interface{}), DslPath: dslPath, envBuilt: false}
	return ll
}

// SetGlobalVar ... Push into enviroment
func (ll *LuaLoader) SetGlobalVar(n string, v interface{}) {
	// If already exists then remove it first
	if _, exists := ll.EnvMap[n]; exists {
		delete(ll.EnvMap, n)
	}
	ll.EnvMap[n] = v
}

func (ll *LuaLoader) BuildEnv() {
	// Don't build the more than once
	if ll.envBuilt {
		return
	}
	err := ll.State.DoString(lslFrame)
	if err != nil {
		log.Fatalf("Failed to load lsl framework %s", err)
	}
	// Log wrappers
	ll.SetGlobalVar("log_debug", luar.New(ll.State, ll.Log.Debug))
	ll.SetGlobalVar("log_info", luar.New(ll.State, ll.Log.Info))
	ll.SetGlobalVar("log_error", luar.New(ll.State, ll.Log.Error))
	ll.SetGlobalVar("log", luar.New(ll.State, ll.Log.Log))
	//ll.SetGlobalVar("print", luar.New(ll.State, ll.Log.Log))
	ll.State.SetGlobal("env_map", luar.New(ll.State, ll.EnvMap))
	ll.envBuilt = true
}

// Code ... Execute code path
func (ll *LuaLoader) Code(code string) error {
	ll.BuildEnv()
	return ll.State.CallByParam(lua.P{
		Fn:      ll.State.GetGlobal("run_code_with_env"),
		NRet:    1,
		Protect: true,
	}, lua.LString(code))
}

// File ... Runs a DSL file
func (ll *LuaLoader) File(path string) error {
	ll.BuildEnv()
	return ll.State.CallByParam(lua.P{
		Fn:      ll.State.GetGlobal("run_file_with_env"),
		NRet:    1,
		Protect: true,
	}, lua.LString(path))
}

// Test ... Execute test file in dsl mode
func (ll *LuaLoader) Test(path string) error {
	ll.State.SetGlobal("print", luar.New(ll.State, func(msg string) {
		ll.Output.Write([]byte(msg))
	}))

	ut, err := ll.State.LoadString(uTestFrame)
	if err != nil {
		log.Fatalf("Failed to load u-test framework %s", err)
	}
	ll.State.SetGlobal("utest", ut)
	ll.BuildEnv()
	testPath := filepath.Dir(path)
	return ll.State.CallByParam(lua.P{
		Fn:      ll.State.GetGlobal("run_test_with_env"),
		NRet:    1,
		Protect: true,
	}, lua.LString(path), lua.LString(testPath))
}

// Close ... End execution and exit
func (ll *LuaLoader) Close() {
	ll.State.Close()
}
<|endoftext|>


<commit_before>package putio

import "fmt"

// File represents a Put.io file.
type File struct {
	ID                int64  `json:"id"`
	Name              string `json:"name"`
	Size              int64  `json:"size"`
	ContentType       string `json:"content_type"`
	CreatedAt         *Time  `json:"created_at"`
	FirstAccessedAt   *Time  `json:"first_accessed_at"`
	ParentID          int64  `json:"parent_id"`
	Screenshot        string `json:"screenshot"`
	OpensubtitlesHash string `json:"opensubtitles_hash"`
	IsMP4Available    bool   `json:"is_mp4_available"`
	Icon              string `json:"icon"`
	CRC32             string `json:"crc32"`
	IsShared          bool   `json:"is_shared"`
}

func (f *File) String() string {
	return fmt.Sprintf("<ID: %v Name: %q Size: %v>", f.ID, f.Name, f.Size)
}

// IsDir reports whether the file is a directory.
func (f *File) IsDir() bool {
	return f.ContentType == "application/x-directory"
}

// Upload represents a Put.io upload. If the uploaded file is a torrent file,
// Transfer field will represent the status of the transfer.
type Upload struct {
	File     *File     `json:"file"`
	Transfer *Transfer `json:"transfer"`
}

// Search represents a search response.
type Search struct {
	Files []File `json:"files"`
	Next  string `json:"next"`
}

// Transfer represents a Put.io transfer state.
type Transfer struct {
	Availability   int    `json:"availability"`
	CallbackURL    string `json:"callback_url"`
	CreatedAt      *Time  `json:"created_at"`
	CreatedTorrent bool   `json:"created_torrent"`
	ClientIP       string `json:"client_ip"`

	// FIXME: API returns either string or float non-deterministically.
	// CurrentRatio       float32 `json:"current_ratio"`

	DownloadSpeed      int    `json:"down_speed"`
	Downloaded         int64  `json:"downloaded"`
	DownloadID         int64  `json:"download_id"`
	ErrorMessage       string `json:"error_message"`
	EstimatedTime      int64  `json:"estimated_time"`
	Extract            bool   `json:"extract"`
	FileID             int64  `json:"file_id"`
	FinishedAt         *Time  `json:"finished_at"`
	ID                 int64  `json:"id"`
	IsPrivate          bool   `json:"is_private"`
	MagnetURI          string `json:"magneturi"`
	Name               string `json:"name"`
	PeersConnected     int    `json:"peers_connected"`
	PeersGettingFromUs int    `json:"peers_getting_from_us"`
	PeersSendingToUs   int    `json:"peers_sending_to_us"`
	PercentDone        int    `json:"percent_done"`
	SaveParentID       int64  `json:"save_parent_id"`
	SecondsSeeding     int    `json:"seconds_seeding"`
	Size               int    `json:"size"`
	Source             string `json:"source"`
	Status             string `json:"status"`
	StatusMessage      string `json:"status_message"`
	SubscriptionID     int    `json:"subscription_id"`
	TorrentLink        string `json:"torrent_link"`
	TrackerMessage     string `json:"tracker_message"`
	Trackers           string `json:"tracker"`
	Type               string `json:"type"`
	UploadSpeed        int    `json:"up_speed"`
	Uploaded           int64  `json:"uploaded"`
}

// AccountInfo represents user's account information.
type AccountInfo struct {
	AccountActive           bool   `json:"account_active"`
	AvatarURL               string `json:"avatar_url"`
	DaysUntilFilesDeletion  int    `json:"days_until_files_deletion"`
	DefaultSubtitleLanguage string `json:"default_subtitle_language"`
	Disk                    struct {
		Avail int64 `json:"avail"`
		Size  int64 `json:"size"`
		Used  int64 `json:"used"`
	} `json:"disk"`
	HasVoucher                bool     `json:"has_voucher"`
	Mail                      string   `json:"mail"`
	PlanExpirationDate        string   `json:"plan_expiration_date"`
	Settings                  Settings `json:"settings"`
	SimultaneousDownloadLimit int      `json:"simultaneous_download_limit"`
	SubtitleLanguages         []string `json:"subtitle_languages"`
	UserID                    int64    `json:"user_id"`
	Username                  string   `json:"username"`
}

// Settings represents user's personal settings.
type Settings struct {
	CallbackURL             string      `json:"callback_url"`
	DefaultDownloadFolder   int64       `json:"default_download_folder"`
	DefaultSubtitleLanguage string      `json:"default_subtitle_language"`
	DownloadFolderUnset     bool        `json:"download_folder_unset"`
	IsInvisible             bool        `json:"is_invisible"`
	Nextepisode             bool        `json:"nextepisode"`
	PrivateDownloadHostIP   interface{} `json:"private_download_host_ip"`
	PushoverToken           string      `json:"pushover_token"`
	Routing                 string      `json:"routing"`
	Sorting                 string      `json:"sorting"`
	SSLEnabled              bool        `json:"ssl_enabled"`
	StartFrom               bool        `json:"start_from"`
	SubtitleLanguages       []string    `json:"subtitle_languages"`
}

// Friend represents Put.io user's friend.
type Friend struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
}

// Zip represents Put.io zip file.
type Zip struct {
	ID        int64 `json:"id"`
	CreatedAt *Time `json:"created_at"`

	Size   int64  `json:"size"`
	Status string `json:"status"`
	URL    string `json:"url"`
}

// Subtitle represents a subtitle.
type Subtitle struct {
	Key      string
	Language string
	Name     string
	Source   string
}

// Event represents a Put.io event. It could be a transfer or a shared file.
type Event struct {
	ID           int64  `json:"id"`
	FileID       int64  `json:"file_id"`
	Source       string `json:"source"`
	Type         string `json:"type"`
	TransferName string `json:"transfer_name"`
	TransferSize int64  `json:"transfer_size"`
	CreatedAt    *Time  `json:"created_at"`
}

type share struct {
	FileID   int64  `json:"file_id"`
	Filename string `json:"file_name"`
	// Number of friends the file is shared with
	SharedWith int64 `json:"shared_with"`
}
<commit_msg>add updated_at field to File type<commit_after>package putio

import "fmt"

// File represents a Put.io file.
type File struct {
	ID                int64  `json:"id"`
	Name              string `json:"name"`
	Size              int64  `json:"size"`
	ContentType       string `json:"content_type"`
	CreatedAt         *Time  `json:"created_at"`
	UpdatedAt         *Time  `json:"updated_at"`
	FirstAccessedAt   *Time  `json:"first_accessed_at"`
	ParentID          int64  `json:"parent_id"`
	Screenshot        string `json:"screenshot"`
	OpensubtitlesHash string `json:"opensubtitles_hash"`
	IsMP4Available    bool   `json:"is_mp4_available"`
	Icon              string `json:"icon"`
	CRC32             string `json:"crc32"`
	IsShared          bool   `json:"is_shared"`
}

func (f *File) String() string {
	return fmt.Sprintf("<ID: %v Name: %q Size: %v>", f.ID, f.Name, f.Size)
}

// IsDir reports whether the file is a directory.
func (f *File) IsDir() bool {
	return f.ContentType == "application/x-directory"
}

// Upload represents a Put.io upload. If the uploaded file is a torrent file,
// Transfer field will represent the status of the transfer.
type Upload struct {
	File     *File     `json:"file"`
	Transfer *Transfer `json:"transfer"`
}

// Search represents a search response.
type Search struct {
	Files []File `json:"files"`
	Next  string `json:"next"`
}

// Transfer represents a Put.io transfer state.
type Transfer struct {
	Availability   int    `json:"availability"`
	CallbackURL    string `json:"callback_url"`
	CreatedAt      *Time  `json:"created_at"`
	CreatedTorrent bool   `json:"created_torrent"`
	ClientIP       string `json:"client_ip"`

	// FIXME: API returns either string or float non-deterministically.
	// CurrentRatio       float32 `json:"current_ratio"`

	DownloadSpeed      int    `json:"down_speed"`
	Downloaded         int64  `json:"downloaded"`
	DownloadID         int64  `json:"download_id"`
	ErrorMessage       string `json:"error_message"`
	EstimatedTime      int64  `json:"estimated_time"`
	Extract            bool   `json:"extract"`
	FileID             int64  `json:"file_id"`
	FinishedAt         *Time  `json:"finished_at"`
	ID                 int64  `json:"id"`
	IsPrivate          bool   `json:"is_private"`
	MagnetURI          string `json:"magneturi"`
	Name               string `json:"name"`
	PeersConnected     int    `json:"peers_connected"`
	PeersGettingFromUs int    `json:"peers_getting_from_us"`
	PeersSendingToUs   int    `json:"peers_sending_to_us"`
	PercentDone        int    `json:"percent_done"`
	SaveParentID       int64  `json:"save_parent_id"`
	SecondsSeeding     int    `json:"seconds_seeding"`
	Size               int    `json:"size"`
	Source             string `json:"source"`
	Status             string `json:"status"`
	StatusMessage      string `json:"status_message"`
	SubscriptionID     int    `json:"subscription_id"`
	TorrentLink        string `json:"torrent_link"`
	TrackerMessage     string `json:"tracker_message"`
	Trackers           string `json:"tracker"`
	Type               string `json:"type"`
	UploadSpeed        int    `json:"up_speed"`
	Uploaded           int64  `json:"uploaded"`
}

// AccountInfo represents user's account information.
type AccountInfo struct {
	AccountActive           bool   `json:"account_active"`
	AvatarURL               string `json:"avatar_url"`
	DaysUntilFilesDeletion  int    `json:"days_until_files_deletion"`
	DefaultSubtitleLanguage string `json:"default_subtitle_language"`
	Disk                    struct {
		Avail int64 `json:"avail"`
		Size  int64 `json:"size"`
		Used  int64 `json:"used"`
	} `json:"disk"`
	HasVoucher                bool     `json:"has_voucher"`
	Mail                      string   `json:"mail"`
	PlanExpirationDate        string   `json:"plan_expiration_date"`
	Settings                  Settings `json:"settings"`
	SimultaneousDownloadLimit int      `json:"simultaneous_download_limit"`
	SubtitleLanguages         []string `json:"subtitle_languages"`
	UserID                    int64    `json:"user_id"`
	Username                  string   `json:"username"`
}

// Settings represents user's personal settings.
type Settings struct {
	CallbackURL             string      `json:"callback_url"`
	DefaultDownloadFolder   int64       `json:"default_download_folder"`
	DefaultSubtitleLanguage string      `json:"default_subtitle_language"`
	DownloadFolderUnset     bool        `json:"download_folder_unset"`
	IsInvisible             bool        `json:"is_invisible"`
	Nextepisode             bool        `json:"nextepisode"`
	PrivateDownloadHostIP   interface{} `json:"private_download_host_ip"`
	PushoverToken           string      `json:"pushover_token"`
	Routing                 string      `json:"routing"`
	Sorting                 string      `json:"sorting"`
	SSLEnabled              bool        `json:"ssl_enabled"`
	StartFrom               bool        `json:"start_from"`
	SubtitleLanguages       []string    `json:"subtitle_languages"`
}

// Friend represents Put.io user's friend.
type Friend struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
}

// Zip represents Put.io zip file.
type Zip struct {
	ID        int64 `json:"id"`
	CreatedAt *Time `json:"created_at"`

	Size   int64  `json:"size"`
	Status string `json:"status"`
	URL    string `json:"url"`
}

// Subtitle represents a subtitle.
type Subtitle struct {
	Key      string
	Language string
	Name     string
	Source   string
}

// Event represents a Put.io event. It could be a transfer or a shared file.
type Event struct {
	ID           int64  `json:"id"`
	FileID       int64  `json:"file_id"`
	Source       string `json:"source"`
	Type         string `json:"type"`
	TransferName string `json:"transfer_name"`
	TransferSize int64  `json:"transfer_size"`
	CreatedAt    *Time  `json:"created_at"`
}

type share struct {
	FileID   int64  `json:"file_id"`
	Filename string `json:"file_name"`
	// Number of friends the file is shared with
	SharedWith int64 `json:"shared_with"`
}
<|endoftext|>


<commit_before>package logrusmiddleware

import (
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
)

type (
	// Middleware is a middleware handler for HTTP logging
	Middleware struct {
		// Logger is the log.Logger instance used to log messages with the Logger middleware
		Logger *logrus.Logger
		// Name is the name of the application as recorded in latency metrics
		Name string
	}

	// Handler is the actual middleware that handles logging
	Handler struct {
		http.ResponseWriter
		status    int
		size      int
		m         *Middleware
		handler   http.Handler
		component string
	}
)

// Handler create a new handler. component, if set, is emitted in the log messages.
func (m *Middleware) Handler(h http.Handler, component string) *Handler {
	return &Handler{
		m:         m,
		handler:   h,
		component: component,
	}
}

// Write is a wrapper for the "real" ResponseWriter.Write
func (h *Handler) Write(b []byte) (int, error) {
	if h.status == 0 {
		// The status will be StatusOK if WriteHeader has not been called yet
		h.status = http.StatusOK
	}
	size, err := h.ResponseWriter.Write(b)
	h.size += size
	return size, err
}

// WriteHeader is a wrapper around ResponseWriter.WriteHeader
func (h *Handler) WriteHeader(s int) {
	h.ResponseWriter.WriteHeader(s)
	h.status = s
}

// Header is a wrapper around ResponseWriter.Header
func (h *Handler) Header() http.Header {
	return h.ResponseWriter.Header()
}

// ServeHTTP calls the "real" handler and logs using the logger
func (h *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	start := time.Now()

	h.handler.ServeHTTP(rw, r)

	latency := time.Since(start)

	fields := logrus.Fields{
		"status":   h.status,
		"method":   r.Method,
		"request":  r.RequestURI,
		"remote":   r.RemoteAddr,
		"duration": latency.Seconds(),
		"size":     h.size,
	}

	if h.m.Name != "" {
		fields["name"] = h.m.Name
	}

	if h.component != "" {
		fields["component"] = h.component
	}

	h.m.Logger.WithFields(fields).Info("completed handling request")
}
<commit_msg>package doc<commit_after>// logrusmiddleware is a simple net/http middleware for logging
// using logrus
package logrusmiddleware

import (
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
)

type (
	// Middleware is a middleware handler for HTTP logging
	Middleware struct {
		// Logger is the log.Logger instance used to log messages with the Logger middleware
		Logger *logrus.Logger
		// Name is the name of the application as recorded in latency metrics
		Name string
	}

	// Handler is the actual middleware that handles logging
	Handler struct {
		http.ResponseWriter
		status    int
		size      int
		m         *Middleware
		handler   http.Handler
		component string
	}
)

// Handler create a new handler. component, if set, is emitted in the log messages.
func (m *Middleware) Handler(h http.Handler, component string) *Handler {
	return &Handler{
		m:         m,
		handler:   h,
		component: component,
	}
}

// Write is a wrapper for the "real" ResponseWriter.Write
func (h *Handler) Write(b []byte) (int, error) {
	if h.status == 0 {
		// The status will be StatusOK if WriteHeader has not been called yet
		h.status = http.StatusOK
	}
	size, err := h.ResponseWriter.Write(b)
	h.size += size
	return size, err
}

// WriteHeader is a wrapper around ResponseWriter.WriteHeader
func (h *Handler) WriteHeader(s int) {
	h.ResponseWriter.WriteHeader(s)
	h.status = s
}

// Header is a wrapper around ResponseWriter.Header
func (h *Handler) Header() http.Header {
	return h.ResponseWriter.Header()
}

// ServeHTTP calls the "real" handler and logs using the logger
func (h *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	start := time.Now()

	h.handler.ServeHTTP(rw, r)

	latency := time.Since(start)

	fields := logrus.Fields{
		"status":   h.status,
		"method":   r.Method,
		"request":  r.RequestURI,
		"remote":   r.RemoteAddr,
		"duration": latency.Seconds(),
		"size":     h.size,
	}

	if h.m.Name != "" {
		fields["name"] = h.m.Name
	}

	if h.component != "" {
		fields["component"] = h.component
	}

	h.m.Logger.WithFields(fields).Info("completed handling request")
}
<|endoftext|>


<commit_before>// Copyright 2012 Andreas Louca. All rights reserved.
// Use of this source code is goverend by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"./gosnmp"
	"flag"
	"fmt"
)

var (
	cmdCommunity string
	cmdTarget    string
	cmdOid       string
)

func init() {
	flag.StringVar(&cmdTarget, "target", "", "Target SNMP Agent")
	flag.StringVar(&cmdCommunity, "community", "", "SNNP Community")
	flag.StringVar(&cmdOid, "oid", "", "OID")
	flag.Parse()
}

func main() {
	s := gosnmp.NewGoSNMP(cmdTarget, cmdCommunity, 1)
	resp := s.Get(cmdOid)
	fmt.Printf("%s -> %s\n", cmdOid, resp)
}
<commit_msg>Updated Example to reflect changes in GoSNMP library<commit_after>// Copyright 2012 Andreas Louca. All rights reserved.
// Use of this source code is goverend by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/alouca/gosnmp"
)

var (
	cmdCommunity string
	cmdTarget    string
	cmdOid       string
)

func init() {
	flag.StringVar(&cmdTarget, "target", "", "Target SNMP Agent")
	flag.StringVar(&cmdCommunity, "community", "", "SNNP Community")
	flag.StringVar(&cmdOid, "oid", "", "OID")
	flag.Parse()
}

func main() {
	s := gosnmp.NewGoSNMP(cmdTarget, cmdCommunity, 1)
	resp, err := s.Get(cmdOid)

	if err != nil {
		fmt.Printf("Error during SNMP GET: %s\n", err.Error())
	} else {
		fmt.Printf("%s -> ", cmdOid)
		switch resp.Type {
		case gosnmp.OctetString:
			if s, ok := resp.Value.(string); ok {
				fmt.Printf("%s\n", s)
			} else {
				fmt.Printf("Response is not a string\n")
			}
		default:
			fmt.Printf("Type: %d\n", resp.Type)
		}
	}

}
<|endoftext|>


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

package main

import (
	"fmt"
	"github.ibm.com/riethm/gopherlayer.git/filter"
)

func main() {
	fmt.Println(filter.Path("virtualGuests.hostname").Eq("example.com").Build())

	fmt.Println(
		filter.New(
			filter.Path("id").Eq("134"),
			filter.Path("datacenter.locationName").Eq("Dallas"),
			filter.Path("something.creationDate").Date("01/01/01"),
		).Build(),
	)

	fmt.Println(
		filter.Build(
			filter.Path("virtualGuests.domain").Eq("example.com"),
			filter.Path("virtualGuests.id").NotEq(12345),
		),
	)

	filters := filter.New(
		filter.Path("virtualGuests.hostname").StartsWith("KM078"),
		filter.Path("virtualGuests.id").NotEq(12345),
	)

	filters = append(filters, filter.Path("virtualGuests.domain").Eq("example.com"))

	fmt.Println(filters.Build())
}
<commit_msg>Do not use main() for the examples. Avoid main redeclarations<commit_after>/**
 * Copyright 2016 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"fmt"
	"github.ibm.com/riethm/gopherlayer.git/filter"
)

func testFilters() {
	fmt.Println(filter.Path("virtualGuests.hostname").Eq("example.com").Build())

	fmt.Println(
		filter.New(
			filter.Path("id").Eq("134"),
			filter.Path("datacenter.locationName").Eq("Dallas"),
			filter.Path("something.creationDate").Date("01/01/01"),
		).Build(),
	)

	fmt.Println(
		filter.Build(
			filter.Path("virtualGuests.domain").Eq("example.com"),
			filter.Path("virtualGuests.id").NotEq(12345),
		),
	)

	filters := filter.New(
		filter.Path("virtualGuests.hostname").StartsWith("KM078"),
		filter.Path("virtualGuests.id").NotEq(12345),
	)

	filters = append(filters, filter.Path("virtualGuests.domain").Eq("example.com"))

	fmt.Println(filters.Build())
}
<|endoftext|>


<commit_before>package dukedb

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/theduke/go-apperror"
)

/**
 * Query parser functions.
 */

func ParseJsonQuery(collection string, js []byte) (Query, apperror.Error) {
	var data map[string]interface{}
	if err := json.Unmarshal(js, &data); err != nil {
		return nil, &apperror.Err{
			Code:    "invalid_json",
			Message: "Query json could not be unmarshaled. Check for invalid json.",
		}
	}

	return ParseQuery(collection, data)
}

// Build a database query based a map[string]interface{} data structure
// resembling a Mongo query.
//
// It returns a Query equal to the Mongo query, with unsupported features omitted.
// An error is returned if the building of the query fails.
//
// Format: {
//   // Order by field:
//   order: "field",
//  //  Order descending:
//  order: "-field",
//
//  // Joins:
//  joins: ["myJoin", "my.nestedJoin"],
//
//  // Filters:
//  Filters conform to the mongo query syntax.
//  See http://docs.mongodb.org/manual/reference/operator/query/.
//  filters: {
//  	id: "22",
//    weight: {$gt: 222},
//    type: {$in: ["type1", "type2"]}
//  },
//
//  // Limiting:
//  limit: 100,
//
//  // Offset:
//  offset: 20
// }
//
func ParseQuery(collection string, data map[string]interface{}) (Query, apperror.Error) {
	q := Q(collection)

	// First, Handle joins so query and field specification parsing can use
	// join info.
	if rawJoins, ok := data["joins"]; ok {
		rawJoinSlice, ok := rawJoins.([]interface{})
		if !ok {
			return nil, &apperror.Err{
				Code:    "invalid_joins",
				Message: "Joins must be an array of strings",
			}
		}

		// Convert []interface{} joins to []string.

		joins := make([]string, 0)
		for _, rawJoin := range rawJoinSlice {
			join, ok := rawJoin.(string)
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_joins",
					Message: "Joins must be an array of strings",
				}
			}
			joins = append(joins, join)
		}

		// To handle nested joins, parseQueryJoins has to be called repeatedly
		// until no more joins are returned.
		for depth := 1; true; depth++ {
			var err apperror.Error
			joins, err = parseQueryJoins(q, joins, depth)
			if err != nil {
				return nil, err
			}

			if len(joins) == 0 {
				break
			}
		}
	}

	// Handle filters.

	if rawQuery, ok := data["filters"]; ok {
		query, ok := rawQuery.(map[string]interface{})
		if !ok {
			return nil, &apperror.Err{
				Code:    "invalid_filters",
				Message: "The filters key must contain a dict",
			}
		}

		if err := parseQueryFilters(q, query); err != nil {
			return nil, err
		}
	}

	// Handle fields.
	if rawFields, ok := data["fields"]; ok {
		fields, ok := rawFields.([]interface{})
		if !ok {
			return nil, &apperror.Err{
				Code:    "invalid_fields",
				Message: "Fields specification must be an array",
			}
		}

		for _, rawField := range fields {
			field, ok := rawField.(string)
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_fields",
					Message: "Fields specification must be an array of strings",
				}
			}

			parts := strings.Split(field, ".")
			if len(parts) > 1 {
				// Possibly a field on a joined model. Check if a parent join can be found.
				joinQ := q.GetJoin(strings.Join(parts[:len(parts)-1], "."))
				if joinQ != nil {
					// Join query found, add field to the join query.
					joinQ.AddFields(parts[len(parts)-1])
				} else {
					// No join query found, maybe the backend supports nested fields.
					joinQ.AddFields(field)
				}
			} else {
				// Not nested, just add the field.
				q.AddFields(field)
			}
		}
	}

	// Handle orders.
	if rawOrders, ok := data["order"]; ok {
		var orders []interface{}

		// Order may either be a single string, or a list of strings.

		if oneOrder, ok := rawOrders.(string); ok {
			orders = []interface{}{oneOrder}
		} else {
			var ok bool
			orders, ok = rawOrders.([]interface{})
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_orders",
					Message: "Order specification must be an array",
				}
			}
		}

		for _, rawOrder := range orders {
			field, ok := rawOrder.(string)
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_orders",
					Message: "Order specification must be an array of strings.",
				}
			}

			ascending := true
			if field[0] == '-' {
				ascending = false
				field = strings.TrimLeft(field, "-")
			} else if field[0] == '+' {
				field = strings.TrimLeft(field, "-")
			}

			if field == "" {
				return nil, &apperror.Err{
					Code:    "invalid_orders_empty_field",
					Message: "Order specification is empty",
				}
			}

			q.Order(field, ascending)
		}
	}

	// Handle limit.
	if rawLimit, ok := data["limit"]; ok {
		if limit, err := NumericToInt64(rawLimit); err != nil {
			return nil, &apperror.Err{
				Code:    "limit_non_numeric",
				Message: "Limit must be a number",
			}
		} else {
			q.Limit(int(limit))
		}
	}

	// Handle offset.
	if rawOffset, ok := data["offset"]; ok {
		if offset, err := NumericToInt64(rawOffset); err != nil {
			return nil, &apperror.Err{
				Code:    "offset_non_numeric",
				Message: "Offset must be a number",
			}
		} else {
			q.Offset(int(offset))
		}
	}

	return q, nil
}

func parseQueryJoins(q Query, joins []string, depth int) ([]string, apperror.Error) {
	remaining := make([]string, 0)

	for _, name := range joins {
		parts := strings.Split(name, ".")
		joinDepth := len(parts)
		if joinDepth == depth {
			// The depth of the join equals to the one that should be processed, so do!
			if len(parts) > 1 {
				// Nested join! So try to retrieve the parent join query.
				joinQ := q.GetJoin(strings.Join(parts[:joinDepth-1], "."))
				if joinQ == nil {
					// Parent join not found, obviosly an error.
					return nil, &apperror.Err{
						Code:    "invalid_nested_join",
						Message: fmt.Sprintf("Tried to join %v, but the parent join was not found", name),
					}
				}
				// Join the current join on the parent join.
				joinQ.Join(parts[len(parts)-1])
			} else {
				// Not nested, just join on the main query.
				q.Join(name)
			}
		} else {
			// Join has other depth than the one that is processed, so append to
			// remaining.
			remaining = append(remaining, name)
		}
	}

	return remaining, nil
}

func parseQueryFilters(q Query, filters map[string]interface{}) apperror.Error {
	filter, err := parseQueryFilter("", filters)
	if err != nil {
		return err
	}

	// If filter is an and, add the and clauses separately to the query.
	// Done for prettier query without top level AND.
	if andFilter, ok := filter.(*AndCondition); ok {
		for _, filter := range andFilter.Filters {
			q.FilterQ(filter)
		}
	} else {
		q.FilterQ(filter)
	}

	return nil
}

// Parses a mongo query filter to a Filter.
// All mongo operators expect $nor are supported.
// Refer to http://docs.mongodb.org/manual/reference/operator/query.
func parseQueryFilter(name string, data interface{}) (Filter, apperror.Error) {
	// Handle
	switch name {
	case "$eq":
		return Eq("placeholder", data), nil
	case "$ne":
		return Neq("placeholder", data), nil
	case "$in":
		return In("placeholder", data), nil
	case "$like":
		return Like("placeholder", data), nil
	case "$gt":
		return Gt("placeholder", data), nil
	case "$gte":
		return Gte("placeholder", data), nil
	case "$lt":
		return Lt("placeholder", data), nil
	case "$lte":
		return Lte("placeholder", data), nil
	case "$nin":
		return Not(In("placeholder", data)), nil
	}

	if name == "$nor" {
		return nil, &apperror.Err{
			Code:    "unsupported_nor_query",
			Message: "$nor queryies are not supported",
		}
	}

	// Handle OR.
	if name == "$or" {
		orClauses, ok := data.([]interface{})
		if !ok {
			return nil, &apperror.Err{Code: "invalid_or_data"}
		}

		or := Or()
		for _, rawClause := range orClauses {
			clause, ok := rawClause.(map[string]interface{})
			if !ok {
				return nil, &apperror.Err{Code: "invalid_or_data"}
			}

			filter, err := parseQueryFilter("", clause)
			if err != nil {
				return nil, err
			}
			or.Add(filter)
		}

		return or, nil
	}

	if nestedData, ok := data.(map[string]interface{}); ok {
		// Nested dict with multipe AND clauses.

		// Build an AND filter.
		and := And()
		for key := range nestedData {
			filter, err := parseQueryFilter(key, nestedData[key])
			if err != nil {
				return nil, err
			}

			if key == "$or" || key == "$and" || key == "$not" {
				// Do nothing
			} else if name == "" {
				filter.SetField(key)
			} else {
				filter.SetField(name)
			}

			and.Add(filter)
		}

		if len(and.Filters) == 1 {
			return and.Filters[0], nil
		} else {
			return and, nil
		}
	}

	// If execution reaches this point, the filter must be a simple equals filter
	// with a value.
	return Eq(name, data), nil
}
<commit_msg>QueryParser now supports filters on joins<commit_after>package dukedb

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/theduke/go-apperror"
)

/**
 * Query parser functions.
 */

func ParseJsonQuery(collection string, js []byte) (Query, apperror.Error) {
	var data map[string]interface{}
	if err := json.Unmarshal(js, &data); err != nil {
		return nil, &apperror.Err{
			Code:    "invalid_json",
			Message: "Query json could not be unmarshaled. Check for invalid json.",
		}
	}

	return ParseQuery(collection, data)
}

// Build a database query based a map[string]interface{} data structure
// resembling a Mongo query.
//
// It returns a Query equal to the Mongo query, with unsupported features omitted.
// An error is returned if the building of the query fails.
//
// Format: {
//   // Order by field:
//   order: "field",
//  //  Order descending:
//  order: "-field",
//
//  // Joins:
//  joins: ["myJoin", "my.nestedJoin"],
//
//  // Filters:
//  Filters conform to the mongo query syntax.
//  See http://docs.mongodb.org/manual/reference/operator/query/.
//  filters: {
//  	id: "22",
//    weight: {$gt: 222},
//    type: {$in: ["type1", "type2"]}
//  },
//
//  // Limiting:
//  limit: 100,
//
//  // Offset:
//  offset: 20
// }
//
func ParseQuery(collection string, data map[string]interface{}) (Query, apperror.Error) {
	q := Q(collection)

	// First, Handle joins so query and field specification parsing can use
	// join info.
	if rawJoins, ok := data["joins"]; ok {
		rawJoinSlice, ok := rawJoins.([]interface{})
		if !ok {
			return nil, &apperror.Err{
				Code:    "invalid_joins",
				Message: "Joins must be an array of strings",
			}
		}

		// Convert []interface{} joins to []string.

		joins := make([]string, 0)
		for _, rawJoin := range rawJoinSlice {
			join, ok := rawJoin.(string)
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_joins",
					Message: "Joins must be an array of strings",
				}
			}
			joins = append(joins, join)
		}

		// To handle nested joins, parseQueryJoins has to be called repeatedly
		// until no more joins are returned.
		for depth := 1; true; depth++ {
			var err apperror.Error
			joins, err = parseQueryJoins(q, joins, depth)
			if err != nil {
				return nil, err
			}

			if len(joins) == 0 {
				break
			}
		}
	}

	// Handle filters.

	if rawQuery, ok := data["filters"]; ok {
		query, ok := rawQuery.(map[string]interface{})
		if !ok {
			return nil, &apperror.Err{
				Code:    "invalid_filters",
				Message: "The filters key must contain a dict",
			}
		}

		if err := parseQueryFilters(q, query); err != nil {
			return nil, err
		}
	}

	// Handle fields.
	if rawFields, ok := data["fields"]; ok {
		fields, ok := rawFields.([]interface{})
		if !ok {
			return nil, &apperror.Err{
				Code:    "invalid_fields",
				Message: "Fields specification must be an array",
			}
		}

		for _, rawField := range fields {
			field, ok := rawField.(string)
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_fields",
					Message: "Fields specification must be an array of strings",
				}
			}

			parts := strings.Split(field, ".")
			if len(parts) > 1 {
				// Possibly a field on a joined model. Check if a parent join can be found.
				joinQ := q.GetJoin(strings.Join(parts[:len(parts)-1], "."))
				if joinQ != nil {
					// Join query found, add field to the join query.
					joinQ.AddFields(parts[len(parts)-1])
				} else {
					// No join query found, maybe the backend supports nested fields.
					joinQ.AddFields(field)
				}
			} else {
				// Not nested, just add the field.
				q.AddFields(field)
			}
		}
	}

	// Handle orders.
	if rawOrders, ok := data["order"]; ok {
		var orders []interface{}

		// Order may either be a single string, or a list of strings.

		if oneOrder, ok := rawOrders.(string); ok {
			orders = []interface{}{oneOrder}
		} else {
			var ok bool
			orders, ok = rawOrders.([]interface{})
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_orders",
					Message: "Order specification must be an array",
				}
			}
		}

		for _, rawOrder := range orders {
			field, ok := rawOrder.(string)
			if !ok {
				return nil, &apperror.Err{
					Code:    "invalid_orders",
					Message: "Order specification must be an array of strings.",
				}
			}

			ascending := true
			if field[0] == '-' {
				ascending = false
				field = strings.TrimLeft(field, "-")
			} else if field[0] == '+' {
				field = strings.TrimLeft(field, "-")
			}

			if field == "" {
				return nil, &apperror.Err{
					Code:    "invalid_orders_empty_field",
					Message: "Order specification is empty",
				}
			}

			q.Order(field, ascending)
		}
	}

	// Handle limit.
	if rawLimit, ok := data["limit"]; ok {
		if limit, err := NumericToInt64(rawLimit); err != nil {
			return nil, &apperror.Err{
				Code:    "limit_non_numeric",
				Message: "Limit must be a number",
			}
		} else {
			q.Limit(int(limit))
		}
	}

	// Handle offset.
	if rawOffset, ok := data["offset"]; ok {
		if offset, err := NumericToInt64(rawOffset); err != nil {
			return nil, &apperror.Err{
				Code:    "offset_non_numeric",
				Message: "Offset must be a number",
			}
		} else {
			q.Offset(int(offset))
		}
	}

	return q, nil
}

func parseQueryJoins(q Query, joins []string, depth int) ([]string, apperror.Error) {
	remaining := make([]string, 0)

	for _, name := range joins {
		parts := strings.Split(name, ".")
		joinDepth := len(parts)
		if joinDepth == depth {
			// The depth of the join equals to the one that should be processed, so do!
			if len(parts) > 1 {
				// Nested join! So try to retrieve the parent join query.
				joinQ := q.GetJoin(strings.Join(parts[:joinDepth-1], "."))
				if joinQ == nil {
					// Parent join not found, obviosly an error.
					return nil, &apperror.Err{
						Code:    "invalid_nested_join",
						Message: fmt.Sprintf("Tried to join %v, but the parent join was not found", name),
					}
				}
				// Join the current join on the parent join.
				joinQ.Join(parts[len(parts)-1])
			} else {
				// Not nested, just join on the main query.
				q.Join(name)
			}
		} else {
			// Join has other depth than the one that is processed, so append to
			// remaining.
			remaining = append(remaining, name)
		}
	}

	return remaining, nil
}

func parseQueryFilters(q Query, filters map[string]interface{}) apperror.Error {
	filter, err := parseQueryFilter("", filters, q)
	if err != nil {
		return err
	}

	// If filter is an and, add the and clauses separately to the query.
	// Done for prettier query without top level AND.
	if andFilter, ok := filter.(*AndCondition); ok {
		for _, filter := range andFilter.Filters {
			q.FilterQ(filter)
		}
	} else {
		q.FilterQ(filter)
	}

	return nil
}

// Parses a mongo query filter to a Filter.
// All mongo operators expect $nor are supported.
// Refer to http://docs.mongodb.org/manual/reference/operator/query.
func parseQueryFilter(name string, data interface{}, query Query) (Filter, apperror.Error) {
	// Handle
	switch name {
	case "$eq":
		return Eq("placeholder", data), nil
	case "$ne":
		return Neq("placeholder", data), nil
	case "$in":
		return In("placeholder", data), nil
	case "$like":
		return Like("placeholder", data), nil
	case "$gt":
		return Gt("placeholder", data), nil
	case "$gte":
		return Gte("placeholder", data), nil
	case "$lt":
		return Lt("placeholder", data), nil
	case "$lte":
		return Lte("placeholder", data), nil
	case "$nin":
		return Not(In("placeholder", data)), nil
	}

	if name == "$nor" {
		return nil, &apperror.Err{
			Code:    "unsupported_nor_query",
			Message: "$nor queryies are not supported",
		}
	}

	// Handle OR.
	if name == "$or" {
		orClauses, ok := data.([]interface{})
		if !ok {
			return nil, &apperror.Err{Code: "invalid_or_data"}
		}

		or := Or()
		for _, rawClause := range orClauses {
			clause, ok := rawClause.(map[string]interface{})
			if !ok {
				return nil, &apperror.Err{Code: "invalid_or_data"}
			}

			filter, err := parseQueryFilter("", clause, query)
			if err != nil {
				return nil, err
			}
			or.Add(filter)
		}

		return or, nil
	}

	if nestedData, ok := data.(map[string]interface{}); ok {
		// Nested dict with multipe AND clauses.

		// Build an AND filter.
		and := And()
		for key := range nestedData {
			filter, err := parseQueryFilter(key, nestedData[key], query)
			if err != nil {
				return nil, err
			}

			doAdd := true

			if key == "$or" || key == "$and" || key == "$not" {
				// Do nothing
			} else {
				field := name
				if field == "" {
					field = key
				}

				// Check for joins.

				parts := strings.Split(field, ".")
				if len(parts) > 1 {
					// Possibly a field on a joined model. Check if a parent join can be found.
					joinQ := query.GetJoin(strings.Join(parts[:len(parts)-1], "."))
					if joinQ != nil {
						// Join query found, add filter to the join query.
						filter.SetField(parts[len(parts)-1])
						joinQ.FilterQ(filter)
						// Set flag to prevent adding to regular query.
						doAdd = false
					}
				}

				if doAdd {
					filter.SetField(field)
				}
			}

			if doAdd {
				and.Add(filter)
			}
		}

		if len(and.Filters) == 1 {
			return and.Filters[0], nil
		} else {
			return and, nil
		}
	}

	// If execution reaches this point, the filter must be a simple equals filter
	// with a value.
	return Eq(name, data), nil
}
<|endoftext|>


<commit_before>package gojenkins

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

type Auth struct {
	Username string
	ApiToken string
}

type Jenkins struct {
	auth    *Auth
	baseUrl string
}

func NewJenkins(auth *Auth, baseUrl string) *Jenkins {
	return &Jenkins{
		auth:    auth,
		baseUrl: baseUrl,
	}
}

func (jenkins *Jenkins) buildUrl(path string, params url.Values) (requestUrl string) {
	requestUrl = jenkins.baseUrl + path + "/api/json"
	if params != nil {
		queryString := params.Encode()
		if queryString != "" {
			requestUrl = requestUrl + "?" + queryString
		}
	}

	return
}

func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) {
	if jenkins.auth != nil {
		req.SetBasicAuth(jenkins.auth.Username, jenkins.auth.ApiToken)
	}
	return http.DefaultClient.Do(req)
}

func (jenkins *Jenkins) parseXmlResponse(resp *http.Response, body interface{}) (err error) {
	defer resp.Body.Close()

	if body == nil {
		return
	}

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

	return xml.Unmarshal(data, body)
}

func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) {
	defer resp.Body.Close()

	if body == nil {
		return
	}

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

	return json.Unmarshal(data, body)
}

func (jenkins *Jenkins) get(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}
	return jenkins.parseResponse(resp, body)
}

func (jenkins *Jenkins) getXml(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}
	return jenkins.parseXmlResponse(resp, body)
}

func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("POST", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}

	return jenkins.parseResponse(resp, body)
}
func (jenkins *Jenkins) postXml(path string, params url.Values, xmlBody io.Reader, body interface{}) (err error) {
	requestUrl := jenkins.baseUrl + path
	if params != nil {
		queryString := params.Encode()
		if queryString != "" {
			requestUrl = requestUrl + "?" + queryString
		}
	}

	req, err := http.NewRequest("POST", requestUrl, xmlBody)
	if err != nil {
		return
	}

	req.Header.Add("Content-Type", "application/xml")
	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}
	if resp.StatusCode != 200 {
		return errors.New(fmt.Sprintf("error: HTTP POST returned status code returned: %d", resp.StatusCode))
	}

	return jenkins.parseXmlResponse(resp, body)
}

// GetJobs returns all jobs you can read.
func (jenkins *Jenkins) GetJobs() ([]Job, error) {
	var payload = struct {
		Jobs []Job `json:"jobs"`
	}{}
	err := jenkins.get("", nil, &payload)
	return payload.Jobs, err
}

// GetJob returns a job which has specified name.
func (jenkins *Jenkins) GetJob(name string) (job Job, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s", name), nil, &job)
	return
}

//GetJobConfig returns a maven job, has the one used to create Maven job
func (jenkins *Jenkins) GetJobConfig(name string) (job MavenJobItem, err error) {
	err = jenkins.getXml(fmt.Sprintf("/job/%s/config.xml", name), nil, &job)
	return
}

// GetBuild returns a number-th build result of specified job.
func (jenkins *Jenkins) GetBuild(job Job, number int) (build Build, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s/%d", job.Name, number), nil, &build)
	return
}

// Create a new job
func (jenkins *Jenkins) CreateJob(mavenJobItem MavenJobItem, jobName string) error {
	mavenJobItemXml, _ := xml.Marshal(mavenJobItem)
	reader := bytes.NewReader(mavenJobItemXml)
	params := url.Values{"name": []string{jobName}}

	return jenkins.postXml("/createItem", params, reader, nil)
}

// Add job to view
func (jenkins *Jenkins) AddJobToView(viewName string, job Job) error {
	params := url.Values{"name": []string{job.Name}}
	return jenkins.post(fmt.Sprintf("/view/%s/addJobToView", viewName), params, nil)
}

// Create a new view
func (jenkins *Jenkins) CreateView(listView ListView) error {
	xmlListView, _ := xml.Marshal(listView)
	reader := bytes.NewReader(xmlListView)
	params := url.Values{"name": []string{listView.Name}}

	return jenkins.postXml("/createView", params, reader, nil)
}

// Create a new build for this job.
// Params can be nil.
func (jenkins *Jenkins) Build(job Job, params url.Values) error {
	if params == nil {
		return jenkins.post(fmt.Sprintf("/job/%s/build", job.Name), params, nil)
	} else {
		return jenkins.post(fmt.Sprintf("/job/%s/buildWithParameters", job.Name), params, nil)
	}
}

// Get the console output from a build.
func (jenkins *Jenkins) GetBuildConsoleOutput(build Build) ([]byte, error) {
	requestUrl := fmt.Sprintf("%s/consoleText", build.Url)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return nil, err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	return ioutil.ReadAll(res.Body)
}

// GetQueue returns the current build queue from Jenkins
func (jenkins *Jenkins) GetQueue() (queue Queue, err error) {
	err = jenkins.get(fmt.Sprintf("/queue"), nil, &queue)
	return
}

// GetArtifact return the content of a build artifact
func (jenkins *Jenkins) GetArtifact(build Build, artifact Artifact) ([]byte, error) {
	requestUrl := fmt.Sprintf("%s/artifact/%s", build.Url, artifact.RelativePath)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return nil, err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	return ioutil.ReadAll(res.Body)
}

// SetBuildDescription sets the description of a build
func (jenkins *jenkins) SetBuildDescription(build Build, description string) error {
	requestUrl := fmt.Sprintf("%ssubmitDescription?description=%s", build.Url, url.QueryEscape(description))
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return fmt.Errorf("Unexpected response: expected '200' but received '%d'", res.StatusCode)
	}

	return nil
}

// GetComputerObject returns the main ComputerObject
func (jenkins *Jenkins) GetComputerObject() (co ComputerObject, err error) {
	err = jenkins.get(fmt.Sprintf("/computer"), nil, &co)
	return
}

// GetComputers returns the list of all Computer objects
func (jenkins *Jenkins) GetComputers() ([]Computer, error) {
	var payload = struct {
		Computers []Computer `json:"computer"`
	}{}
	err := jenkins.get("/computer", nil, &payload)
	return payload.Computers, err
}

// GetComputer returns a Computer object with a specified name.
func (jenkins *Jenkins) GetComputer(name string) (computer Computer, err error) {
	err = jenkins.get(fmt.Sprintf("/computer/%s", name), nil, &computer)
	return
}
<commit_msg>Revert "Adds SetBuildDescription"<commit_after>package gojenkins

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

type Auth struct {
	Username string
	ApiToken string
}

type Jenkins struct {
	auth    *Auth
	baseUrl string
}

func NewJenkins(auth *Auth, baseUrl string) *Jenkins {
	return &Jenkins{
		auth:    auth,
		baseUrl: baseUrl,
	}
}

func (jenkins *Jenkins) buildUrl(path string, params url.Values) (requestUrl string) {
	requestUrl = jenkins.baseUrl + path + "/api/json"
	if params != nil {
		queryString := params.Encode()
		if queryString != "" {
			requestUrl = requestUrl + "?" + queryString
		}
	}

	return
}

func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) {
	if jenkins.auth != nil {
		req.SetBasicAuth(jenkins.auth.Username, jenkins.auth.ApiToken)
	}
	return http.DefaultClient.Do(req)
}

func (jenkins *Jenkins) parseXmlResponse(resp *http.Response, body interface{}) (err error) {
	defer resp.Body.Close()

	if body == nil {
		return
	}

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

	return xml.Unmarshal(data, body)
}

func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) {
	defer resp.Body.Close()

	if body == nil {
		return
	}

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

	return json.Unmarshal(data, body)
}

func (jenkins *Jenkins) get(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}
	return jenkins.parseResponse(resp, body)
}

func (jenkins *Jenkins) getXml(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}
	return jenkins.parseXmlResponse(resp, body)
}

func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (err error) {
	requestUrl := jenkins.buildUrl(path, params)
	req, err := http.NewRequest("POST", requestUrl, nil)
	if err != nil {
		return
	}

	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}

	return jenkins.parseResponse(resp, body)
}
func (jenkins *Jenkins) postXml(path string, params url.Values, xmlBody io.Reader, body interface{}) (err error) {
	requestUrl := jenkins.baseUrl + path
	if params != nil {
		queryString := params.Encode()
		if queryString != "" {
			requestUrl = requestUrl + "?" + queryString
		}
	}

	req, err := http.NewRequest("POST", requestUrl, xmlBody)
	if err != nil {
		return
	}

	req.Header.Add("Content-Type", "application/xml")
	resp, err := jenkins.sendRequest(req)
	if err != nil {
		return
	}
	if resp.StatusCode != 200 {
		return errors.New(fmt.Sprintf("error: HTTP POST returned status code returned: %d", resp.StatusCode))
	}

	return jenkins.parseXmlResponse(resp, body)
}

// GetJobs returns all jobs you can read.
func (jenkins *Jenkins) GetJobs() ([]Job, error) {
	var payload = struct {
		Jobs []Job `json:"jobs"`
	}{}
	err := jenkins.get("", nil, &payload)
	return payload.Jobs, err
}

// GetJob returns a job which has specified name.
func (jenkins *Jenkins) GetJob(name string) (job Job, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s", name), nil, &job)
	return
}

//GetJobConfig returns a maven job, has the one used to create Maven job
func (jenkins *Jenkins) GetJobConfig(name string) (job MavenJobItem, err error) {
	err = jenkins.getXml(fmt.Sprintf("/job/%s/config.xml", name), nil, &job)
	return
}

// GetBuild returns a number-th build result of specified job.
func (jenkins *Jenkins) GetBuild(job Job, number int) (build Build, err error) {
	err = jenkins.get(fmt.Sprintf("/job/%s/%d", job.Name, number), nil, &build)
	return
}

// Create a new job
func (jenkins *Jenkins) CreateJob(mavenJobItem MavenJobItem, jobName string) error {
	mavenJobItemXml, _ := xml.Marshal(mavenJobItem)
	reader := bytes.NewReader(mavenJobItemXml)
	params := url.Values{"name": []string{jobName}}

	return jenkins.postXml("/createItem", params, reader, nil)
}

// Add job to view
func (jenkins *Jenkins) AddJobToView(viewName string, job Job) error {
	params := url.Values{"name": []string{job.Name}}
	return jenkins.post(fmt.Sprintf("/view/%s/addJobToView", viewName), params, nil)
}

// Create a new view
func (jenkins *Jenkins) CreateView(listView ListView) error {
	xmlListView, _ := xml.Marshal(listView)
	reader := bytes.NewReader(xmlListView)
	params := url.Values{"name": []string{listView.Name}}

	return jenkins.postXml("/createView", params, reader, nil)
}

// Create a new build for this job.
// Params can be nil.
func (jenkins *Jenkins) Build(job Job, params url.Values) error {
	if params == nil {
		return jenkins.post(fmt.Sprintf("/job/%s/build", job.Name), params, nil)
	} else {
		return jenkins.post(fmt.Sprintf("/job/%s/buildWithParameters", job.Name), params, nil)
	}
}

// Get the console output from a build.
func (jenkins *Jenkins) GetBuildConsoleOutput(build Build) ([]byte, error) {
	requestUrl := fmt.Sprintf("%s/consoleText", build.Url)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return nil, err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	return ioutil.ReadAll(res.Body)
}

// GetQueue returns the current build queue from Jenkins
func (jenkins *Jenkins) GetQueue() (queue Queue, err error) {
	err = jenkins.get(fmt.Sprintf("/queue"), nil, &queue)
	return
}

// GetArtifact return the content of a build artifact
func (jenkins *Jenkins) GetArtifact(build Build, artifact Artifact) ([]byte, error) {
	requestUrl := fmt.Sprintf("%s/artifact/%s", build.Url, artifact.RelativePath)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		return nil, err
	}

	res, err := jenkins.sendRequest(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	return ioutil.ReadAll(res.Body)
}

// GetComputerObject returns the main ComputerObject
func (jenkins *Jenkins) GetComputerObject() (co ComputerObject, err error) {
	err = jenkins.get(fmt.Sprintf("/computer"), nil, &co)
	return
}

// GetComputers returns the list of all Computer objects
func (jenkins *Jenkins) GetComputers() ([]Computer, error) {
	var payload = struct {
		Computers []Computer `json:"computer"`
	}{}
	err := jenkins.get("/computer", nil, &payload)
	return payload.Computers, err
}

// GetComputer returns a Computer object with a specified name.
func (jenkins *Jenkins) GetComputer(name string) (computer Computer, err error) {
	err = jenkins.get(fmt.Sprintf("/computer/%s", name), nil, &computer)
	return
}
<|endoftext|>


<commit_before>package lxd

import (
	"fmt"

	"github.com/lxc/lxd/shared/api"
)

// GetCluster returns information about a cluster
//
// If this client is not trusted, the password must be supplied
func (r *ProtocolLXD) GetCluster() (*api.Cluster, string, error) {
	if !r.HasExtension("clustering") {
		return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	cluster := &api.Cluster{}
	etag, err := r.queryStruct("GET", "/cluster", nil, "", &cluster)
	if err != nil {
		return nil, "", err
	}

	return cluster, etag, nil
}

// UpdateCluster requests to bootstrap a new cluster or join an existing one.
func (r *ProtocolLXD) UpdateCluster(cluster api.ClusterPut, ETag string) (Operation, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	if cluster.ServerAddress != "" || cluster.ClusterPassword != "" || len(cluster.MemberConfig) > 0 {
		if !r.HasExtension("clustering_join") {
			return nil, fmt.Errorf("The server is missing the required \"clustering_join\" API extension")
		}
	}

	op, _, err := r.queryOperation("PUT", "/cluster", cluster, "")
	if err != nil {
		return nil, err
	}

	return op, nil
}

// DeleteClusterMember makes the given member leave the cluster (gracefully or not,
// depending on the force flag)
func (r *ProtocolLXD) DeleteClusterMember(name string, force bool) error {
	if !r.HasExtension("clustering") {
		return fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	params := ""
	if force {
		params += "?force=1"
	}

	_, err := r.queryStruct("DELETE", fmt.Sprintf("/cluster/members/%s%s", name, params), nil, "", nil)
	if err != nil {
		return err
	}

	return nil
}

// GetClusterMemberNames returns the URLs of the current members in the cluster
func (r *ProtocolLXD) GetClusterMemberNames() ([]string, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	urls := []string{}
	_, err := r.queryStruct("GET", "/cluster/members", nil, "", &urls)
	if err != nil {
		return nil, err
	}

	return urls, nil
}

// GetClusterMembers returns the current members of the cluster
func (r *ProtocolLXD) GetClusterMembers() ([]api.ClusterMember, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	members := []api.ClusterMember{}
	_, err := r.queryStruct("GET", "/cluster/members?recursion=1", nil, "", &members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

// GetClusterMember returns information about the given member
func (r *ProtocolLXD) GetClusterMember(name string) (*api.ClusterMember, string, error) {
	if !r.HasExtension("clustering") {
		return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	member := api.ClusterMember{}
	etag, err := r.queryStruct("GET", fmt.Sprintf("/cluster/members/%s", name), nil, "", &member)
	if err != nil {
		return nil, "", err
	}

	return &member, etag, nil
}

// UpdateClusterMember updates information about the given member
func (r *ProtocolLXD) UpdateClusterMember(name string, member api.ClusterMemberPut, ETag string) error {
	if !r.HasExtension("clustering_edit_roles") {
		return fmt.Errorf("The server is missing the required \"clustering_edit_roles\" API extension")
	}
	if member.FailureDomain != "" {
		if !r.HasExtension("clustering_failure_domains") {
			return fmt.Errorf("The server is missing the required \"clustering_failure_domains\" API extension")
		}
	}

	// Send the request
	_, _, err := r.query("PUT", fmt.Sprintf("/cluster/members/%s", name), member, ETag)
	if err != nil {
		return err
	}

	return nil
}

// RenameClusterMember changes the name of an existing member
func (r *ProtocolLXD) RenameClusterMember(name string, member api.ClusterMemberPost) error {
	if !r.HasExtension("clustering") {
		return fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	_, _, err := r.query("POST", fmt.Sprintf("/cluster/members/%s", name), member, "")
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>client: Fix output of GetClusterMemberNames<commit_after>package lxd

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd/shared/api"
)

// GetCluster returns information about a cluster
//
// If this client is not trusted, the password must be supplied
func (r *ProtocolLXD) GetCluster() (*api.Cluster, string, error) {
	if !r.HasExtension("clustering") {
		return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	cluster := &api.Cluster{}
	etag, err := r.queryStruct("GET", "/cluster", nil, "", &cluster)
	if err != nil {
		return nil, "", err
	}

	return cluster, etag, nil
}

// UpdateCluster requests to bootstrap a new cluster or join an existing one.
func (r *ProtocolLXD) UpdateCluster(cluster api.ClusterPut, ETag string) (Operation, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	if cluster.ServerAddress != "" || cluster.ClusterPassword != "" || len(cluster.MemberConfig) > 0 {
		if !r.HasExtension("clustering_join") {
			return nil, fmt.Errorf("The server is missing the required \"clustering_join\" API extension")
		}
	}

	op, _, err := r.queryOperation("PUT", "/cluster", cluster, "")
	if err != nil {
		return nil, err
	}

	return op, nil
}

// DeleteClusterMember makes the given member leave the cluster (gracefully or not,
// depending on the force flag)
func (r *ProtocolLXD) DeleteClusterMember(name string, force bool) error {
	if !r.HasExtension("clustering") {
		return fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	params := ""
	if force {
		params += "?force=1"
	}

	_, err := r.queryStruct("DELETE", fmt.Sprintf("/cluster/members/%s%s", name, params), nil, "", nil)
	if err != nil {
		return err
	}

	return nil
}

// GetClusterMemberNames returns the URLs of the current members in the cluster
func (r *ProtocolLXD) GetClusterMemberNames() ([]string, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	urls := []string{}
	_, err := r.queryStruct("GET", "/cluster/members", nil, "", &urls)
	if err != nil {
		return nil, err
	}

	// Parse it
	names := []string{}
	for _, url := range urls {
		fields := strings.Split(url, "/cluster/members/")
		names = append(names, fields[len(fields)-1])
	}

	return names, nil
}

// GetClusterMembers returns the current members of the cluster
func (r *ProtocolLXD) GetClusterMembers() ([]api.ClusterMember, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	members := []api.ClusterMember{}
	_, err := r.queryStruct("GET", "/cluster/members?recursion=1", nil, "", &members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

// GetClusterMember returns information about the given member
func (r *ProtocolLXD) GetClusterMember(name string) (*api.ClusterMember, string, error) {
	if !r.HasExtension("clustering") {
		return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	member := api.ClusterMember{}
	etag, err := r.queryStruct("GET", fmt.Sprintf("/cluster/members/%s", name), nil, "", &member)
	if err != nil {
		return nil, "", err
	}

	return &member, etag, nil
}

// UpdateClusterMember updates information about the given member
func (r *ProtocolLXD) UpdateClusterMember(name string, member api.ClusterMemberPut, ETag string) error {
	if !r.HasExtension("clustering_edit_roles") {
		return fmt.Errorf("The server is missing the required \"clustering_edit_roles\" API extension")
	}
	if member.FailureDomain != "" {
		if !r.HasExtension("clustering_failure_domains") {
			return fmt.Errorf("The server is missing the required \"clustering_failure_domains\" API extension")
		}
	}

	// Send the request
	_, _, err := r.query("PUT", fmt.Sprintf("/cluster/members/%s", name), member, ETag)
	if err != nil {
		return err
	}

	return nil
}

// RenameClusterMember changes the name of an existing member
func (r *ProtocolLXD) RenameClusterMember(name string, member api.ClusterMemberPost) error {
	if !r.HasExtension("clustering") {
		return fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	_, _, err := r.query("POST", fmt.Sprintf("/cluster/members/%s", name), member, "")
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>