<commit_before>// A simple LLRP-based logical reader mock for RFID Tags using go-llrp
package main

import (
	"encoding/binary"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/gin-gonic/contrib/static"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/iomz/go-llrp"
	"github.com/juju/loggo"
	"golang.org/x/net/websocket"
	"gopkg.in/alecthomas/kingpin.v2"
)

// ManagementAction is a type for TagManager
type ManagementAction int

const (
	// RetrieveTags is a const for retrieving tags
	RetrieveTags ManagementAction = iota
	// AddTags is a const for adding tags
	AddTags
	// DeleteTags is a const for deleting tags
	DeleteTags
)

// TagManager is a struct for tag management channel
type TagManager struct {
	action ManagementAction
	tags   []*Tag
}

// Constant values
const (
	// BufferSize is a general size for a buffer
	BufferSize = 512
)

var (
	// Current Version
	version = "0.1.0"

	// kingpin app
	app = kingpin.New("golemu", "A mock LLRP-based logical reader emulator for RFID Tags.")
	// kingpin debug mode flag
	debug = app.Flag("debug", "Enable debug mode.").Short('v').Default("false").Bool()
	// kingpin initial MessageID
	initialMessageID = app.Flag("initialMessageID", "The initial messageID to start from.").Short('m').Default("1000").Int()
	// kingpin initial KeepaliveID
	initialKeepaliveID = app.Flag("initialKeepaliveID", "The initial keepaliveID to start from.").Short('k').Default("80000").Int()
	// kingpin LLRP listening port
	port = app.Flag("port", "LLRP listening port.").Short('p').Default("5084").Int()
	// kingpin LLRP listening IP address
	ip = app.Flag("ip", "LLRP listening address.").Short('a').Default("0.0.0.0").IP()

	// kingpin server command
	server = app.Command("server", "Run as a tag stream server.")
	// kingpin report interval
	reportInterval = server.Flag("reportInterval", "The interval of ROAccessReport in ms. Pseudo ROReport spec option.").Short('i').Default("1000").Int()
	// kingpin maximum tag to include in ROAccessReport
	maxTag = server.Flag("maxTag", "The maximum number of TagReportData parameters per ROAccessReport. Pseudo ROReport spec option. 0 for no limit.").Short('t').Default("0").Int()
	// kingpin tag list file
	file = server.Flag("file", "The file containing Tag data.").Short('f').Default("tags.csv").String()
	// kingpin web port
	webPort = server.Flag("webPort", "Port listening for web access.").Short('w').Default("3000").Int()

	// kingpin client command
	client = app.Command("client", "Run as a client mode.")

	// loggo
	logger = loggo.GetLogger("")

	// LLRPConn flag
	isLLRPConnAlive = false
	// Current messageID
	messageID = uint32(*initialMessageID)
	// Current KeepaliveID
	keepaliveID = *initialKeepaliveID
	// Current activeClients
	activeClients = make(map[WebsockConn]int) // map containing clients
	// Tag management channel
	tagManager = make(chan *TagManager)
	// notify tag update channel
	notify = make(chan bool)
	// update TagReportDataStack when tag is updated
	tagUpdated = make(chan []*Tag)
)

func init() {
}

// Iterate through the Tags and write ROAccessReport message to the socket
func sendROAccessReport(conn net.Conn, trds *TagReportDataStack) error {
	for _, trd := range trds.Stack {
		// Append TagReportData to ROAccessReport
		roar := llrp.ROAccessReport(trd.Parameter, messageID)
		atomic.AddUint32(&messageID, 1)
		runtime.Gosched()
		//logger.Infof("%v\n", len(roar))

		// Send
		_, err := conn.Write(roar)
		if err != nil {
			return err
		}

		// Wait until ACK received
		time.Sleep(time.Millisecond)
	}

	return nil
}

// Handles incoming requests.
func handleRequest(conn net.Conn, tags []*Tag) {
	// Make a buffer to hold incoming data.
	buf := make([]byte, BufferSize)
	trds := buildTagReportDataStack(tags)

	for {
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err == io.EOF {
			// Close the connection when you're done with it.
			logger.Infof("The client is disconnected, closing LLRP connection")
			conn.Close()
			return
		} else if err != nil {
			logger.Errorf(err.Error())
			logger.Infof("Closing LLRP connection")
			conn.Close()
			return
		}

		// Respond according to the LLRP packet header
		header := binary.BigEndian.Uint16(buf[:2])
		if header == llrp.SetReaderConfigHeader || header == llrp.KeepaliveAckHeader {
			if header == llrp.SetReaderConfigHeader {
				// SRC received, start ROAR
				logger.Infof(">>> SET_READER_CONFIG")
				conn.Write(llrp.SetReaderConfigResponse())
			} else if header == llrp.KeepaliveAckHeader {
				// KA receieved, continue ROAR
				logger.Infof(">>> KeepaliveAck")
			}
			// Tick ROAR and Keepalive interval
			roarTicker := time.NewTicker(time.Duration(*reportInterval) * time.Millisecond)
			keepaliveTicker := time.NewTicker(10 * time.Second)
			for { // Infinite loop
				isLLRPConnAlive = true
				logger.Debugf("[LLRP handler select]: %v", trds)
				select {
				// ROAccessReport interval tick
				case <-roarTicker.C:
					logger.Tracef("### roarTicker.C")
					logger.Infof("<<< ROAccessReport (# of Tags: %v, # of TagReportData: %v)", trds.TotalTagCounts(), len(trds.Stack))
					err := sendROAccessReport(conn, trds)
					if err != nil {
						logger.Errorf(err.Error())
						isLLRPConnAlive = false
					}
				// Keepalive interval tick
				case <-keepaliveTicker.C:
					logger.Tracef("### keepaliveTicker.C")
					logger.Infof("<<< Keepalive")
					conn.Write(llrp.Keepalive())
					isLLRPConnAlive = false
				// When the tag queue is updated
				case tags := <-tagUpdated:
					logger.Tracef("### TagUpdated")
					trds = buildTagReportDataStack(tags)
				}
				if !isLLRPConnAlive {
					roarTicker.Stop()
					keepaliveTicker.Stop()
					break
				}
			}
		} else {
			// Unknown LLRP packet received, reset the connection
			logger.Warningf("Unknown header: %v, reqlen: %v", header, reqLen)
			logger.Warningf("Message: %v", buf)
			return
		}
	}
}

// APIPostTag redirects the tag addition request
func APIPostTag(c *gin.Context) {
	var json []TagInString
	c.BindWith(&json, binding.JSON)
	if res := ReqAddTag("add", json); res == "error" {
		c.String(http.StatusAlreadyReported, "The tag already exists!\n")
	} else {
		c.String(http.StatusAccepted, "Post requested!\n")
	}
}

// APIDeleteTag redirects the tag deletion request
func APIDeleteTag(c *gin.Context) {
	var json []TagInString
	c.BindWith(&json, binding.JSON)
	if res := ReqDeleteTag("delete", json); res == "error" {
		c.String(http.StatusNoContent, "The tag doesn't exist!\n")
	} else {
		c.String(http.StatusAccepted, "Delete requested!\n")
	}
}

// server mode
func runServer() int {
	// Read virtual tags from a csv file
	logger.Infof("Loading virtual Tags from \"%v\"", *file)

	if _, err := os.Stat(*file); os.IsNotExist(err) {
		_, err := os.Create(*file)
		check(err)
		logger.Infof("%v created.", *file)
	}

	csvIn, err := ioutil.ReadFile(*file)
	check(err)
	tags := loadTagsFromCSV(string(csvIn))

	// Listen for incoming connections.
	l, err := net.Listen("tcp", ip.String()+":"+strconv.Itoa(*port))
	check(err)

	// Close the listener when the application closes.
	defer l.Close()
	logger.Infof("Listening on %v:%v", ip, *port)

	// Channel for communicating virtual tag updates and signals
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	// Handle websocket and static file hosting with gin
	go func() {
		r := gin.Default()
		r.Use(static.Serve("/", static.LocalFile("./public", true)))
		r.GET("/ws", func(c *gin.Context) {
			handler := websocket.Handler(SockServer)
			handler.ServeHTTP(c.Writer, c.Request)
		})
		v1 := r.Group("api/v1")
		v1.POST("/tags", APIPostTag)
		v1.DELETE("/tags", APIDeleteTag)
		r.Run(":" + strconv.Itoa(*webPort))
	}()

	go func() {
		for {
			select {
			case cmd := <-tagManager:
				// Tag management
				res := []*Tag{}
				switch cmd.action {
				case AddTags:
					for _, t := range cmd.tags {
						if i := getIndexOfTag(tags, t); i < 0 {
							tags = append(tags, t)
							res = append(res, t)
							writeTagsToCSV(tags, *file)
							if isLLRPConnAlive {
								tagUpdated <- tags
							}
						}
					}
				case DeleteTags:
					for _, t := range cmd.tags {
						if i := getIndexOfTag(tags, t); i >= 0 {
							tags = append(tags[:i], tags[i+1:]...)
							res = append(res, t)
							writeTagsToCSV(tags, *file)
							if isLLRPConnAlive {
								tagUpdated <- tags
							}
						}
					}
				case RetrieveTags:
					res = tags
				}
				cmd.tags = res
				tagManager <- cmd
			case signal := <-signals:
				// Handle SIGINT and SIGTERM.
				logger.Infof("%v", signal)
				os.Exit(0)
			}
		}
	}()

	// Handle LLRP connection
	for {
		// Accept an incoming connection.
		logger.Infof("LLRP connection initiated")
		conn, err := l.Accept()
		if err != nil {
			logger.Errorf(err.Error())
			os.Exit(2)
		}

		// Send back READER_EVENT_NOTIFICATION
		currentTime := uint64(time.Now().UTC().Nanosecond() / 1000)
		conn.Write(llrp.ReaderEventNotification(messageID, currentTime))
		logger.Infof("<<< READER_EVENT_NOTIFICATION")
		atomic.AddUint32(&messageID, 1)
		runtime.Gosched()
		time.Sleep(time.Millisecond)

		// Handle connections in a new goroutine.
		go handleRequest(conn, tags)
	}
}

// client mode
func runClient() int {
	// Establish a connection to the llrp client
	conn, err := net.Dial("tcp", ip.String()+":"+strconv.Itoa(*port))
	check(err)

	buf := make([]byte, BufferSize)
	for {
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err == io.EOF {
			// Close the connection when you're done with it.
			return 0
		} else if err != nil {
			logger.Errorf(err.Error())
			logger.Errorf("reqLen = %v", reqLen)
			conn.Close()
			break
		}

		header := binary.BigEndian.Uint16(buf[:2])
		if header == llrp.ReaderEventNotificationHeader {
			logger.Infof(">>> READER_EVENT_NOTIFICATION")
			conn.Write(llrp.SetReaderConfig(messageID))
		} else if header == llrp.KeepaliveHeader {
			logger.Infof(">>> KEEP_ALIVE")
			conn.Write(llrp.KeepaliveAck())
		} else if header == llrp.SetReaderConfigResponseHeader {
			logger.Infof(">>> SET_READER_CONFIG_RESPONSE")
		} else if header == llrp.ROAccessReportHeader {
			logger.Infof(">>> RO_ACCESS_REPORT")
			logger.Debugf("Packet size: %v\n", reqLen)
			logger.Debugf("% x\n", buf[:reqLen])
		} else {
			logger.Warningf("Unknown header: %v\n", header)
		}
	}
	return 0
}

func main() {
	app.Version(version)
	parse := kingpin.MustParse(app.Parse(os.Args[1:]))

	if *debug {
		loggo.ConfigureLoggers("TRACE")
		gin.SetMode(gin.DebugMode)
	} else {
		loggo.ConfigureLoggers("INFO")
		gin.SetMode(gin.ReleaseMode)
	}

	switch parse {
	case server.FullCommand():
		os.Exit(runServer())
	case client.FullCommand():
		os.Exit(runClient())
	}
}
<commit_msg>randomize the order of virtual tags to be reported, adjust keepalive interval<commit_after>// A simple LLRP-based logical reader mock for RFID Tags using go-llrp
package main

import (
	"encoding/binary"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/gin-gonic/contrib/static"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/iomz/go-llrp"
	"github.com/juju/loggo"
	"golang.org/x/net/websocket"
	"gopkg.in/alecthomas/kingpin.v2"
)

// ManagementAction is a type for TagManager
type ManagementAction int

const (
	// RetrieveTags is a const for retrieving tags
	RetrieveTags ManagementAction = iota
	// AddTags is a const for adding tags
	AddTags
	// DeleteTags is a const for deleting tags
	DeleteTags
)

// TagManager is a struct for tag management channel
type TagManager struct {
	action ManagementAction
	tags   []*Tag
}

// Constant values
const (
	// BufferSize is a general size for a buffer
	BufferSize = 512
)

var (
	// Current Version
	version = "0.1.0"

	// kingpin app
	app = kingpin.New("golemu", "A mock LLRP-based logical reader emulator for RFID Tags.")
	// kingpin debug mode flag
	debug = app.Flag("debug", "Enable debug mode.").Short('v').Default("false").Bool()
	// kingpin initial MessageID
	initialMessageID = app.Flag("initialMessageID", "The initial messageID to start from.").Short('m').Default("1000").Int()
	// kingpin initial KeepaliveID
	initialKeepaliveID = app.Flag("initialKeepaliveID", "The initial keepaliveID to start from.").Short('k').Default("80000").Int()
	// kingpin LLRP listening port
	port = app.Flag("port", "LLRP listening port.").Short('p').Default("5084").Int()
	// kingpin LLRP listening IP address
	ip = app.Flag("ip", "LLRP listening address.").Short('a').Default("0.0.0.0").IP()

	// kingpin server command
	server = app.Command("server", "Run as a tag stream server.")
	// kingpin report interval
	reportInterval = server.Flag("reportInterval", "The interval of ROAccessReport in ms. Pseudo ROReport spec option.").Short('i').Default("1000").Int()
	// kingpin maximum tag to include in ROAccessReport
	maxTag = server.Flag("maxTag", "The maximum number of TagReportData parameters per ROAccessReport. Pseudo ROReport spec option. 0 for no limit.").Short('t').Default("0").Int()
	// kingpin tag list file
	file = server.Flag("file", "The file containing Tag data.").Short('f').Default("tags.csv").String()
	// kingpin web port
	webPort = server.Flag("webPort", "Port listening for web access.").Short('w').Default("3000").Int()

	// kingpin client command
	client = app.Command("client", "Run as a client mode.")

	// loggo
	logger = loggo.GetLogger("")

	// LLRPConn flag
	isLLRPConnAlive = false
	// Current messageID
	messageID = uint32(*initialMessageID)
	// Current KeepaliveID
	keepaliveID = *initialKeepaliveID
	// Current activeClients
	activeClients = make(map[WebsockConn]int) // map containing clients
	// Tag management channel
	tagManager = make(chan *TagManager)
	// notify tag update channel
	notify = make(chan bool)
	// update TagReportDataStack when tag is updated
	tagUpdated = make(chan []*Tag)
)

func init() {
}

// Iterate through the Tags and write ROAccessReport message to the socket
func sendROAccessReport(conn net.Conn, trds *TagReportDataStack) error {
	perms := rand.Perm(len(trds.Stack))
	for _, i := range perms {
		trd := trds.Stack[i]
		// Append TagReportData to ROAccessReport
		roar := llrp.ROAccessReport(trd.Parameter, messageID)
		atomic.AddUint32(&messageID, 1)
		runtime.Gosched()
		//logger.Infof("%v\n", len(roar))

		// Send
		_, err := conn.Write(roar)
		if err != nil {
			return err
		}

		// Wait until ACK received
		time.Sleep(time.Millisecond)
	}

	return nil
}

// Handles incoming requests.
func handleRequest(conn net.Conn, tags []*Tag) {
	// Make a buffer to hold incoming data.
	buf := make([]byte, BufferSize)
	trds := buildTagReportDataStack(tags)

	for {
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err == io.EOF {
			// Close the connection when you're done with it.
			logger.Infof("The client is disconnected, closing LLRP connection")
			conn.Close()
			return
		} else if err != nil {
			logger.Errorf(err.Error())
			logger.Infof("Closing LLRP connection")
			conn.Close()
			return
		}

		// Respond according to the LLRP packet header
		header := binary.BigEndian.Uint16(buf[:2])
		if header == llrp.SetReaderConfigHeader || header == llrp.KeepaliveAckHeader {
			if header == llrp.SetReaderConfigHeader {
				// SRC received, start ROAR
				logger.Infof(">>> SET_READER_CONFIG")
				conn.Write(llrp.SetReaderConfigResponse())
			} else if header == llrp.KeepaliveAckHeader {
				// KA receieved, continue ROAR
				logger.Infof(">>> KEEP_ALIVE_ACK")
			}

			logger.Debugf("Initial RO_ACCESS_REPORT")
			err := sendROAccessReport(conn, trds)
			if err != nil {
				logger.Errorf(err.Error())
				isLLRPConnAlive = false
			}

			// Tick ROAR and Keepalive interval
			roarTicker := time.NewTicker(time.Duration(*reportInterval) * time.Millisecond)
			keepaliveTicker := time.NewTicker(60 * time.Second)
			go func() {
				for { // Infinite loop
					isLLRPConnAlive = true
					logger.Debugf("[LLRP handler select]: %v", trds)
					select {
					// ROAccessReport interval tick
					case <-roarTicker.C:
						logger.Tracef("### roarTicker.C")
						logger.Infof("<<< RO_ACCESS_REPORT (# of Tags: %v, # of TagReportData: %v)", trds.TotalTagCounts(), len(trds.Stack))
						err := sendROAccessReport(conn, trds)
						if err != nil {
							logger.Errorf(err.Error())
							isLLRPConnAlive = false
						}
					// Keepalive interval tick
					case <-keepaliveTicker.C:
						logger.Tracef("### keepaliveTicker.C")
						logger.Infof("<<< KEEP_ALIVE")
						conn.Write(llrp.Keepalive())
						isLLRPConnAlive = false
					// When the tag queue is updated
					case tags := <-tagUpdated:
						logger.Tracef("### TagUpdated")
						trds = buildTagReportDataStack(tags)
					}
					if !isLLRPConnAlive {
						roarTicker.Stop()
						keepaliveTicker.Stop()
						break
					}
				}
			}()
		} else {
			// Unknown LLRP packet received, reset the connection
			logger.Warningf("Unknown header: %v, reqlen: %v", header, reqLen)
			logger.Warningf("Message: %v", buf)
			return
		}
	}
}

// APIPostTag redirects the tag addition request
func APIPostTag(c *gin.Context) {
	var json []TagInString
	c.BindWith(&json, binding.JSON)
	if res := ReqAddTag("add", json); res == "error" {
		c.String(http.StatusAlreadyReported, "The tag already exists!\n")
	} else {
		c.String(http.StatusAccepted, "Post requested!\n")
	}
}

// APIDeleteTag redirects the tag deletion request
func APIDeleteTag(c *gin.Context) {
	var json []TagInString
	c.BindWith(&json, binding.JSON)
	if res := ReqDeleteTag("delete", json); res == "error" {
		c.String(http.StatusNoContent, "The tag doesn't exist!\n")
	} else {
		c.String(http.StatusAccepted, "Delete requested!\n")
	}
}

// server mode
func runServer() int {
	// Read virtual tags from a csv file
	logger.Infof("Loading virtual Tags from \"%v\"", *file)

	if _, err := os.Stat(*file); os.IsNotExist(err) {
		_, err := os.Create(*file)
		check(err)
		logger.Infof("%v created.", *file)
	}

	csvIn, err := ioutil.ReadFile(*file)
	check(err)
	tags := loadTagsFromCSV(string(csvIn))

	// Listen for incoming connections.
	l, err := net.Listen("tcp", ip.String()+":"+strconv.Itoa(*port))
	check(err)

	// Close the listener when the application closes.
	defer l.Close()
	logger.Infof("Listening on %v:%v", ip, *port)

	// Channel for communicating virtual tag updates and signals
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	// Handle websocket and static file hosting with gin
	go func() {
		r := gin.Default()
		r.Use(static.Serve("/", static.LocalFile("./public", true)))
		r.GET("/ws", func(c *gin.Context) {
			handler := websocket.Handler(SockServer)
			handler.ServeHTTP(c.Writer, c.Request)
		})
		v1 := r.Group("api/v1")
		v1.POST("/tags", APIPostTag)
		v1.DELETE("/tags", APIDeleteTag)
		r.Run(":" + strconv.Itoa(*webPort))
	}()

	go func() {
		for {
			select {
			case cmd := <-tagManager:
				// Tag management
				res := []*Tag{}
				switch cmd.action {
				case AddTags:
					for _, t := range cmd.tags {
						if i := getIndexOfTag(tags, t); i < 0 {
							tags = append(tags, t)
							res = append(res, t)
							writeTagsToCSV(tags, *file)
							if isLLRPConnAlive {
								tagUpdated <- tags
							}
						}
					}
				case DeleteTags:
					for _, t := range cmd.tags {
						if i := getIndexOfTag(tags, t); i >= 0 {
							tags = append(tags[:i], tags[i+1:]...)
							res = append(res, t)
							writeTagsToCSV(tags, *file)
							if isLLRPConnAlive {
								tagUpdated <- tags
							}
						}
					}
				case RetrieveTags:
					res = tags
				}
				cmd.tags = res
				tagManager <- cmd
			case signal := <-signals:
				// Handle SIGINT and SIGTERM.
				logger.Infof("%v", signal)
				os.Exit(0)
			}
		}
	}()

	// Handle LLRP connection
	for {
		// Accept an incoming connection.
		logger.Infof("LLRP connection initiated")
		conn, err := l.Accept()
		if err != nil {
			logger.Errorf(err.Error())
			os.Exit(2)
		}

		// Send back READER_EVENT_NOTIFICATION
		currentTime := uint64(time.Now().UTC().Nanosecond() / 1000)
		conn.Write(llrp.ReaderEventNotification(messageID, currentTime))
		logger.Infof("<<< READER_EVENT_NOTIFICATION")
		atomic.AddUint32(&messageID, 1)
		runtime.Gosched()
		time.Sleep(time.Millisecond)

		// Handle connections in a new goroutine.
		go handleRequest(conn, tags)
	}
}

// client mode
func runClient() int {
	// Establish a connection to the llrp client
	conn, err := net.Dial("tcp", ip.String()+":"+strconv.Itoa(*port))
	check(err)

	buf := make([]byte, BufferSize)
	for {
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err == io.EOF {
			// Close the connection when you're done with it.
			return 0
		} else if err != nil {
			logger.Errorf(err.Error())
			logger.Errorf("reqLen = %v", reqLen)
			conn.Close()
			break
		}

		header := binary.BigEndian.Uint16(buf[:2])
		if header == llrp.ReaderEventNotificationHeader {
			logger.Infof(">>> READER_EVENT_NOTIFICATION")
			conn.Write(llrp.SetReaderConfig(messageID))
		} else if header == llrp.KeepaliveHeader {
			logger.Infof(">>> KEEP_ALIVE")
			conn.Write(llrp.KeepaliveAck())
		} else if header == llrp.SetReaderConfigResponseHeader {
			logger.Infof(">>> SET_READER_CONFIG_RESPONSE")
		} else if header == llrp.ROAccessReportHeader {
			logger.Infof(">>> RO_ACCESS_REPORT")
			logger.Debugf("Packet size: %v\n", reqLen)
			logger.Debugf("% x\n", buf[:reqLen])
		} else {
			logger.Warningf("Unknown header: %v\n", header)
		}
	}
	return 0
}

func main() {
	app.Version(version)
	parse := kingpin.MustParse(app.Parse(os.Args[1:]))

	if *debug {
		loggo.ConfigureLoggers("TRACE")
		gin.SetMode(gin.DebugMode)
	} else {
		loggo.ConfigureLoggers("INFO")
		gin.SetMode(gin.ReleaseMode)
	}

	switch parse {
	case server.FullCommand():
		os.Exit(runServer())
	case client.FullCommand():
		os.Exit(runClient())
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Florin Pățan
//
// 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.

// Command gopher
//
// This is a Slack bot for the Gophers Slack.
//
// You can get an invite from https://invite.slack.golangbridge.org/
//
// To run this you need to set the ` GOPHERS_SLACK_BOT_TOKEN ` environment
// variable with the Slack bot token and that's it.
package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/gobridge/gopher/bot"

	"cloud.google.com/go/datastore"
	"cloud.google.com/go/trace"
	"github.com/ChimeraCoder/anaconda"
	"github.com/gorilla/mux"
	"github.com/nlopes/slack"
	"golang.org/x/net/context"
	"google.golang.org/api/option"
)

const (
	gerritLink            = "https://go-review.googlesource.com/changes/?q=status:merged&O=12&n=100"
	defaultCredentialFile = "/tmp/trace/trace.json"
)

var (
	botVersion = "HEAD"
	info       = `{ "version": "` + botVersion + `" }`
)

func decodeGoogleCredentialsToFile(ec string) string {
	r := base64.NewDecoder(base64.StdEncoding, strings.NewReader(ec))
	b, err := ioutil.ReadAll(r)
	if err != nil {
		log.Fatalln("Unable to decode google credentials:", err)
	}
	fi, err := ioutil.TempFile("", "google_*")
	if err != nil {
		log.Fatalln("Unable to create temporary credential file:", err)
	}
	if n, err := fi.Write(b); err != nil || n != len(b) {
		log.Fatalln("Unable to completely write credential file:", err)
	}
	if err := fi.Close(); err != nil {
		log.Fatalln("Unable to close temporary credential file: ", err)
	}
	return fi.Name()
}

func main() {
	log.SetFlags(log.Lshortfile)

	botName := os.Getenv("GOPHERS_SLACK_BOT_NAME")
	slackBotToken := os.Getenv("GOPHERS_SLACK_BOT_TOKEN")
	twitterConsumerKey := os.Getenv("GOPHER_SLACK_BOT_TWITTER_CONSUMER_KEY")
	twitterConsumerSecret := os.Getenv("GOPHER_SLACK_BOT_TWITTER_CONSUMER_SECRET")
	twitterAccessToken := os.Getenv("GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN")
	twitterAccessTokenSecret := os.Getenv("GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN_SECRET")
	googleCredentials := os.Getenv("GOOGLE_CREDENTIALS")
	googleProjectID := os.Getenv("GOOGLE_PROJECT_ID")
	devMode := os.Getenv("GOPHERS_SLACK_BOT_DEV_MODE") == "true"

	if slackBotToken == "" {
		log.Fatalln("slack bot token must be set in GOPHERS_SLACK_BOT_TOKEN")
	}

	if botName == "" {
		if devMode {
			log.Fatalln("bot name must be set in GOPHERS_SLACK_BOT_NAME")
		}
		botName = "tempbot"
	}

	if twitterConsumerKey == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_CONSUMER_KEY")
	}

	if twitterConsumerSecret == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_CONSUMER_SECRET")
	}

	if twitterAccessToken == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN")
	}

	if twitterAccessTokenSecret == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN_SECRET")
	}

	if googleCredentials == "" {
		if _, err := os.Stat(defaultCredentialFile); err == nil {
			googleCredentials = defaultCredentialFile
		}
	} else {
		googleCredentials = decodeGoogleCredentialsToFile(googleCredentials)
	}

	httpClient := &http.Client{
		Transport: &http.Transport{
			Dial: (&net.Dialer{
				Timeout:   15 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout:   5 * time.Second,
			ResponseHeaderTimeout: 10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

	ctx := context.Background()

	traceClient, err := trace.NewClient(ctx, googleProjectID, option.WithServiceAccountFile(googleCredentials))
	if err != nil {
		log.Fatalln("Unable to create trace client:", err)
	}

	startupSpan := traceClient.NewSpan("b.main")
	ctx = trace.NewContext(ctx, startupSpan)

	traceHttpClient := traceClient.NewHTTPClient(httpClient)

	slack.SetHTTPClient(traceHttpClient)
	slackBotAPI := slack.New(slackBotToken)

	botName = strings.TrimPrefix(botName, "@")

	anaconda.SetConsumerKey(twitterConsumerKey)
	anaconda.SetConsumerSecret(twitterConsumerSecret)
	twitterAPI := anaconda.NewTwitterApi(twitterAccessToken, twitterAccessTokenSecret)

	rtmOptions := &slack.RTMOptions{}
	slackBotRTM := slackBotAPI.NewRTMWithOptions(rtmOptions)
	go slackBotRTM.ManageConnection()
	runtime.Gosched()

	dsClient, err := datastore.NewClient(ctx, googleProjectID, option.WithServiceAccountFile("/tmp/datastore/datastore.json"))
	if err != nil {
		log.Fatalln("Unable to create datastore client:", err)
	}
	defer dsClient.Close()

	b := bot.NewBot(slackBotAPI, dsClient, traceClient, twitterAPI, traceHttpClient, gerritLink, botName, slackBotToken, botVersion, devMode, log.Printf)
	if err := b.Init(ctx, slackBotRTM, startupSpan); err != nil {
		log.Fatalln("Unable to init bot:", err)
	}

	_, err = b.GetLastSeenCL(ctx)
	if err != nil {
		panic("Unable to GetLastSeenCL: " + err.Error())
	}

	go func() {
		<-time.After(1 * time.Second)
		for i := 0; i < 7; i++ {
			b.MonitorGerrit(30 * time.Minute)
			log.Printf("monitoring Gerrit failed %d times\n", i+1)
			if i == 6 {
				break
			}
			time.Sleep(time.Duration(i*10) * time.Second)
		}
		panic("monitoring Gerrit was terminated")
	}()

	go func() {
		for msg := range slackBotRTM.IncomingEvents {
			switch message := msg.Data.(type) {
			case *slack.MessageEvent:
				go b.HandleMessage(message)

			case *slack.TeamJoinEvent:
				go b.TeamJoined(message)
			}
		}
	}()

	go func(traceClient *trace.Client) {
		healthz := func(traceClient *trace.Client) http.HandlerFunc {
			return func(w http.ResponseWriter, r *http.Request) {
				span := traceClient.SpanFromRequest(r)
				defer span.Finish()

				w.Header().Add("Content-Type", "application/json")
				w.WriteHeader(http.StatusOK)
				fmt.Fprintln(w, info)
			}
		}

		r := mux.NewRouter()

		r.HandleFunc("/healthz", healthz(traceClient)).
			Name("info").
			Methods("GET")

		s := http.Server{
			Addr:         ":8081",
			Handler:      r,
			ReadTimeout:  5 * time.Second,
			WriteTimeout: 10 * time.Second,
		}

		log.Fatal(s.ListenAndServe())
	}(traceClient)

	go func() {
		gotimefm := time.NewTicker(1 * time.Minute)
		defer gotimefm.Stop()

		for range gotimefm.C {
			b.GoTimeFM()
		}
	}()

	log.Println("Gopher is now running")
	startupSpan.Finish()
	select {}
}
<commit_msg>Forgot about the datastore<commit_after>// Copyright 2016 Florin Pățan
//
// 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.

// Command gopher
//
// This is a Slack bot for the Gophers Slack.
//
// You can get an invite from https://invite.slack.golangbridge.org/
//
// To run this you need to set the ` GOPHERS_SLACK_BOT_TOKEN ` environment
// variable with the Slack bot token and that's it.
package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/gobridge/gopher/bot"

	"cloud.google.com/go/datastore"
	"cloud.google.com/go/trace"
	"github.com/ChimeraCoder/anaconda"
	"github.com/gorilla/mux"
	"github.com/nlopes/slack"
	"golang.org/x/net/context"
	"google.golang.org/api/option"
)

const (
	gerritLink            = "https://go-review.googlesource.com/changes/?q=status:merged&O=12&n=100"
	defaultCredentialFile = "/tmp/trace/trace.json" // Also /tmp/datastore/datastore.json :-(
)

var (
	botVersion = "HEAD"
	info       = `{ "version": "` + botVersion + `" }`
)

func decodeGoogleCredentialsToFile(ec string) string {
	r := base64.NewDecoder(base64.StdEncoding, strings.NewReader(ec))
	b, err := ioutil.ReadAll(r)
	if err != nil {
		log.Fatalln("Unable to decode google credentials:", err)
	}
	fi, err := ioutil.TempFile("", "google_*")
	if err != nil {
		log.Fatalln("Unable to create temporary credential file:", err)
	}
	if n, err := fi.Write(b); err != nil || n != len(b) {
		log.Fatalln("Unable to completely write credential file:", err)
	}
	if err := fi.Close(); err != nil {
		log.Fatalln("Unable to close temporary credential file: ", err)
	}
	return fi.Name()
}

func main() {
	log.SetFlags(log.Lshortfile)

	botName := os.Getenv("GOPHERS_SLACK_BOT_NAME")
	slackBotToken := os.Getenv("GOPHERS_SLACK_BOT_TOKEN")
	twitterConsumerKey := os.Getenv("GOPHER_SLACK_BOT_TWITTER_CONSUMER_KEY")
	twitterConsumerSecret := os.Getenv("GOPHER_SLACK_BOT_TWITTER_CONSUMER_SECRET")
	twitterAccessToken := os.Getenv("GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN")
	twitterAccessTokenSecret := os.Getenv("GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN_SECRET")
	googleCredentials := os.Getenv("GOOGLE_CREDENTIALS")
	googleProjectID := os.Getenv("GOOGLE_PROJECT_ID")
	devMode := os.Getenv("GOPHERS_SLACK_BOT_DEV_MODE") == "true"

	if slackBotToken == "" {
		log.Fatalln("slack bot token must be set in GOPHERS_SLACK_BOT_TOKEN")
	}

	if botName == "" {
		if devMode {
			log.Fatalln("bot name must be set in GOPHERS_SLACK_BOT_NAME")
		}
		botName = "tempbot"
	}

	if twitterConsumerKey == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_CONSUMER_KEY")
	}

	if twitterConsumerSecret == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_CONSUMER_SECRET")
	}

	if twitterAccessToken == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN")
	}

	if twitterAccessTokenSecret == "" {
		log.Fatalln("missing GOPHER_SLACK_BOT_TWITTER_ACCESS_TOKEN_SECRET")
	}

	if googleCredentials == "" {
		// FIXME: This doesn't deal with the default credentials in per service locations.

		if _, err := os.Stat(defaultCredentialFile); err == nil {
			googleCredentials = defaultCredentialFile
		}
	} else {
		googleCredentials = decodeGoogleCredentialsToFile(googleCredentials)
	}

	httpClient := &http.Client{
		Transport: &http.Transport{
			Dial: (&net.Dialer{
				Timeout:   15 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout:   5 * time.Second,
			ResponseHeaderTimeout: 10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

	ctx := context.Background()

	traceClient, err := trace.NewClient(ctx, googleProjectID, option.WithServiceAccountFile(googleCredentials))
	if err != nil {
		log.Fatalln("Unable to create trace client:", err)
	}

	startupSpan := traceClient.NewSpan("b.main")
	ctx = trace.NewContext(ctx, startupSpan)

	traceHttpClient := traceClient.NewHTTPClient(httpClient)

	slack.SetHTTPClient(traceHttpClient)
	slackBotAPI := slack.New(slackBotToken)

	botName = strings.TrimPrefix(botName, "@")

	anaconda.SetConsumerKey(twitterConsumerKey)
	anaconda.SetConsumerSecret(twitterConsumerSecret)
	twitterAPI := anaconda.NewTwitterApi(twitterAccessToken, twitterAccessTokenSecret)

	rtmOptions := &slack.RTMOptions{}
	slackBotRTM := slackBotAPI.NewRTMWithOptions(rtmOptions)
	go slackBotRTM.ManageConnection()
	runtime.Gosched()

	dsClient, err := datastore.NewClient(ctx, googleProjectID, option.WithServiceAccountFile(googleCredentials))
	if err != nil {
		log.Fatalln("Unable to create datastore client:", err)
	}
	defer dsClient.Close()

	b := bot.NewBot(slackBotAPI, dsClient, traceClient, twitterAPI, traceHttpClient, gerritLink, botName, slackBotToken, botVersion, devMode, log.Printf)
	if err := b.Init(ctx, slackBotRTM, startupSpan); err != nil {
		log.Fatalln("Unable to init bot:", err)
	}

	_, err = b.GetLastSeenCL(ctx)
	if err != nil {
		panic("Unable to GetLastSeenCL: " + err.Error())
	}

	go func() {
		<-time.After(1 * time.Second)
		for i := 0; i < 7; i++ {
			b.MonitorGerrit(30 * time.Minute)
			log.Printf("monitoring Gerrit failed %d times\n", i+1)
			if i == 6 {
				break
			}
			time.Sleep(time.Duration(i*10) * time.Second)
		}
		panic("monitoring Gerrit was terminated")
	}()

	go func() {
		for msg := range slackBotRTM.IncomingEvents {
			switch message := msg.Data.(type) {
			case *slack.MessageEvent:
				go b.HandleMessage(message)

			case *slack.TeamJoinEvent:
				go b.TeamJoined(message)
			}
		}
	}()

	go func(traceClient *trace.Client) {
		healthz := func(traceClient *trace.Client) http.HandlerFunc {
			return func(w http.ResponseWriter, r *http.Request) {
				span := traceClient.SpanFromRequest(r)
				defer span.Finish()

				w.Header().Add("Content-Type", "application/json")
				w.WriteHeader(http.StatusOK)
				fmt.Fprintln(w, info)
			}
		}

		r := mux.NewRouter()

		r.HandleFunc("/healthz", healthz(traceClient)).
			Name("info").
			Methods("GET")

		s := http.Server{
			Addr:         ":8081",
			Handler:      r,
			ReadTimeout:  5 * time.Second,
			WriteTimeout: 10 * time.Second,
		}

		log.Fatal(s.ListenAndServe())
	}(traceClient)

	go func() {
		gotimefm := time.NewTicker(1 * time.Minute)
		defer gotimefm.Stop()

		for range gotimefm.C {
			b.GoTimeFM()
		}
	}()

	log.Println("Gopher is now running")
	startupSpan.Finish()
	select {}
}
<|endoftext|>


<commit_before>package gopher

import (
	f "github.com/gopherlabs/gopher-framework"
	"github.com/gopherlabs/gopher-services"
)

var (
	c          *f.Container
	App        *app
	Log        f.Loggable
	Route      f.Routable
	RouteGroup routeGroup
	Context    f.Mappable
	Render     f.Renderable
)

type app struct {
	ctnr *f.Container
}

func init() {
	initApp()
	App.Config()
}

func initApp() {
	app := new(app)
	App = app
}

type Config map[string]interface{}
type ConfigLogger f.ConfigLogger
type ConfigRouter f.ConfigRouter
type ConfigRenderer f.ConfigRenderer

type GroupMatcher struct {
	Host       string
	PathPrefix string
	Methods    []string
	Queries    []string
	Schemes    []string
}

type routeGroup struct{}

func (g *routeGroup) New(matcher GroupMatcher) f.Routable {
	return App.ctnr.RouteGroup.New(f.GroupMatcher(matcher))
}

func (m *app) Config(config ...map[string]interface{}) {
	if len(config) > 0 {
		c = f.NewContainer(convertConfig(config[0]))
	} else {
		c = f.NewContainer()
	}
	App.ctnr = c
	c.Use(f.LoggerMiddleware)
	registerProviders()
}

func (m *app) Use(mw f.MiddlewareHandler, args ...interface{}) {
	m.ctnr.Use(mw, args...)
}

func convertConfig(in Config) f.Config {
	conf := f.Config{}
	if in[KEY_LOGGER] != nil {
		conf[KEY_LOGGER] = f.ConfigLogger(in[KEY_LOGGER].(ConfigLogger))
	}
	if in[KEY_ROUTER] != nil {
		conf[KEY_ROUTER] = f.ConfigRouter(in[KEY_ROUTER].(ConfigRouter))
	}
	if in[KEY_RENDERER] != nil {
		conf[KEY_RENDERER] = f.ConfigRenderer(in[KEY_RENDERER].(ConfigRenderer))
	}
	return conf
}

func registerProviders() {
	c.RegisterProvider(new(services.LogProvider))
	Log = c.Log
	c.RegisterProvider(new(services.MapProvider))
	Context = c.Context
	c.RegisterProvider(new(services.RouteProvider))
	Route = c.Route
	RouteGroup = routeGroup{}
	c.RegisterProvider(new(services.RenderProvider))
	Render = c.Render
	f.Initialized = true
}

func ListenAndServe() {
	Route.(f.Servable).Serve()
}

const (
	KEY_LOGGER   = f.LOGGER
	KEY_ROUTER   = f.ROUTER
	KEY_RENDERER = f.RENDERER
	KEY_MAPPER   = f.MAPPER
)

// Log Levels
const (
	// PanicLevel level, highest level of severity. Logs and then calls panic with the
	// message passed to Debug, Info, ...
	LEVEL_PANIC uint8 = iota
	// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
	// logging level is set to Panic.
	LEVEL_FATAL
	// ErrorLevel level. Logs. Used for errors that should definitely be noted.
	// Commonly used for hooks to send errors to an error tracking service.
	LEVEL_ERROR
	// WarnLevel level. Non-critical entries that deserve eyes.
	LEVEL_WARN
	// InfoLevel level. General operational entries about what's going on inside the
	// application.
	LEVEL_INFO
	// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
	LEVEL_DEBUG
)
<commit_msg>Added GetHttpHandler() for standard support<commit_after>package gopher

import (
	"net/http"

	f "github.com/gopherlabs/gopher-framework"
	"github.com/gopherlabs/gopher-services"
)

var (
	c          *f.Container
	App        *app
	Log        f.Loggable
	Route      f.Routable
	RouteGroup routeGroup
	Context    f.Mappable
	Render     f.Renderable
)

type app struct {
	ctnr *f.Container
}

func init() {
	initApp()
	App.Config()
}

func initApp() {
	app := new(app)
	App = app
}

type Config map[string]interface{}
type ConfigLogger f.ConfigLogger
type ConfigRouter f.ConfigRouter
type ConfigRenderer f.ConfigRenderer

type GroupMatcher struct {
	Host       string
	PathPrefix string
	Methods    []string
	Queries    []string
	Schemes    []string
}

type routeGroup struct{}

func (g *routeGroup) New(matcher GroupMatcher) f.Routable {
	return App.ctnr.RouteGroup.New(f.GroupMatcher(matcher))
}

func (m *app) Config(config ...map[string]interface{}) {
	if len(config) > 0 {
		c = f.NewContainer(convertConfig(config[0]))
	} else {
		c = f.NewContainer()
	}
	App.ctnr = c
	c.Use(f.LoggerMiddleware)
	registerProviders()
}

func (m *app) Use(mw f.MiddlewareHandler, args ...interface{}) {
	m.ctnr.Use(mw, args...)
}

func convertConfig(in Config) f.Config {
	conf := f.Config{}
	if in[KEY_LOGGER] != nil {
		conf[KEY_LOGGER] = f.ConfigLogger(in[KEY_LOGGER].(ConfigLogger))
	}
	if in[KEY_ROUTER] != nil {
		conf[KEY_ROUTER] = f.ConfigRouter(in[KEY_ROUTER].(ConfigRouter))
	}
	if in[KEY_RENDERER] != nil {
		conf[KEY_RENDERER] = f.ConfigRenderer(in[KEY_RENDERER].(ConfigRenderer))
	}
	return conf
}

func registerProviders() {
	c.RegisterProvider(new(services.LogProvider))
	Log = c.Log
	c.RegisterProvider(new(services.MapProvider))
	Context = c.Context
	c.RegisterProvider(new(services.RouteProvider))
	Route = c.Route
	RouteGroup = routeGroup{}
	c.RegisterProvider(new(services.RenderProvider))
	Render = c.Render
	f.Initialized = true
}

func ListenAndServe() {
	Route.(f.Servable).ListenAndServe()
}

func GetHttpHandler() http.Handler {
	return Route.(f.Servable).GetHttpHandler()
}

const (
	KEY_LOGGER   = f.LOGGER
	KEY_ROUTER   = f.ROUTER
	KEY_RENDERER = f.RENDERER
	KEY_MAPPER   = f.MAPPER
)

// Log Levels
const (
	// PanicLevel level, highest level of severity. Logs and then calls panic with the
	// message passed to Debug, Info, ...
	LEVEL_PANIC uint8 = iota
	// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
	// logging level is set to Panic.
	LEVEL_FATAL
	// ErrorLevel level. Logs. Used for errors that should definitely be noted.
	// Commonly used for hooks to send errors to an error tracking service.
	LEVEL_ERROR
	// WarnLevel level. Non-critical entries that deserve eyes.
	LEVEL_WARN
	// InfoLevel level. General operational entries about what's going on inside the
	// application.
	LEVEL_INFO
	// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
	LEVEL_DEBUG
)
<|endoftext|>


<commit_before>package gopher

import (
	"github.com/gopherlabs/gopher-framework"
	"github.com/gopherlabs/gopher-services"
)

const (
	LOGGER   = framework.LOGGER
	ROUTER   = framework.ROUTER
	RENDERER = framework.RENDERER
	PARAMS   = framework.PARAMS
	SAMPLE   = framework.SAMPLE
)

func NewApp(config ...framework.Config) *framework.Container {
	appConf := framework.Config{}
	if len(config) > 0 {
		appConf = config[0]
	}
	container := framework.NewContainer(appConf)
	registerProviders(container)
	return container
}

func registerProviders(container *framework.Container) {
	container.RegisterProvider(new(services.LogProvider))
	container.RegisterProvider(new(services.RouteProvider))
	container.RegisterProvider(new(services.ParameterProvider))
	container.RegisterProvider(new(services.SampleProvider))
	container.RegisterProvider(new(services.RenderProvider))
}
<commit_msg>Initial work on route middlewares<commit_after>package gopher

import (
	"github.com/gopherlabs/gopher-framework"
	"github.com/gopherlabs/gopher-services"
)

const (
	LOGGER   = framework.LOGGER
	ROUTER   = framework.ROUTER
	RENDERER = framework.RENDERER
	PARAMS   = framework.PARAMS
	SAMPLE   = framework.SAMPLE
)

func NewApp(config ...framework.Config) *framework.Container {
	appConf := framework.Config{}
	if len(config) > 0 {
		appConf = config[0]
	}
	container := framework.NewContainer(appConf)
	registerProviders(container)
	return container
}

func registerProviders(container *framework.Container) {
	container.RegisterProvider(new(services.LogProvider))
	container.RegisterProvider(new(services.RouteProvider))
	container.RegisterProvider(new(services.ParameterProvider))
	container.RegisterProvider(new(services.RenderProvider))
}
<|endoftext|>


<commit_before>// gotify is a client library for the Spotify API
package gotify

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

const Version = 1

var APIUrl = fmt.Sprintf("https://api.spotify.com/v%d/", Version)

type SpotifyError struct {
	HttpStatus int
	Code       int
	Message    string
}

func (e SpotifyError) Error() string {
	return fmt.Sprintf("%d, code: %d - %s", e.HttpStatus, e.Code, e.Message)
}

func call(call string, verb string, parameters url.Values, token Token) (string, error) {
	Url := fmt.Sprintf("%s%s", APIUrl, call)
	client := &http.Client{}
	resp := &http.Response{}
	var body string
	req, err := http.NewRequest(strings.ToUpper(verb), Url, strings.NewReader(parameters.Encode()))
	if err == nil {
		if strings.ToUpper(verb) == "POST" {
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
		resp, err = client.Do(req)
		defer resp.Body.Close()
		if err == nil {
			if resp.StatusCode == http.StatusOK {
				tmp, err := ioutil.ReadAll(resp.Body)
				if err == nil {
					body = string(tmp)
					return body, nil
				}
			} else {
				err = SpotifyError{
					resp.StatusCode,
					-1,
					"Requested resource could not be found"}
			}
		}
	}
	return "", err
}

// Returns an album
func Album(id string, token Token) (string, error) {
	id = parseId("album", id)
	return call(fmt.Sprintf("albums/%s", id), "GET", url.Values{}, token)
}

// Returns a list of albums
func Albums(albums []string, token Token) (string, error) {
	ids := buildIdString(albums, "album")
	return call(fmt.Sprintf("albums?ids=%s", ids), "GET", url.Values{}, token)
}

// Returns an artist
func Artist(id string, token Token) (string, error) {
	id = parseId("artist", id)
	return call(fmt.Sprintf("artists/%s", id), "GET", url.Values{}, token)
}

// Returns a list of artists
func Artists(artists []string, token Token) (string, error) {
	ids := buildIdString(artists, "artist")
	return call(fmt.Sprintf("artists?ids=%s", ids), "GET", url.Values{}, token)
}

// Returns information about me
func Me(token Token) (string, error) {
	return call("me", "GET", url.Values{}, token)
}

// Returns a playlist
func Playlist(userId string, playlistId string, fields string, token Token) (string, error) {
	params := url.Values{}
	params.Add("fields", fields)
	return call(fmt.Sprintf("users/%s/playlists/%s", userId, playlistId), "GET", params, token)
}

// Returns a users playlists
func Playlists(userId string, token Token) (string, error) {
	return call(fmt.Sprintf("users/%s/playlists", userId), "GET", url.Values{}, token)
}

// Returns a single track
func Track(trackId string, token Token) (string, error) {
	return call(fmt.Sprintf("tracks/%s", trackId), "GET", url.Values{}, token)
}

// Returns a list of tracks
func Tracks(tracks []string, token Token) (string, error) {
	ids := buildIdString(tracks, "track")
	return call(fmt.Sprintf("tracks?ids=%s", ids), "GET", url.Values{}, token)
}

// Gets basic information about a user
func User(userId string, token Token) (string, error) {
	return call(fmt.Sprintf("users/%s", userId), "GET", url.Values{}, token)
}

func buildIdString(slice []string, idType string) string {
	ids := make([]string, len(slice))
	for i, item := range slice {
		ids[i] = parseId(idType, item)
	}
	return strings.Join(ids, ",")
}

// Parses the id from spotify uri's or http links before making api calls
func parseId(idType string, uri string) string {
	fields := strings.Split(uri, ":")
	if len(fields) == 3 {
		if fields[1] != idType {
			log.Print(fmt.Sprintf("Warning: expected if of type %s but found id of type %s", idType, fields[1]))
		}
		return fields[2]
	}
	fields = strings.Split(uri, "/")
	fmt.Println(len(fields))
	if len(fields) >= 3 {
		if idType != fields[len(fields)-2] {
			log.Print(fmt.Sprintf("Warning: expected if of type %s but found id of type %s", idType, fields[1]))
		}
		return fields[len(fields)-1]
	}
	return uri
}
<commit_msg>added some more api calls<commit_after>// gotify is a client library for the Spotify API
package gotify

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

const Version = 1

var APIUrl = fmt.Sprintf("https://api.spotify.com/v%d/", Version)

type SpotifyError struct {
	HttpStatus int
	Code       int
	Message    string
}

func (e SpotifyError) Error() string {
	return fmt.Sprintf("%d, code: %d - %s", e.HttpStatus, e.Code, e.Message)
}

func call(call string, verb string, params url.Values, token Token) (string, error) {
	verb = strings.ToUpper(verb)
	var body string
	var Url string

	if verb == "GET" {
		Url = fmt.Sprintf("%s%s?%s", APIUrl, call, params.Encode())
	} else {
		Url = fmt.Sprintf("%s%s", APIUrl, call)
	}
	client := &http.Client{}
	resp := &http.Response{}

	req, err := http.NewRequest(verb, Url, strings.NewReader(params.Encode()))
	if err == nil {
		if verb == "POST" {
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
		resp, err = client.Do(req)
		defer resp.Body.Close()
		fmt.Println(resp.Request)
		if err == nil {
			if resp.StatusCode == http.StatusOK {
				tmp, err := ioutil.ReadAll(resp.Body)
				if err == nil {
					body = string(tmp)
					return body, nil
				}
			} else {
				err = SpotifyError{
					resp.StatusCode,
					-1,
					"Requested resource could not be found"}
			}
		}
	}
	return "", err
}

// Returns an album
func Album(id string, token Token) (string, error) {
	id = parseId("album", id)
	return call(fmt.Sprintf("albums/%s", id), "GET", url.Values{}, token)
}

// Returns a list of albums
func Albums(albums []string, token Token) (string, error) {
	ids := buildIdString(albums, "album")
	params := url.Values{}
	params.Add("ids", ids)
	return call("albums", "GET", params, token)
}

// Returns tracks information from an album given an album id
func AlbumTracks(id string, token Token) (string, error) {
	id = parseId("album", id)
	return call(fmt.Sprintf("albums/%s/tracks", id), "GET", url.Values{}, token)
}

// Returns an artist
func Artist(id string, token Token) (string, error) {
	id = parseId("artist", id)
	return call(fmt.Sprintf("artists/%s", id), "GET", url.Values{}, token)
}

// Returns albums from an artist, given an artist id
func ArtistAlbums(id string, token Token) (string, error) {
	id = parseId("artist", id)
	return call(fmt.Sprintf("artists/%s/albums", id), "GET", url.Values{}, token)
}

// Returns an artists top tracks given artist id and country
func ArtistTopTracks(id string, country string, token Token) (string, error) {
	id = parseId("artist", id)
	params := url.Values{}
	params.Add("country", country)
	return call(fmt.Sprintf("artists/%s/top-tracks", id), "GET", params, token)
}

// Returns a list of artists
func Artists(artists []string, token Token) (string, error) {
	ids := buildIdString(artists, "artist")
	params := url.Values{}
	params.Add("ids", ids)
	return call("artists", "GET", params, token)
}

// Returns information about me
func Me(token Token) (string, error) {
	return call("me", "GET", url.Values{}, token)
}

// Returns a playlist
func Playlist(userId string, playlistId string, fields string, token Token) (string, error) {
	params := url.Values{}
	params.Add("fields", url.QueryEscape(fields))
	return call(fmt.Sprintf("users/%s/playlists/%s", userId, playlistId), "GET", params, token)
}

// Returns a users playlists
func Playlists(userId string, token Token) (string, error) {
	return call(fmt.Sprintf("users/%s/playlists", userId), "GET", url.Values{}, token)
}

// Returns a tracks from playlists
func Playlists(userId string, playlistId string, fields string, token Token) (string, error) {
	params := url.Values{}
	params.Add("fields", url.QueryEscape(fields))
	return call(fmt.Sprintf("users/%s/playlists/%s/tracks", userId, playlistId), "GET", params, token)
}

func Search(q string, qType string, limit int, offset int, token Token) (string, error) {
	params := url.Values{}
	params.Add("q", q)
	params.Add("type", url.QueryEscape(qType))
	params.Add("limit", fmt.Sprintf("%d", limit))
	params.Add("offset", fmt.Sprintf("%d", offset))

	return call("search", "GET", params, token)
}

// Returns a single track
func Track(id string, token Token) (string, error) {
	return call(fmt.Sprintf("tracks/%s", id), "GET", url.Values{}, token)
}

// Returns a list of tracks
func Tracks(tracks []string, token Token) (string, error) {
	ids := buildIdString(tracks, "track")
	params := url.Values{}
	params.Add("ids", ids)
	return call("tracks", "GET", params, token)
}

// Gets basic information about a user
func User(userId string, token Token) (string, error) {
	return call(fmt.Sprintf("users/%s", userId), "GET", url.Values{}, token)
}

func buildIdString(slice []string, idType string) string {
	ids := make([]string, len(slice))
	for i, item := range slice {
		ids[i] = parseId(idType, item)
	}
	return strings.Join(ids, ",")
}

// Parses the id from spotify uri's or http links before making api calls
func parseId(idType string, uri string) string {
	fields := strings.Split(uri, ":")
	if len(fields) == 3 {
		if fields[1] != idType {
			log.Print(fmt.Sprintf("Warning: expected if of type %s but found id of type %s", idType, fields[1]))
		}
		return fields[2]
	}
	fields = strings.Split(uri, "/")
	fmt.Println(len(fields))
	if len(fields) >= 3 {
		if idType != fields[len(fields)-2] {
			log.Print(fmt.Sprintf("Warning: expected if of type %s but found id of type %s", idType, fields[1]))
		}
		return fields[len(fields)-1]
	}
	return uri
}
<|endoftext|>


<commit_before>package firewall

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/flexiant/concerto/utils"
	"github.com/flexiant/concerto/webservice"
	"os"
	"text/tabwriter"
)

const endpoint = "cloud/firewall_profile"

type FirewallProfile stuct {
	Profile Policy `json:"firewall_profile"`
}

type Policy struct {
	Rules []Rule `json:"rules"`
	Md5   string `json:"md5"`
}

type Rule struct {
	Protocol string `json:"ip_protocol"`
	Cidr     string `json:"cidr_ip"`
	MaxPort  int    `json:"max_port"`
	MinPort  int    `json:"min_port"`
}

func list(policy Policy) error {
	w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
	fmt.Fprintln(w, "CIDR\tPROTOCOL\tMIN\tMAX")

	for _, rule := range policy.Rules {
		fmt.Fprintf(w, "%s\t%s\t%d\t%d\n", rule.Cidr, rule.Protocol, rule.MinPort, rule.MaxPort)
	}
	w.Flush()
	return nil
}

func get() Policy {
	var policy Policy
	webservice, err := webservice.NewWebService()
	if err != nil {
		log.Fatal(err)
	}

	log.Debugf("Current firewall driver %s", driverName())
	data, err := webservice.Get(endpoint)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(data, &policy)
	if err != nil {
		log.Fatal(err)
	}
	policy.Md5 = fmt.Sprintf("%x", md5.Sum(data))
	return policy
}

func cmdList(c *cli.Context) {
	list(get())
}

func cmdApply(c *cli.Context) {
	apply(get())
}

func cmdFlush(c *cli.Context) {
	flush()
}

func check(policy Policy, rule Rule) bool {
	exists := false
	for _, policyRule := range policy.Rules {
		if (policyRule.Cidr == rule.Cidr) && (policyRule.MaxPort == rule.MaxPort) && (policyRule.MinPort == rule.MinPort) && (policyRule.Protocol == rule.Protocol) {
			exists = true
		}
	}
	return exists
}

func cmdCheck(c *cli.Context) {
	utils.FlagsRequired(c, []string{"cidr", "minPort", "maxPort", "ipProtocol"})

	newRule := &Rule{
		c.String("ipProtocol"),
		c.String("cidr"),
		c.Int("minPort"),
		c.Int("maxPort"),
	}
	policy := get()

	fmt.Printf("%t\n", check(policy, *newRule))
}

func cmdAdd(c *cli.Context) {
	utils.FlagsRequired(c, []string{"cidr", "minPort", "maxPort", "ipProtocol"})

	newRule := &Rule{
		c.String("ipProtocol"),
		c.String("cidr"),
		c.Int("minPort"),
		c.Int("maxPort"),
	}
	policy := get()

	exists := check(policy, *newRule)

	if exists == false {
		fmt.Printf("%#v", newRule)
		fmt.Printf("We are going to insert firewall")
		policy.Rules = append(policy.Rules, *newRule)
		fmt.Printf("\n\n%#v\n\n", policy)

		webservice, err := webservice.NewWebService()
		utils.CheckError(err)

		profile := &FirewallProfile{
			policy
		}

		json, err := json.Marshal(profile)
		utils.CheckError(err)
		err, res, code := webservice.Put(endpoint, json)
		if res == nil {
			log.Fatal(err)
		}
		utils.CheckError(err)
		utils.CheckReturnCode(code)
	}

}

func SubCommands() []cli.Command {
	return []cli.Command{
		{
			Name:   "apply",
			Usage:  "Applies selected firewall rules in host",
			Action: cmdApply,
		},
		{
			Name:   "flush",
			Usage:  "Flushes all firewall rules from host",
			Action: cmdFlush,
		},
		{
			Name:   "check",
			Usage:  "Checks if a firewall rule exists in host",
			Action: cmdCheck,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "cidr",
					Usage: "CIDR",
				},
				cli.IntFlag{
					Name:  "minPort",
					Usage: "Minimum Port",
				},
				cli.IntFlag{
					Name:  "maxPort",
					Usage: "Maximum Port",
				},
				cli.StringFlag{
					Name:  "ipProtocol",
					Usage: "Ip protocol udp or tcp",
				},
			},
		},
		{
			Name:   "add",
			Usage:  "Adds a firewall rule to host",
			Action: cmdAdd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "cidr",
					Usage: "CIDR",
				},
				cli.IntFlag{
					Name:  "minPort",
					Usage: "Minimum Port",
				},
				cli.IntFlag{
					Name:  "maxPort",
					Usage: "Maximum Port",
				},
				cli.StringFlag{
					Name:  "ipProtocol",
					Usage: "Ip protocol udp or tcp",
				},
			},
		},
		{
			Name:   "list",
			Usage:  "Lists all firewall rules associated to host",
			Action: cmdList,
		},
	}
}
<commit_msg>Inclusion of add command<commit_after>package firewall

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/flexiant/concerto/utils"
	"github.com/flexiant/concerto/webservice"
	"os"
	"text/tabwriter"
)

const endpoint = "cloud/firewall_profile"

type FirewallProfile struct {
	Profile Policy `json:"firewall_profile"`
}

type Policy struct {
	Rules []Rule `json:"rules"`
	Md5   string `json:"md5"`
}

type Rule struct {
	Protocol string `json:"ip_protocol"`
	Cidr     string `json:"cidr_ip"`
	MaxPort  int    `json:"max_port"`
	MinPort  int    `json:"min_port"`
}

func list(policy Policy) error {
	w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
	fmt.Fprintln(w, "CIDR\tPROTOCOL\tMIN\tMAX")

	for _, rule := range policy.Rules {
		fmt.Fprintf(w, "%s\t%s\t%d\t%d\n", rule.Cidr, rule.Protocol, rule.MinPort, rule.MaxPort)
	}
	w.Flush()
	return nil
}

func get() Policy {
	var policy Policy
	webservice, err := webservice.NewWebService()
	if err != nil {
		log.Fatal(err)
	}

	log.Debugf("Current firewall driver %s", driverName())
	data, err := webservice.Get(endpoint)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(data, &policy)
	if err != nil {
		log.Fatal(err)
	}
	policy.Md5 = fmt.Sprintf("%x", md5.Sum(data))
	return policy
}

func cmdList(c *cli.Context) {
	list(get())
}

func cmdApply(c *cli.Context) {
	apply(get())
}

func cmdFlush(c *cli.Context) {
	flush()
}

func check(policy Policy, rule Rule) bool {
	exists := false
	for _, policyRule := range policy.Rules {
		if (policyRule.Cidr == rule.Cidr) && (policyRule.MaxPort == rule.MaxPort) && (policyRule.MinPort == rule.MinPort) && (policyRule.Protocol == rule.Protocol) {
			exists = true
		}
	}
	return exists
}

func cmdCheck(c *cli.Context) {
	utils.FlagsRequired(c, []string{"cidr", "minPort", "maxPort", "ipProtocol"})

	newRule := &Rule{
		c.String("ipProtocol"),
		c.String("cidr"),
		c.Int("minPort"),
		c.Int("maxPort"),
	}
	policy := get()

	fmt.Printf("%t\n", check(policy, *newRule))
}

func cmdAdd(c *cli.Context) {
	utils.FlagsRequired(c, []string{"cidr", "minPort", "maxPort", "ipProtocol"})

	newRule := &Rule{
		c.String("ipProtocol"),
		c.String("cidr"),
		c.Int("minPort"),
		c.Int("maxPort"),
	}
	policy := get()

	exists := check(policy, *newRule)

	if exists == false {
		fmt.Printf("%#v", newRule)
		fmt.Printf("We are going to insert firewall")
		policy.Rules = append(policy.Rules, *newRule)
		fmt.Printf("\n\n%#v\n\n", policy)

		webservice, err := webservice.NewWebService()
		utils.CheckError(err)

		profile := &FirewallProfile{
			policy,
		}

		json, err := json.Marshal(profile)
		utils.CheckError(err)
		err, res, code := webservice.Put(endpoint, json)
		if res == nil {
			log.Fatal(err)
		}
		utils.CheckError(err)
		utils.CheckReturnCode(code)
	}

}

func SubCommands() []cli.Command {
	return []cli.Command{
		{
			Name:   "apply",
			Usage:  "Applies selected firewall rules in host",
			Action: cmdApply,
		},
		{
			Name:   "flush",
			Usage:  "Flushes all firewall rules from host",
			Action: cmdFlush,
		},
		{
			Name:   "check",
			Usage:  "Checks if a firewall rule exists in host",
			Action: cmdCheck,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "cidr",
					Usage: "CIDR",
				},
				cli.IntFlag{
					Name:  "minPort",
					Usage: "Minimum Port",
				},
				cli.IntFlag{
					Name:  "maxPort",
					Usage: "Maximum Port",
				},
				cli.StringFlag{
					Name:  "ipProtocol",
					Usage: "Ip protocol udp or tcp",
				},
			},
		},
		{
			Name:   "add",
			Usage:  "Adds a firewall rule to host",
			Action: cmdAdd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "cidr",
					Usage: "CIDR",
				},
				cli.IntFlag{
					Name:  "minPort",
					Usage: "Minimum Port",
				},
				cli.IntFlag{
					Name:  "maxPort",
					Usage: "Maximum Port",
				},
				cli.StringFlag{
					Name:  "ipProtocol",
					Usage: "Ip protocol udp or tcp",
				},
			},
		},
		{
			Name:   "list",
			Usage:  "Lists all firewall rules associated to host",
			Action: cmdList,
		},
	}
}
<|endoftext|>


<commit_before>package main

// The "implements" tool uses code analysis and the conventions of the go-ceph
// project to produce a report comparing what go-ceph implements and what
// exists in the C ceph APIs. Only (lib)cephfs, (lib)rados, and (lib)rbd are
// and the equilvaent go-ceph packages are supported.
//
// Examples:
//   # generate a full report on cephfs with a function listing
//   ./implements --list ./cephfs
//
//   # generate a brief summary on all packages
//   ./implements ./cephfs ./rados ./rbd
//
//   # generate a comprehensive report on rbd in json
//   ./implements --list --json rbd

import (
	"flag"
	"log"
	"os"
	"path"

	"github.com/ceph/go-ceph/contrib/implements/internal/implements"
)

var (
	verbose    bool
	list       bool
	reportJSON bool
	outputJSON string
	outputText string

	// verbose logger
	logger = log.New(os.Stderr, "(implements/verbose) ", log.LstdFlags)
)

func abort(msg string) {
	log.Fatalf("error: %v\n", msg)
}

func init() {
	flag.BoolVar(&verbose, "verbose", false, "be more verbose (for debugging)")
	flag.BoolVar(&list, "list", false, "list functions")
	flag.BoolVar(&reportJSON, "json", false,
		"use JSON output format (this is a shortcut for '--report-json -')")

	flag.StringVar(&outputJSON, "report-json", "", "filename for JSON report")
	flag.StringVar(&outputText, "report-text", "", "filename for plain-text report")
}

func main() {
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		abort("missing package(s) to analyze")
	}
	if verbose {
		implements.SetLogger(logger)
	}

	rpts := []implements.Reporter{}
	// always annotate for now, leave the option of disabling it someday if it
	// gets costly
	o := implements.ReportOptions{
		List:     list,
		Annotate: true,
	}
	switch {
	case reportJSON:
		rpts = append(rpts, implements.NewJSONReport(o, os.Stdout))
	case outputJSON == "-":
		rpts = append(rpts, implements.NewJSONReport(o, os.Stdout))
	case outputJSON != "":
		f, err := os.Create(outputJSON)
		if err != nil {
			abort(err.Error())
		}
		defer f.Close()
		rpts = append(rpts, implements.NewJSONReport(o, f))
	}
	switch {
	case outputText == "-":
		rpts = append(rpts, implements.NewTextReport(o, os.Stdout))
	case outputText != "":
		f, err := os.Create(outputText)
		if err != nil {
			abort(err.Error())
		}
		defer f.Close()
		rpts = append(rpts, implements.NewTextReport(o, f))
	}
	if len(rpts) == 0 {
		// no report types were explicitly selected. Use text report to stdout.
		rpts = append(rpts, implements.NewTextReport(o, os.Stdout))
	}

	for _, pkgref := range args[0:] {
		source, pkg := path.Split(pkgref)
		switch pkg {
		case "cephfs", "rados", "rbd":
			if verbose {
				logger.Printf("Processing package: %s\n", pkg)
			}
		default:
			abort("unknown package name: " + pkg)
		}
		if source == "" {
			source = "."
		}
		ii := implements.NewInspector()
		if err := implements.CephCFunctions(pkg, ii); err != nil {
			abort(err.Error())
		}
		if err := implements.CephGoFunctions(source, pkg, ii); err != nil {
			abort(err.Error())
		}
		for _, r := range rpts {
			if err := r.Report(pkg, ii); err != nil {
				abort(err.Error())
			}
		}
	}
	for _, r := range rpts {
		r.Done()
	}
}
<commit_msg>implements: add support for scanning the api of the "admin" subpackages<commit_after>package main

// The "implements" tool uses code analysis and the conventions of the go-ceph
// project to produce a report comparing what go-ceph implements and what
// exists in the C ceph APIs. Only (lib)cephfs, (lib)rados, and (lib)rbd are
// and the equilvaent go-ceph packages are supported.
//
// Examples:
//   # generate a full report on cephfs with a function listing
//   ./implements --list ./cephfs
//
//   # generate a brief summary on all packages
//   ./implements ./cephfs ./rados ./rbd
//
//   # generate a comprehensive report on rbd in json
//   ./implements --list --json rbd

import (
	"flag"
	"log"
	"os"

	"github.com/ceph/go-ceph/contrib/implements/internal/implements"
)

var (
	verbose    bool
	list       bool
	reportJSON bool
	outputJSON string
	outputText string

	// verbose logger
	logger = log.New(os.Stderr, "(implements/verbose) ", log.LstdFlags)
)

func abort(msg string) {
	log.Fatalf("error: %v\n", msg)
}

func init() {
	flag.BoolVar(&verbose, "verbose", false, "be more verbose (for debugging)")
	flag.BoolVar(&list, "list", false, "list functions")
	flag.BoolVar(&reportJSON, "json", false,
		"use JSON output format (this is a shortcut for '--report-json -')")

	flag.StringVar(&outputJSON, "report-json", "", "filename for JSON report")
	flag.StringVar(&outputText, "report-text", "", "filename for plain-text report")
}

// TODO: this is a stub implementation that doesn't do anything.
// the intent is to eventually be able to tell a go-ceph sub-package
// like "rbd" or "cephfs/admin" apart from a real path like "/home/foo/go-ceph/rbd".
func splitPkg(s string) (string, string) {
	return "", s
}

func main() {
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		abort("missing package(s) to analyze")
	}
	if verbose {
		implements.SetLogger(logger)
	}

	rpts := []implements.Reporter{}
	// always annotate for now, leave the option of disabling it someday if it
	// gets costly
	o := implements.ReportOptions{
		List:     list,
		Annotate: true,
	}
	switch {
	case reportJSON:
		rpts = append(rpts, implements.NewJSONReport(o, os.Stdout))
	case outputJSON == "-":
		rpts = append(rpts, implements.NewJSONReport(o, os.Stdout))
	case outputJSON != "":
		f, err := os.Create(outputJSON)
		if err != nil {
			abort(err.Error())
		}
		defer f.Close()
		rpts = append(rpts, implements.NewJSONReport(o, f))
	}
	switch {
	case outputText == "-":
		rpts = append(rpts, implements.NewTextReport(o, os.Stdout))
	case outputText != "":
		f, err := os.Create(outputText)
		if err != nil {
			abort(err.Error())
		}
		defer f.Close()
		rpts = append(rpts, implements.NewTextReport(o, f))
	}
	if len(rpts) == 0 {
		// no report types were explicitly selected. Use text report to stdout.
		rpts = append(rpts, implements.NewTextReport(o, os.Stdout))
	}

	for _, pkgref := range args[0:] {
		source, pkg := splitPkg(pkgref)
		checkCLang := false
		switch pkg {
		case "cephfs", "rados", "rbd":
			checkCLang = true
			if verbose {
				logger.Printf("Processing package (with C): %s\n", pkg)
			}
		case "cephfs/admin", "rbd/admin", "rgw/admin":
			if verbose {
				logger.Printf("Processing package: %s\n", pkg)
			}
		default:
			abort("unknown package name: " + pkg)
		}
		if source == "" {
			source = "."
		}
		ii := implements.NewInspector()
		if checkCLang {
			if err := implements.CephCFunctions(pkg, ii); err != nil {
				abort(err.Error())
			}
		}
		if err := implements.CephGoFunctions(source, pkg, ii); err != nil {
			abort(err.Error())
		}
		for _, r := range rpts {
			if err := r.Report(pkg, ii); err != nil {
				abort(err.Error())
			}
		}
	}
	for _, r := range rpts {
		r.Done()
	}
}
<|endoftext|>


<commit_before>package gofakeit

import (
	"math/rand"
)

// Username will genrate a random username based upon picking a random lastname and random numbers at the end
func Username() string {
	return getRandValue([]string{"person", "last"}) + replaceWithNumbers("####")
}

// Password will generate a random password
// Minimum number length of 5 if less than
func Password(lower bool, upper bool, numeric bool, special bool, space bool, num int) string {
	// Make sure the num minimun is at least 5
	if num < 5 {
		num = 5
	}
	i := 0
	b := make([]byte, num)
	var passString string

	if lower {
		passString += lowerStr
		b[i] = lowerStr[rand.Int63()%int64(len(lowerStr))]
		i++
	}
	if upper {
		passString += upperStr
		b[i] = upperStr[rand.Int63()%int64(len(upperStr))]
		i++
	}
	if numeric {
		passString += numericStr
		b[i] = numericStr[rand.Int63()%int64(len(numericStr))]
		i++
	}
	if special {
		passString += specialStr
		b[i] = specialStr[rand.Int63()%int64(len(specialStr))]
		i++
	}
	if space {
		passString += spaceStr
		b[i] = spaceStr[rand.Int63()%int64(len(spaceStr))]
		i++
	}

	// Set default if empty
	if passString == "" {
		passString = lowerStr + numericStr
	}

	// Loop through and add it up
	for i <= num-1 {
		b[i] = passString[rand.Int63()%int64(len(passString))]
		i++
	}

	// Shuffle bytes
	for i := range b {
		j := rand.Intn(i + 1)
		b[i], b[j] = b[j], b[i]
	}

	return string(b)
}

func addAuthLookup() {
	AddLookupData("username", Info{
		Category:    "auth",
		Description: "Generates a random username",
		Example:     "Daniel1364",
		Call: func(m *map[string][]string, info *Info) (interface{}, error) {
			return Username(), nil
		},
	})

	AddLookupData("password", Info{
		Category:    "auth",
		Description: "Generates a random password",
		Example:     "EEP+wwpk 4lU-eHNXlJZ4n K9%v&TZ9e",
		Params: []Param{
			{Field: "lower", Type: "bool", Default: "true", Description: "Whether or not to add lower case characters"},
			{Field: "upper", Type: "bool", Default: "true", Description: "Whether or not to add upper case characters"},
			{Field: "numeric", Type: "bool", Default: "true", Description: "Whether or not to add numeric characters"},
			{Field: "special", Type: "bool", Default: "true", Description: "Whether or not to add special characters"},
			{Field: "space", Type: "bool", Default: "true", Description: "Whether or not to add spaces"},
			{Field: "length", Type: "int", Default: "12", Description: "Number of characters in password"},
		},
		Call: func(m *map[string][]string, info *Info) (interface{}, error) {
			lower, err := info.GetBool(m, "lower")
			if err != nil {
				return nil, err
			}

			upper, err := info.GetBool(m, "upper")
			if err != nil {
				return nil, err
			}

			numeric, err := info.GetBool(m, "numeric")
			if err != nil {
				return nil, err
			}

			special, err := info.GetBool(m, "special")
			if err != nil {
				return nil, err
			}

			space, err := info.GetBool(m, "space")
			if err != nil {
				return nil, err
			}

			length, err := info.GetInt(m, "length")
			if err != nil {
				return nil, err
			}

			return Password(lower, upper, numeric, special, space, length), nil
		},
	})
}
<commit_msg>password - turned space off by default<commit_after>package gofakeit

import (
	"math/rand"
)

// Username will genrate a random username based upon picking a random lastname and random numbers at the end
func Username() string {
	return getRandValue([]string{"person", "last"}) + replaceWithNumbers("####")
}

// Password will generate a random password
// Minimum number length of 5 if less than
func Password(lower bool, upper bool, numeric bool, special bool, space bool, num int) string {
	// Make sure the num minimun is at least 5
	if num < 5 {
		num = 5
	}
	i := 0
	b := make([]byte, num)
	var passString string

	if lower {
		passString += lowerStr
		b[i] = lowerStr[rand.Int63()%int64(len(lowerStr))]
		i++
	}
	if upper {
		passString += upperStr
		b[i] = upperStr[rand.Int63()%int64(len(upperStr))]
		i++
	}
	if numeric {
		passString += numericStr
		b[i] = numericStr[rand.Int63()%int64(len(numericStr))]
		i++
	}
	if special {
		passString += specialStr
		b[i] = specialStr[rand.Int63()%int64(len(specialStr))]
		i++
	}
	if space {
		passString += spaceStr
		b[i] = spaceStr[rand.Int63()%int64(len(spaceStr))]
		i++
	}

	// Set default if empty
	if passString == "" {
		passString = lowerStr + numericStr
	}

	// Loop through and add it up
	for i <= num-1 {
		b[i] = passString[rand.Int63()%int64(len(passString))]
		i++
	}

	// Shuffle bytes
	for i := range b {
		j := rand.Intn(i + 1)
		b[i], b[j] = b[j], b[i]
	}

	return string(b)
}

func addAuthLookup() {
	AddLookupData("username", Info{
		Category:    "auth",
		Description: "Generates a random username",
		Example:     "Daniel1364",
		Call: func(m *map[string][]string, info *Info) (interface{}, error) {
			return Username(), nil
		},
	})

	AddLookupData("password", Info{
		Category:    "auth",
		Description: "Generates a random password",
		Example:     "EEP+wwpk 4lU-eHNXlJZ4n K9%v&TZ9e",
		Params: []Param{
			{Field: "lower", Type: "bool", Default: "true", Description: "Whether or not to add lower case characters"},
			{Field: "upper", Type: "bool", Default: "true", Description: "Whether or not to add upper case characters"},
			{Field: "numeric", Type: "bool", Default: "true", Description: "Whether or not to add numeric characters"},
			{Field: "special", Type: "bool", Default: "true", Description: "Whether or not to add special characters"},
			{Field: "space", Type: "bool", Default: "false", Description: "Whether or not to add spaces"},
			{Field: "length", Type: "int", Default: "12", Description: "Number of characters in password"},
		},
		Call: func(m *map[string][]string, info *Info) (interface{}, error) {
			lower, err := info.GetBool(m, "lower")
			if err != nil {
				return nil, err
			}

			upper, err := info.GetBool(m, "upper")
			if err != nil {
				return nil, err
			}

			numeric, err := info.GetBool(m, "numeric")
			if err != nil {
				return nil, err
			}

			special, err := info.GetBool(m, "special")
			if err != nil {
				return nil, err
			}

			space, err := info.GetBool(m, "space")
			if err != nil {
				return nil, err
			}

			length, err := info.GetInt(m, "length")
			if err != nil {
				return nil, err
			}

			return Password(lower, upper, numeric, special, space, length), nil
		},
	})
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	_ "github.com/mattn/go-sqlite3" //we want to use sqlite natively
	"github.com/thewhitetulip/Tasks/types"
	"log"
	"strings"
	"time"
)

var database *sql.DB
var err error

func init() {
	database, err = sql.Open("sqlite3", "./tasks.db")
	if err != nil {
		log.Println(err)
	}
}

//Close function closes this database connection
func Close() {
	database.Close()
}

//GetTasks retrieves all the tasks depending on the
//status pending or trashed or completed
func GetTasks(status string) types.Context {
	var task []types.Task
	var context types.Context
	var TaskID int
	var TaskTitle string
	var TaskContent string
	var TaskCreated time.Time
	var TaskPriority string
	var getTasksql string

	basicSQL := "select id, title, content, created_date, priority from task "
	if status == "pending" {
		getTasksql = basicSQL + " where finish_date is null and is_deleted='N' order by priority desc, created_date asc"
	} else if status == "deleted" {
		getTasksql = basicSQL + " where is_deleted='Y' order by priority desc, created_date asc"
	} else if status == "completed" {
		getTasksql = basicSQL + " where finish_date is not null order by priority desc, created_date asc"
	}

	rows, err := database.Query(getTasksql)
	if err != nil {
		log.Println(err)
	}
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&TaskID, &TaskTitle, &TaskContent, &TaskCreated, &TaskPriority)
		TaskContent = strings.Replace(TaskContent, "\n", "
", -1)
		if err != nil {
			log.Println(err)
		}
		TaskCreated = TaskCreated.Local()
		a := types.Task{Id: TaskID, Title: TaskTitle, Content: TaskContent, Created: TaskCreated.Format(time.UnixDate)[0:20], Priority: TaskPriority}
		task = append(task, a)
	}
	context = types.Context{Tasks: task, Navigation: status}
	return context
}

//GetTaskByID function gets the tasks from the ID passed to the function, used to populate EditTask
func GetTaskByID(id int) types.Context {
	var tasks []types.Task
	var task types.Task

	getTasksql := "select id, title, content, priority from task where id=?"

	rows, err := database.Query(getTasksql, id)
	if err != nil {
		log.Println(err)
	}
	defer rows.Close()
	if rows.Next() {
		err := rows.Scan(&task.Id, &task.Title, &task.Content, &task.Priority)
		if err != nil {
			log.Println(err)
			//send email to respective people
		}
	}
	tasks = append(tasks, task)
	context := types.Context{Tasks: tasks, Navigation: "edit"}
	return context
}

//TrashTask is used to delete the task
func TrashTask(id int) error {
	trashSQL, err := database.Prepare("update task set is_deleted='Y',last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(trashSQL).Exec(id)
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//CompleteTask  is used to mark tasks as complete
func CompleteTask(id int) error {
	stmt, err := database.Prepare("update task set is_deleted='Y', finish_date=datetime(),last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(stmt).Exec(id)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//DeleteAll is used to empty the trash
func DeleteAll() error {
	stmt, err := database.Prepare("delete from task where is_deleted='Y'")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(stmt).Exec()
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//RestoreTask is used to restore tasks from the Trash
func RestoreTask(id int) error {
	restoreSQL, err := database.Prepare("update task set is_deleted='N',last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(restoreSQL).Exec(id)
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//RestoreTask is used to restore tasks from the Trash
func RestoreTaskFromComplete(id int) error {
	restoreSQL, err := database.Prepare("update task set finish_date=null,last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(restoreSQL).Exec(id)
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//DeleteTask is used to delete the task from the database
func DeleteTask(id int) error {
	deleteSQL, err := database.Prepare("delete from task where id = ?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(deleteSQL).Exec(id)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//AddTask is used to add the task in the database
func AddTask(title, content string, taskPriority int) error {
	restoreSQL, err := database.Prepare("insert into task(title, content, priority, created_date, last_modified_at) values(?,?,datetime(), datetime())")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	_, err = tx.Stmt(restoreSQL).Exec(title, content, taskPriority)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//UpdateTask is used to update the tasks in the database
func UpdateTask(id int, title string, content string) error {
	SQL, err := database.Prepare("update task set title=?, content=? where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()

	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(SQL).Exec(title, content, id)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		log.Println(tx.Commit())
	}
	return err
}

//SearchTask is used to return the search results depending on the query
func SearchTask(query string) types.Context {
	stmt := "select id, title, content, created_date from task where title like '%" + query + "%' or content like '%" + query + "%'"
	var task []types.Task
	var TaskID int
	var TaskTitle string
	var TaskContent string
	var TaskCreated time.Time
	var context types.Context

	rows, err := database.Query(stmt, query, query)
	if err != nil {
		log.Println(err)
	}
	for rows.Next() {
		err := rows.Scan(&TaskID, &TaskTitle, &TaskContent, &TaskCreated)
		if err != nil {
			log.Println(err)
		}
		TaskTitle = strings.Replace(TaskTitle, query, ""+query+"", -1)
		TaskContent = strings.Replace(TaskContent, query, ""+query+"", -1)
		a := types.Task{Id: TaskID, Title: TaskTitle, Content: TaskContent, Created: TaskCreated.Format(time.UnixDate)[0:20]}
		task = append(task, a)
	}
	context = types.Context{Tasks: task, Search: query}
	return context
}
<commit_msg>updated db for priority<commit_after>package db

import (
	"database/sql"
	_ "github.com/mattn/go-sqlite3" //we want to use sqlite natively
	"github.com/thewhitetulip/Tasks/types"
	"log"
	"strings"
	"time"
)

var database *sql.DB
var err error

func init() {
	database, err = sql.Open("sqlite3", "./tasks.db")
	if err != nil {
		log.Println(err)
	}
}

//Close function closes this database connection
func Close() {
	database.Close()
}

//GetTasks retrieves all the tasks depending on the
//status pending or trashed or completed
func GetTasks(status string) types.Context {
	var task []types.Task
	var context types.Context
	var TaskID int
	var TaskTitle string
	var TaskContent string
	var TaskCreated time.Time
	var TaskPriority string
	var getTasksql string

	basicSQL := "select id, title, content, created_date, priority from task "
	if status == "pending" {
		getTasksql = basicSQL + " where finish_date is null and is_deleted='N' order by priority desc, created_date asc"
	} else if status == "deleted" {
		getTasksql = basicSQL + " where is_deleted='Y' order by priority desc, created_date asc"
	} else if status == "completed" {
		getTasksql = basicSQL + " where finish_date is not null order by priority desc, created_date asc"
	}

	rows, err := database.Query(getTasksql)
	if err != nil {
		log.Println(err)
	}
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&TaskID, &TaskTitle, &TaskContent, &TaskCreated, &TaskPriority)
		TaskContent = strings.Replace(TaskContent, "\n", "
", -1)
		if err != nil {
			log.Println(err)
		}
		TaskCreated = TaskCreated.Local()
		a := types.Task{Id: TaskID, Title: TaskTitle, Content: TaskContent, Created: TaskCreated.Format(time.UnixDate)[0:20], Priority: TaskPriority}
		task = append(task, a)
	}
	context = types.Context{Tasks: task, Navigation: status}
	return context
}

//GetTaskByID function gets the tasks from the ID passed to the function, used to populate EditTask
func GetTaskByID(id int) types.Context {
	var tasks []types.Task
	var task types.Task

	getTasksql := "select id, title, content, priority from task where id=?"

	rows, err := database.Query(getTasksql, id)
	if err != nil {
		log.Println(err)
	}
	defer rows.Close()
	if rows.Next() {
		err := rows.Scan(&task.Id, &task.Title, &task.Content, &task.Priority)
		if err != nil {
			log.Println(err)
			//send email to respective people
		}
	}
	tasks = append(tasks, task)
	context := types.Context{Tasks: tasks, Navigation: "edit"}
	return context
}

//TrashTask is used to delete the task
func TrashTask(id int) error {
	trashSQL, err := database.Prepare("update task set is_deleted='Y',last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(trashSQL).Exec(id)
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//CompleteTask  is used to mark tasks as complete
func CompleteTask(id int) error {
	stmt, err := database.Prepare("update task set is_deleted='Y', finish_date=datetime(),last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(stmt).Exec(id)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//DeleteAll is used to empty the trash
func DeleteAll() error {
	stmt, err := database.Prepare("delete from task where is_deleted='Y'")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(stmt).Exec()
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//RestoreTask is used to restore tasks from the Trash
func RestoreTask(id int) error {
	restoreSQL, err := database.Prepare("update task set is_deleted='N',last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(restoreSQL).Exec(id)
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//RestoreTask is used to restore tasks from the Trash
func RestoreTaskFromComplete(id int) error {
	restoreSQL, err := database.Prepare("update task set finish_date=null,last_modified_at=datetime() where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(restoreSQL).Exec(id)
	if err != nil {
		log.Println("doing rollback")
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//DeleteTask is used to delete the task from the database
func DeleteTask(id int) error {
	deleteSQL, err := database.Prepare("delete from task where id = ?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(deleteSQL).Exec(id)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//AddTask is used to add the task in the database
func AddTask(title, content string, taskPriority int) error {
	restoreSQL, err := database.Prepare("insert into task(title, content, priority, created_date, last_modified_at) values(?,?,?,datetime(), datetime())")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()
	_, err = tx.Stmt(restoreSQL).Exec(title, content, taskPriority)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

//UpdateTask is used to update the tasks in the database
func UpdateTask(id int, title string, content string) error {
	SQL, err := database.Prepare("update task set title=?, content=? where id=?")
	if err != nil {
		log.Println(err)
	}
	tx, err := database.Begin()

	if err != nil {
		log.Println(err)
	}
	_, err = tx.Stmt(SQL).Exec(title, content, id)
	if err != nil {
		log.Println(err)
		tx.Rollback()
	} else {
		log.Println(tx.Commit())
	}
	return err
}

//SearchTask is used to return the search results depending on the query
func SearchTask(query string) types.Context {
	stmt := "select id, title, content, created_date from task where title like '%" + query + "%' or content like '%" + query + "%'"
	var task []types.Task
	var TaskID int
	var TaskTitle string
	var TaskContent string
	var TaskCreated time.Time
	var context types.Context

	rows, err := database.Query(stmt, query, query)
	if err != nil {
		log.Println(err)
	}
	for rows.Next() {
		err := rows.Scan(&TaskID, &TaskTitle, &TaskContent, &TaskCreated)
		if err != nil {
			log.Println(err)
		}
		TaskTitle = strings.Replace(TaskTitle, query, ""+query+"", -1)
		TaskContent = strings.Replace(TaskContent, query, ""+query+"", -1)
		a := types.Task{Id: TaskID, Title: TaskTitle, Content: TaskContent, Created: TaskCreated.Format(time.UnixDate)[0:20]}
		task = append(task, a)
	}
	context = types.Context{Tasks: task, Search: query}
	return context
}
<|endoftext|>