<commit_before>package core

// transfer2go data core module, request implementation
// Author: Valentin Kuznetsov <vkuznet@gmail.com>

import (
	"container/heap"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/vkuznet/transfer2go/utils"
)

// AgentStatus data type
type AgentStatus struct {
	Url       string            `json:"url"`      // agent url
	Name      string            `json:"name"`     // agent name or alias
	TimeStamp int64             `json:"ts"`       // time stamp
	Catalog   string            `json:"catalog"`  // underlying TFC catalog
	Protocol  string            `json:"protocol"` // underlying transfer protocol
	Backend   string            `json:"backend"`  // underlying transfer backend
	Tool      string            `json:"tool"`     // underlying transfer tool, e.g. xrdcp
	ToolOpts  string            `json:"toolopts"` // options for backend tool
	Agents    map[string]string `json:"agents"`   // list of known agents
	Addrs     []string          `json:"addrs"`    // list of all IP addresses
	Metrics   map[string]int64  `json:"metrics"`  // agent metrics
}

// Processor is an object who process' given task
// The logic of the Processor should be implemented.
type Processor struct {
}

// Request interface defines a task process
type Request interface {
	Process(*TransferRequest) error
}

// RequestFunc is a function type that implements the Request interface
type RequestFunc func(*TransferRequest) error

// Decorator wraps a request with extra behavior
type Decorator func(Request) Request

// DefaultProcessor is a default processor instance
var DefaultProcessor = &Processor{}

// String provides string representation of given agent status
func (a *AgentStatus) String() string {
	return fmt.Sprintf("<Agent name=%s url=%s catalog=%s protocol=%s backend=%s tool=%s toolOpts=%s agents=%v addrs=%v metrics(%v)>", a.Name, a.Url, a.Catalog, a.Protocol, a.Backend, a.Tool, a.ToolOpts, a.Agents, a.Addrs, a.Metrics)
}

// Process defines execution process for a given task
func (e *Processor) Process(t *TransferRequest) error {
	return nil
}

// Process is a method of TransferRequest
func (f RequestFunc) Process(t *TransferRequest) error {
	return f(t)
}

// filleTransferRequest creates HTTP request to transfer a given file name
// https://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
func fileTransferRequest(c CatalogEntry, tr *TransferRequest) (*http.Response, error) {
	file, err := os.Open(c.Pfn)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	// Define go pipe
	pr, pw := io.Pipe()
	writer := multipart.NewWriter(pw)
	var resp *http.Response
	// we need to wait for everything to be done
	done := make(chan error)
	go func() {
		url := fmt.Sprintf("%s/upload", tr.DstUrl)
		req, err := http.NewRequest("POST", url, pr)
		if err != nil {
			done <- err
			return
		}
		req.Header.Set("Content-Type", writer.FormDataContentType())
		req.Header.Set("Pfn", c.Pfn)
		req.Header.Set("Lfn", c.Lfn)
		req.Header.Set("Bytes", fmt.Sprintf("%d", c.Bytes))
		req.Header.Set("Hash", c.Hash)
		req.Header.Set("Src", tr.SrcAlias)
		req.Header.Set("Dst", tr.DstAlias)
		client := utils.HttpClient()
		resp, err = client.Do(req)
		if err != nil {
			done <- err
			return
		}
		if resp.StatusCode != 200 {
			done <- errors.New("Status Code is not 200")
			return
		}
		done <- nil
	}()
	part, err := writer.CreateFormFile("data", filepath.Base(c.Pfn))
	if err != nil {
		return nil, err
	}
	// Use copy of writer to avoid deadlock condition
	out := io.MultiWriter(part)
	_, err = io.Copy(out, file)
	if err != nil {
		return nil, err
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}
	err = pw.Close()
	if err != nil {
		return nil, err
	}
	err = <-done
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// helper function to perform transfer via HTTP protocol
func httpTransfer(c CatalogEntry, t *TransferRequest) (string, error) {
	// create file transfer request
	resp, err := fileTransferRequest(c, t)
	if err != nil && resp.StatusCode != 200 {
		return "", err
	}
  defer resp.Body.Close()
	var r CatalogEntry
	err = json.NewDecoder(resp.Body).Decode(&r)
	if err != nil {
		return "", err
	}
	return r.Pfn, nil
}

// Store returns a Decorator that stores request
func Store() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			t.Id = time.Now().Unix()
			item := &Item{
				Value:    *t,
				priority: t.Priority,
			}
			fmt.Println(*t)
			err := TFC.InsertRequest(*t)
			if err != nil {
				return err
			} else {
				heap.Push(&RequestQueue, item)
			}
			log.WithFields(log.Fields{
				"Request": t,
			}).Println("Request Saved")
			return r.Process(t)
		})
	}
}

// Delete returns a Decorator that deletes request from heap
func Delete() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			// Delete request from PriorityQueue. The complexity is O(n) where n = heap.Len()
			index := -1
			var err error

			for _, item := range RequestQueue {
				if item.Value.Id == t.Id {
					index = item.index
					break
				}
			}

			if index < RequestQueue.Len() && index >= 0 {
				err = TFC.UpdateRequest(t.Id, "deleted")
				if err != nil {
					t.Status = "error"
					return err
				} else {
					// TODO: May be we need to add lock over here.
					heap.Remove(&RequestQueue, index)
					t.Status = "deleted"
					log.WithFields(log.Fields{
						"Request": t,
					}).Println("Request Deleted")
				}
			} else {
				t.Status = "error"
				err = errors.New("Can't find request in heap")
				return err
			}

			return r.Process(t)
		})
	}
}

// Transfer returns a Decorator that performs request transfers by pull model
func PullTransfer() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			log.WithFields(log.Fields{
				"Request": t.String(),
			}).Println("Request Transfer")
			// obtain information about source and destination agents
			url := fmt.Sprintf("%s/status", t.DstUrl)
			resp := utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var dstAgent AgentStatus
			err := json.Unmarshal(resp.Data, &dstAgent)
			if err != nil {
				return err
			}
			url = fmt.Sprintf("%s/status", t.SrcUrl)
			resp = utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var srcAgent AgentStatus
			err = json.Unmarshal(resp.Data, &srcAgent)
			if err != nil {
				return err
			}
			// if both are up then send acknowledge message to destination on /pullack url.
			body, err := json.Marshal(t)
			if err != nil {
				return err
			}
			url = fmt.Sprintf("%s/pull", t.DstUrl)
			resp = utils.FetchResponse(url, body)
			// check return status code
			if resp.StatusCode != 200 {
				return fmt.Errorf("Response %s, error=%s", resp.Status, string(resp.Data))
			}
			return r.Process(t)
		})
	}
}

// Transfer returns a Decorator that performs request transfers
func PushTransfer() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			log.WithFields(log.Fields{
				"Request": t.String(),
			}).Println("Request Transfer")
			var records []CatalogEntry
			// Consider those requests which are failed in previous iteration.
			// If it is nil then request must be passing through first iteration.
			if t.FailedRecords != nil {
				records = t.FailedRecords
			} else {
				records = TFC.Records(*t)
			}
			if len(records) == 0 {
				// file does not exists in TFC, nothing to do, return immediately
				log.WithFields(log.Fields{
					"TransferRequest": t,
				}).Warn("Does not match anything in TFC of this agent\n", t)
				return r.Process(t)
			}
			// obtain information about source and destination agents
			url := fmt.Sprintf("%s/status", t.DstUrl)
			resp := utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var dstAgent AgentStatus
			err := json.Unmarshal(resp.Data, &dstAgent)
			if err != nil {
				return err
			}
			url = fmt.Sprintf("%s/status", t.SrcUrl)
			resp = utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var srcAgent AgentStatus
			err = json.Unmarshal(resp.Data, &srcAgent)
			if err != nil {
				return err
			}

			// TODO: I need to implement bulk transfer for all files in found records
			// so far I loop over them individually and transfer one by one
			var trRecords []CatalogEntry // list of successfully transferred records
			var failedRecords []CatalogEntry
			// Overwrite the previous error status
			t.Status = ""
			for _, rec := range records {

				time0 := time.Now().Unix()

				AgentMetrics.Bytes.Inc(rec.Bytes)

				// if protocol is not given use default one: HTTP
				var rpfn string // remote PFN
				if srcAgent.Protocol == "" || srcAgent.Protocol == "http" {
					log.WithFields(log.Fields{
						"dstAgent": dstAgent.String(),
					}).Println("Transfer via HTTP protocol to", dstAgent.String())
					rpfn, err = httpTransfer(rec, t)
					if err != nil {
						log.WithFields(log.Fields{
							"TransferRequest": t.String(),
							"Record":          rec.String(),
							"Err":             err,
						}).Error("Transfer", rec.String(), t.String(), err)
						t.Status = err.Error()
						failedRecords = append(failedRecords, rec)
						continue // if we fail on single record we continue with others
					}
				} else {
					// construct remote PFN by using destination agent backend and record LFN
					rpfn = fmt.Sprintf("%s%s", dstAgent.Backend, rec.Lfn)
					// perform transfer with the help of backend tool
					var cmd *exec.Cmd
					if srcAgent.ToolOpts == "" {
						cmd = exec.Command(srcAgent.Tool, rec.Pfn, rpfn)
					} else {
						cmd = exec.Command(srcAgent.Tool, srcAgent.ToolOpts, rec.Pfn, rpfn)
					}
					log.WithFields(log.Fields{
						"Command": cmd,
					}).Println("Transfer command")
					err = cmd.Run()
					if err != nil {
						log.WithFields(log.Fields{
							"Tool":         srcAgent.Tool,
							"Tool options": srcAgent.ToolOpts,
							"PFN":          rec.Pfn,
							"Remote PFN":   rpfn,
							"Err":          err,
						}).Error("Transfer")
						t.Status = err.Error()
						failedRecords = append(failedRecords, rec)
						continue // if we fail on single record we continue with others
					}
				}
				r := CatalogEntry{Dataset: rec.Dataset, Block: rec.Block, Lfn: rec.Lfn, Pfn: rpfn, Bytes: rec.Bytes, Hash: rec.Hash, TransferTime: (time.Now().Unix() - time0), Timestamp: time.Now().Unix()}
				trRecords = append(trRecords, r)

				// record how much we transferred
				AgentMetrics.TotalBytes.Inc(r.Bytes) // keep growing
				AgentMetrics.Total.Inc(1)            // keep growing
				AgentMetrics.Bytes.Dec(rec.Bytes)    // decrement since we're done

			}
			// Add entry for remote TFC after transfer is completed
			url = fmt.Sprintf("%s/tfc", t.DstUrl)
			d, e := json.Marshal(trRecords)
			if e != nil {
				return e
			}
			resp = utils.FetchResponse(url, d) // POST request
			if resp.Error != nil {
				return resp.Error
			}
			t.FailedRecords = failedRecords
			return r.Process(t)
		})
	}
}

// Logging returns a Decorator that logs client requests
func Logging(l *log.Logger) Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			l.Println("TransferRequest", t)
			return r.Process(t)
		})
	}
}

// Pause returns a Decorator that pauses request for a given time interval
func Pause(interval time.Duration) Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			if interval > 0 {
				log.WithFields(log.Fields{
					"Request":  t,
					"Interval": interval,
				}).Println("TransferRequest is paused by")
				time.Sleep(interval)
			}
			return r.Process(t)
		})
	}
}

// Tracer returns a Decorator that traces given request
func Tracer() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			log.WithFields(log.Fields{
				"TransferRequest": t,
			}).Println("Trace")
			return r.Process(t)
		})
	}
}

// Decorate decorates a Request r with all given Decorators
func Decorate(r Request, ds ...Decorator) Request {
	decorated := r
	for _, decorate := range ds {
		decorated = decorate(decorated)
	}
	return decorated
}
<commit_msg>Change and to or condition<commit_after>package core

// transfer2go data core module, request implementation
// Author: Valentin Kuznetsov <vkuznet@gmail.com>

import (
	"container/heap"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/vkuznet/transfer2go/utils"
)

// AgentStatus data type
type AgentStatus struct {
	Url       string            `json:"url"`      // agent url
	Name      string            `json:"name"`     // agent name or alias
	TimeStamp int64             `json:"ts"`       // time stamp
	Catalog   string            `json:"catalog"`  // underlying TFC catalog
	Protocol  string            `json:"protocol"` // underlying transfer protocol
	Backend   string            `json:"backend"`  // underlying transfer backend
	Tool      string            `json:"tool"`     // underlying transfer tool, e.g. xrdcp
	ToolOpts  string            `json:"toolopts"` // options for backend tool
	Agents    map[string]string `json:"agents"`   // list of known agents
	Addrs     []string          `json:"addrs"`    // list of all IP addresses
	Metrics   map[string]int64  `json:"metrics"`  // agent metrics
}

// Processor is an object who process' given task
// The logic of the Processor should be implemented.
type Processor struct {
}

// Request interface defines a task process
type Request interface {
	Process(*TransferRequest) error
}

// RequestFunc is a function type that implements the Request interface
type RequestFunc func(*TransferRequest) error

// Decorator wraps a request with extra behavior
type Decorator func(Request) Request

// DefaultProcessor is a default processor instance
var DefaultProcessor = &Processor{}

// String provides string representation of given agent status
func (a *AgentStatus) String() string {
	return fmt.Sprintf("<Agent name=%s url=%s catalog=%s protocol=%s backend=%s tool=%s toolOpts=%s agents=%v addrs=%v metrics(%v)>", a.Name, a.Url, a.Catalog, a.Protocol, a.Backend, a.Tool, a.ToolOpts, a.Agents, a.Addrs, a.Metrics)
}

// Process defines execution process for a given task
func (e *Processor) Process(t *TransferRequest) error {
	return nil
}

// Process is a method of TransferRequest
func (f RequestFunc) Process(t *TransferRequest) error {
	return f(t)
}

// filleTransferRequest creates HTTP request to transfer a given file name
// https://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
func fileTransferRequest(c CatalogEntry, tr *TransferRequest) (*http.Response, error) {
	file, err := os.Open(c.Pfn)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	// Define go pipe
	pr, pw := io.Pipe()
	writer := multipart.NewWriter(pw)
	var resp *http.Response
	// we need to wait for everything to be done
	done := make(chan error)
	go func() {
		url := fmt.Sprintf("%s/upload", tr.DstUrl)
		req, err := http.NewRequest("POST", url, pr)
		if err != nil {
			done <- err
			return
		}
		req.Header.Set("Content-Type", writer.FormDataContentType())
		req.Header.Set("Pfn", c.Pfn)
		req.Header.Set("Lfn", c.Lfn)
		req.Header.Set("Bytes", fmt.Sprintf("%d", c.Bytes))
		req.Header.Set("Hash", c.Hash)
		req.Header.Set("Src", tr.SrcAlias)
		req.Header.Set("Dst", tr.DstAlias)
		client := utils.HttpClient()
		resp, err = client.Do(req)
		if err != nil {
			done <- err
			return
		}
		if resp.StatusCode != 200 {
			done <- errors.New("Status Code is not 200")
			return
		}
		done <- nil
	}()
	part, err := writer.CreateFormFile("data", filepath.Base(c.Pfn))
	if err != nil {
		return nil, err
	}
	// Use copy of writer to avoid deadlock condition
	out := io.MultiWriter(part)
	_, err = io.Copy(out, file)
	if err != nil {
		return nil, err
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}
	err = pw.Close()
	if err != nil {
		return nil, err
	}
	err = <-done
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// helper function to perform transfer via HTTP protocol
func httpTransfer(c CatalogEntry, t *TransferRequest) (string, error) {
	// create file transfer request
	resp, err := fileTransferRequest(c, t)
	if err != nil || resp==nil || resp.StatusCode != 200 {
		return "", err
	}
        defer resp.Body.Close()
	var r CatalogEntry
	err = json.NewDecoder(resp.Body).Decode(&r)
	if err != nil {
		return "", err
	}
	return r.Pfn, nil
}

// Store returns a Decorator that stores request
func Store() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			t.Id = time.Now().Unix()
			item := &Item{
				Value:    *t,
				priority: t.Priority,
			}
			fmt.Println(*t)
			err := TFC.InsertRequest(*t)
			if err != nil {
				return err
			} else {
				heap.Push(&RequestQueue, item)
			}
			log.WithFields(log.Fields{
				"Request": t,
			}).Println("Request Saved")
			return r.Process(t)
		})
	}
}

// Delete returns a Decorator that deletes request from heap
func Delete() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			// Delete request from PriorityQueue. The complexity is O(n) where n = heap.Len()
			index := -1
			var err error

			for _, item := range RequestQueue {
				if item.Value.Id == t.Id {
					index = item.index
					break
				}
			}

			if index < RequestQueue.Len() && index >= 0 {
				err = TFC.UpdateRequest(t.Id, "deleted")
				if err != nil {
					t.Status = "error"
					return err
				} else {
					// TODO: May be we need to add lock over here.
					heap.Remove(&RequestQueue, index)
					t.Status = "deleted"
					log.WithFields(log.Fields{
						"Request": t,
					}).Println("Request Deleted")
				}
			} else {
				t.Status = "error"
				err = errors.New("Can't find request in heap")
				return err
			}

			return r.Process(t)
		})
	}
}

// Transfer returns a Decorator that performs request transfers by pull model
func PullTransfer() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			log.WithFields(log.Fields{
				"Request": t.String(),
			}).Println("Request Transfer")
			// obtain information about source and destination agents
			url := fmt.Sprintf("%s/status", t.DstUrl)
			resp := utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var dstAgent AgentStatus
			err := json.Unmarshal(resp.Data, &dstAgent)
			if err != nil {
				return err
			}
			url = fmt.Sprintf("%s/status", t.SrcUrl)
			resp = utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var srcAgent AgentStatus
			err = json.Unmarshal(resp.Data, &srcAgent)
			if err != nil {
				return err
			}
			// if both are up then send acknowledge message to destination on /pullack url.
			body, err := json.Marshal(t)
			if err != nil {
				return err
			}
			url = fmt.Sprintf("%s/pull", t.DstUrl)
			resp = utils.FetchResponse(url, body)
			// check return status code
			if resp.StatusCode != 200 {
				return fmt.Errorf("Response %s, error=%s", resp.Status, string(resp.Data))
			}
			return r.Process(t)
		})
	}
}

// Transfer returns a Decorator that performs request transfers
func PushTransfer() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			log.WithFields(log.Fields{
				"Request": t.String(),
			}).Println("Request Transfer")
			var records []CatalogEntry
			// Consider those requests which are failed in previous iteration.
			// If it is nil then request must be passing through first iteration.
			if t.FailedRecords != nil {
				records = t.FailedRecords
			} else {
				records = TFC.Records(*t)
			}
			if len(records) == 0 {
				// file does not exists in TFC, nothing to do, return immediately
				log.WithFields(log.Fields{
					"TransferRequest": t,
				}).Warn("Does not match anything in TFC of this agent\n", t)
				return r.Process(t)
			}
			// obtain information about source and destination agents
			url := fmt.Sprintf("%s/status", t.DstUrl)
			resp := utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var dstAgent AgentStatus
			err := json.Unmarshal(resp.Data, &dstAgent)
			if err != nil {
				return err
			}
			url = fmt.Sprintf("%s/status", t.SrcUrl)
			resp = utils.FetchResponse(url, []byte{})
			if resp.Error != nil {
				return resp.Error
			}
			var srcAgent AgentStatus
			err = json.Unmarshal(resp.Data, &srcAgent)
			if err != nil {
				return err
			}

			// TODO: I need to implement bulk transfer for all files in found records
			// so far I loop over them individually and transfer one by one
			var trRecords []CatalogEntry // list of successfully transferred records
			var failedRecords []CatalogEntry
			// Overwrite the previous error status
			t.Status = ""
			for _, rec := range records {

				time0 := time.Now().Unix()

				AgentMetrics.Bytes.Inc(rec.Bytes)

				// if protocol is not given use default one: HTTP
				var rpfn string // remote PFN
				if srcAgent.Protocol == "" || srcAgent.Protocol == "http" {
					log.WithFields(log.Fields{
						"dstAgent": dstAgent.String(),
					}).Println("Transfer via HTTP protocol to", dstAgent.String())
					rpfn, err = httpTransfer(rec, t)
					if err != nil {
						log.WithFields(log.Fields{
							"TransferRequest": t.String(),
							"Record":          rec.String(),
							"Err":             err,
						}).Error("Transfer", rec.String(), t.String(), err)
						t.Status = err.Error()
						failedRecords = append(failedRecords, rec)
						continue // if we fail on single record we continue with others
					}
				} else {
					// construct remote PFN by using destination agent backend and record LFN
					rpfn = fmt.Sprintf("%s%s", dstAgent.Backend, rec.Lfn)
					// perform transfer with the help of backend tool
					var cmd *exec.Cmd
					if srcAgent.ToolOpts == "" {
						cmd = exec.Command(srcAgent.Tool, rec.Pfn, rpfn)
					} else {
						cmd = exec.Command(srcAgent.Tool, srcAgent.ToolOpts, rec.Pfn, rpfn)
					}
					log.WithFields(log.Fields{
						"Command": cmd,
					}).Println("Transfer command")
					err = cmd.Run()
					if err != nil {
						log.WithFields(log.Fields{
							"Tool":         srcAgent.Tool,
							"Tool options": srcAgent.ToolOpts,
							"PFN":          rec.Pfn,
							"Remote PFN":   rpfn,
							"Err":          err,
						}).Error("Transfer")
						t.Status = err.Error()
						failedRecords = append(failedRecords, rec)
						continue // if we fail on single record we continue with others
					}
				}
				r := CatalogEntry{Dataset: rec.Dataset, Block: rec.Block, Lfn: rec.Lfn, Pfn: rpfn, Bytes: rec.Bytes, Hash: rec.Hash, TransferTime: (time.Now().Unix() - time0), Timestamp: time.Now().Unix()}
				trRecords = append(trRecords, r)

				// record how much we transferred
				AgentMetrics.TotalBytes.Inc(r.Bytes) // keep growing
				AgentMetrics.Total.Inc(1)            // keep growing
				AgentMetrics.Bytes.Dec(rec.Bytes)    // decrement since we're done

			}
			// Add entry for remote TFC after transfer is completed
			url = fmt.Sprintf("%s/tfc", t.DstUrl)
			d, e := json.Marshal(trRecords)
			if e != nil {
				return e
			}
			resp = utils.FetchResponse(url, d) // POST request
			if resp.Error != nil {
				return resp.Error
			}
			t.FailedRecords = failedRecords
			return r.Process(t)
		})
	}
}

// Logging returns a Decorator that logs client requests
func Logging(l *log.Logger) Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			l.Println("TransferRequest", t)
			return r.Process(t)
		})
	}
}

// Pause returns a Decorator that pauses request for a given time interval
func Pause(interval time.Duration) Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			if interval > 0 {
				log.WithFields(log.Fields{
					"Request":  t,
					"Interval": interval,
				}).Println("TransferRequest is paused by")
				time.Sleep(interval)
			}
			return r.Process(t)
		})
	}
}

// Tracer returns a Decorator that traces given request
func Tracer() Decorator {
	return func(r Request) Request {
		return RequestFunc(func(t *TransferRequest) error {
			log.WithFields(log.Fields{
				"TransferRequest": t,
			}).Println("Trace")
			return r.Process(t)
		})
	}
}

// Decorate decorates a Request r with all given Decorators
func Decorate(r Request, ds ...Decorator) Request {
	decorated := r
	for _, decorate := range ds {
		decorated = decorate(decorated)
	}
	return decorated
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"time"

	"github.com/pkg/errors"

	"github.com/eure/kamimai/internal/cast"
	"github.com/eure/kamimai/internal/direction"
	"github.com/eure/kamimai/internal/version"
)

const (
	notFoundIndex = 0xffff
)

var (
	errOutOfBoundsMigrations     = errors.New("out of bounds migration")
	errDuplicateMigrationVersion = errors.New("duplicate migration version")
)

type (
	// A Service manages for kamimai.
	Service struct {
		config    *Config
		driver    Driver
		version   uint64
		direction int
		data      Migrations
	}
)

func (s Service) walker(indexPath map[uint64]*Migration) func(string, os.FileInfo, error) error {
	wd, _ := os.Getwd()

	return func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		name := info.Name()
		if s.direction != direction.Get(name) {
			return nil
		}

		fullname := filepath.Clean(filepath.Join(wd, path))
		ver := cast.Uint64(version.Get(name))
		mig, found := indexPath[ver]
		if found && mig.IsValid() {
			return errors.Wrap(errDuplicateMigrationVersion, fmt.Sprintf("failed to read migration %s", fullname))
		}

		mig = &Migration{
			version: ver,
		}
		indexPath[ver] = mig
		mig.name = fullname

		return nil
	}
}

// NewService returns a new Service pointer that can be chained with builder methods to
// set multiple configuration values inline without using pointers.
func NewService(c *Config) *Service {
	svc := &Service{
		config: c,
	}
	return svc
}

// WithVersion sets a config version value returning a Service pointer
// for chaining.
func (s *Service) WithVersion(v interface{}) *Service {
	s.version = cast.Uint64(v)
	return s
}

// WithDriver sets a driver returning a Service pointer for chaining.
func (s *Service) WithDriver(d Driver) *Service {
	s.driver = d
	return s
}

// MakeMigrationsDir creates a directory named path, along with any necessary
// parents, and returns nil, or else returns an error. If path is
// already a directory, MkdirAll does nothing and returns nil.
func (s *Service) MakeMigrationsDir() error {
	return os.MkdirAll(s.config.migrationsDir(), 0777)
}

func (s *Service) mustApply() {
	if err := s.apply(); err != nil {
		panic(err)
	}
}

func (s *Service) apply() error {
	index := map[uint64]*Migration{}
	if err := filepath.Walk(s.config.migrationsDir(), s.walker(index)); err != nil {
		return err
	}

	list := make([]*Migration, len(index))
	i := 0
	for _, m := range index {
		list[i] = m
		i++
	}

	migs := Migrations(list)
	sort.Sort(migs)
	s.data = migs
	return nil
}

func (s *Service) do(idx int) error {
	drv := s.driver

	migs := s.data
	if !(0 <= idx && idx < migs.Len()) {
		return errOutOfBoundsMigrations
	}

	mig := migs[idx]
	if err := drv.Migrate(mig); err != nil {
		return err
	}

	switch s.direction {
	case direction.Up:
		if err := drv.Version().Insert(mig.version); err != nil {
			return err
		}
	case direction.Down:
		if err := drv.Version().Delete(mig.version); err != nil {
			return err
		}
	}

	log.Println("applied", mig.Name())

	return nil
}

func (s *Service) step(n int) error {
	if err := s.apply(); err != nil {
		return err
	}

	if s.version == 0 {
		// init
		if n > 0 {
			// only up
			for i := 0; i < n; i++ {
				if err := s.do(i); err != nil {
					return err
				}
			}
		}
		return nil
	}

	// gets current index of migrations
	idx := s.data.index(Migration{version: s.version})

	if n > 0 {
		// up
		for i := 0; i < n; i++ {
			if err := s.do(idx + i + 1); err != nil {
				return err
			}
		}
	} else {
		// down
		for i := 0; i < -n; i++ {
			if err := s.do(idx - i); err != nil {
				return err
			}
		}
	}

	return nil
}

// Apply applies the given migration version.
func (s *Service) Apply(d int, version uint64) error {
	s.direction = d
	if err := s.apply(); err != nil {
		return err
	}

	// gets current index of migrations
	idx := s.data.index(Migration{version: version})
	return s.do(idx)
}

func (s *Service) up(n int) error {
	s.direction = direction.Up
	err := s.step(n)
	switch err {
	case errOutOfBoundsMigrations:
		return nil
	}
	return err
}

func (s *Service) down(n int) error {
	s.direction = direction.Down
	err := s.step(-n)
	switch err {
	case errOutOfBoundsMigrations:
		return nil
	}
	return err
}

// Up upgrades migration version.
func (s *Service) Up() error {
	return s.up(notFoundIndex)
}

// Down downgrades migration version.
func (s *Service) Down() error {
	return s.down(notFoundIndex)
}

// Next upgrades migration version.
func (s *Service) Next(n int) error {
	return s.up(n)
}

// Prev downgrades migration version.
func (s *Service) Prev(n int) error {
	return s.down(n)
}

// Sync applies all migrations.
func (s *Service) Sync() error {
	s.direction = direction.Up
	if err := s.apply(); err != nil {
		return err
	}

	version := s.driver.Version()

	for _, mig := range s.data {
		if count := version.Count(mig.version); count == 0 {
			// gets current index of migrations
			idx := s.data.index(*mig)
			if err := s.do(idx); err != nil {
				return err
			}
		}
	}

	return nil
}

// NextMigration returns next version migrations.
func (s *Service) NextMigration(name string) (up *Migration, down *Migration, err error) {
	if err := s.apply(); err != nil {
		return nil, nil, err
	}

	// initialize default variables for making migrations.
	up, down = &Migration{version: 1, name: ""}, &Migration{version: 1, name: ""}
	verFormat := "%03d"

	// gets the oldest migration version file.
	if obj := s.data.last(); obj != nil {
		// for version number
		v := obj.version + 1
		up.version, down.version = v, v
	}
	if obj := s.data.first(); obj != nil {
		// for version format
		_, file := filepath.Split(obj.name)
		verFormat = version.Format(file)

		if _, err := time.Parse("20060102150405", version.Get(file)); err == nil {
			v := cast.Uint64(time.Now())
			up.version, down.version = v, v
		}

	}

	// [ver]_[name]_[direction-suffix][.ext]
	base := fmt.Sprintf("%s_%s_%%s%%s", verFormat, name)
	// including dot
	ext := s.driver.Ext()

	// up
	n := fmt.Sprintf(base, up.version, direction.Suffix(direction.Up), ext)
	up.name = filepath.Join(s.config.migrationsDir(), n)
	// down
	n = fmt.Sprintf(base, down.version, direction.Suffix(direction.Down), ext)
	down.name = filepath.Join(s.config.migrationsDir(), n)

	return
}
<commit_msg>fix: auto set file prefix<commit_after>package core

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"time"

	"github.com/pkg/errors"

	"github.com/eure/kamimai/internal/cast"
	"github.com/eure/kamimai/internal/direction"
	"github.com/eure/kamimai/internal/version"
)

const (
	notFoundIndex = 0xffff
)

var (
	errOutOfBoundsMigrations     = errors.New("out of bounds migration")
	errDuplicateMigrationVersion = errors.New("duplicate migration version")
)

type (
	// A Service manages for kamimai.
	Service struct {
		config    *Config
		driver    Driver
		version   uint64
		direction int
		data      Migrations
	}
)

func (s Service) walker(indexPath map[uint64]*Migration) func(string, os.FileInfo, error) error {
	wd, _ := os.Getwd()

	return func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		name := info.Name()
		if s.direction != direction.Get(name) {
			return nil
		}

		fullname := filepath.Clean(filepath.Join(wd, path))
		ver := cast.Uint64(version.Get(name))
		mig, found := indexPath[ver]
		if found && mig.IsValid() {
			return errors.Wrap(errDuplicateMigrationVersion, fmt.Sprintf("failed to read migration %s", fullname))
		}

		mig = &Migration{
			version: ver,
		}
		indexPath[ver] = mig
		mig.name = fullname

		return nil
	}
}

// NewService returns a new Service pointer that can be chained with builder methods to
// set multiple configuration values inline without using pointers.
func NewService(c *Config) *Service {
	svc := &Service{
		config: c,
	}
	return svc
}

// WithVersion sets a config version value returning a Service pointer
// for chaining.
func (s *Service) WithVersion(v interface{}) *Service {
	s.version = cast.Uint64(v)
	return s
}

// WithDriver sets a driver returning a Service pointer for chaining.
func (s *Service) WithDriver(d Driver) *Service {
	s.driver = d
	return s
}

// MakeMigrationsDir creates a directory named path, along with any necessary
// parents, and returns nil, or else returns an error. If path is
// already a directory, MkdirAll does nothing and returns nil.
func (s *Service) MakeMigrationsDir() error {
	return os.MkdirAll(s.config.migrationsDir(), 0777)
}

func (s *Service) mustApply() {
	if err := s.apply(); err != nil {
		panic(err)
	}
}

func (s *Service) apply() error {
	index := map[uint64]*Migration{}
	if err := filepath.Walk(s.config.migrationsDir(), s.walker(index)); err != nil {
		return err
	}

	list := make([]*Migration, len(index))
	i := 0
	for _, m := range index {
		list[i] = m
		i++
	}

	migs := Migrations(list)
	sort.Sort(migs)
	s.data = migs
	return nil
}

func (s *Service) do(idx int) error {
	drv := s.driver

	migs := s.data
	if !(0 <= idx && idx < migs.Len()) {
		return errOutOfBoundsMigrations
	}

	mig := migs[idx]
	if err := drv.Migrate(mig); err != nil {
		return err
	}

	switch s.direction {
	case direction.Up:
		if err := drv.Version().Insert(mig.version); err != nil {
			return err
		}
	case direction.Down:
		if err := drv.Version().Delete(mig.version); err != nil {
			return err
		}
	}

	log.Println("applied", mig.Name())

	return nil
}

func (s *Service) step(n int) error {
	if err := s.apply(); err != nil {
		return err
	}

	if s.version == 0 {
		// init
		if n > 0 {
			// only up
			for i := 0; i < n; i++ {
				if err := s.do(i); err != nil {
					return err
				}
			}
		}
		return nil
	}

	// gets current index of migrations
	idx := s.data.index(Migration{version: s.version})

	if n > 0 {
		// up
		for i := 0; i < n; i++ {
			if err := s.do(idx + i + 1); err != nil {
				return err
			}
		}
	} else {
		// down
		for i := 0; i < -n; i++ {
			if err := s.do(idx - i); err != nil {
				return err
			}
		}
	}

	return nil
}

// Apply applies the given migration version.
func (s *Service) Apply(d int, version uint64) error {
	s.direction = d
	if err := s.apply(); err != nil {
		return err
	}

	// gets current index of migrations
	idx := s.data.index(Migration{version: version})
	return s.do(idx)
}

func (s *Service) up(n int) error {
	s.direction = direction.Up
	err := s.step(n)
	switch err {
	case errOutOfBoundsMigrations:
		return nil
	}
	return err
}

func (s *Service) down(n int) error {
	s.direction = direction.Down
	err := s.step(-n)
	switch err {
	case errOutOfBoundsMigrations:
		return nil
	}
	return err
}

// Up upgrades migration version.
func (s *Service) Up() error {
	return s.up(notFoundIndex)
}

// Down downgrades migration version.
func (s *Service) Down() error {
	return s.down(notFoundIndex)
}

// Next upgrades migration version.
func (s *Service) Next(n int) error {
	return s.up(n)
}

// Prev downgrades migration version.
func (s *Service) Prev(n int) error {
	return s.down(n)
}

// Sync applies all migrations.
func (s *Service) Sync() error {
	s.direction = direction.Up
	if err := s.apply(); err != nil {
		return err
	}

	version := s.driver.Version()

	for _, mig := range s.data {
		if count := version.Count(mig.version); count == 0 {
			// gets current index of migrations
			idx := s.data.index(*mig)
			if err := s.do(idx); err != nil {
				return err
			}
		}
	}

	return nil
}

// NextMigration returns next version migrations.
func (s *Service) NextMigration(name string) (up *Migration, down *Migration, err error) {
	if err := s.apply(); err != nil {
		return nil, nil, err
	}

	// initialize default variables for making migrations.
	up, down = &Migration{version: 1, name: ""}, &Migration{version: 1, name: ""}
	verFormat := "%03d"

	// check if the format of the latest version is timestamp
	isDateFormat := IsTimeStamp(s.version)

	if isDateFormat {
		// for version format
		v := cast.Uint64(time.Now())
		up.version, down.version = v, v
	} else {
		// for version number
		v := s.version + 1
		up.version, down.version = v, v
	}

	// [ver]_[name]_[direction-suffix][.ext]
	base := fmt.Sprintf("%s_%s_%%s%%s", verFormat, name)
	// including dot
	ext := s.driver.Ext()

	// up
	n := fmt.Sprintf(base, up.version, direction.Suffix(direction.Up), ext)
	up.name = filepath.Join(s.config.migrationsDir(), n)
	// down
	n = fmt.Sprintf(base, down.version, direction.Suffix(direction.Down), ext)
	down.name = filepath.Join(s.config.migrationsDir(), n)

	return
}

// IsTimeStamp return if prefix is timestamp or not
func IsTimeStamp(prefix uint64) (result bool) {
	date := strconv.FormatUint(prefix, 10)
	layout := "20060102150405"
	t, _ := time.Parse(layout, date)
	std, _ := time.Parse(layout, layout)
	result = t.After(std)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"

	"github.com/cxmate/cxmate/proto"
)

// GeneratorConfig contains a description of each network that cxMate will generate for the service.
type GeneratorConfig []NetworkDescription

// validate performs validation on the GeneratorConfig.
func (c GeneratorConfig) validate() error {
	used := map[string]bool{}
	if len(c) == 0 {
		return errors.New("must have at least one output network")
	}
	for i, n := range c {
		if n.Label == "" {
			return fmt.Errorf("invalid input output position: %d error: label missing", i)
		}
		if _, exists := used[n.Label]; exists {
			return fmt.Errorf("invalid config: output position: %d error: duplicate label found: %s", i, n.Label)
		}
		used[n.Label] = true
		if n.Type == "" {
			if len(n.Aspects) == 0 {
				return fmt.Errorf("invalid config: output position: %d networks: %s error: aspect list must not be empty", i, n.Label)
			}
		}
		logDebugln("Config loaded: output position:", i, "network:", n.Label, "required apsects:", n.Aspects)
	}
	return nil
}

// Generator generates CX networks from protocol buffer messages.
type Generator struct {
	w        io.Writer
	elements *elements
	brackets brackets
}

// run initializes and runs a CX generator.
func (c GeneratorConfig) generate(w io.Writer, s <-chan *Message, singleton bool) error {
	logDebugln("Generator initializing")
	stream, err := newElementStream(s)
	if err != nil {
		return err
	}
	g := &Generator{
		w:        w,
		elements: stream,
		brackets: newBracketStack(),
	}
	defer g.closeRemainingBrackets()
	if singleton {
		if c[0].Type == "json" {
			err = g.rawJSON(c[0].Label)
		} else {
			err = g.network(c[0].Label, c[0].Aspects)
		}
	} else {
		err = g.stream(c)
	}
	if err != nil {
		return err
	}
	return nil
}

// stream generates a list of networks from a list of NetworkDescriptions.
func (g *Generator) stream(networks []NetworkDescription) error {
	logDebugln("Generating a stream of networks")
	if err := g.openBrackets("["); err != nil {
		return err
	}
	for i, n := range networks {
		if i == 1 {
			if err := g.rune(','); err != nil {
				return err
			}
		}
		if err := g.network(n.Label, n.Aspects); err != nil {
			return fmt.Errorf("error while generating %s at position %d: %v", n.Label, i, err)
		}
	}
	return g.closeBrackets("]")
}

// network generates a single network.
func (g *Generator) network(network string, aspects []string) error {
	logDebugln("Generating", network, "with aspects", aspects)
	if err := g.openBrackets("["); err != nil {
		return err
	}
	if err := g.numberVerification(network); err != nil {
		return err
	}
	if err := g.rune(','); err != nil {
		return err
	}

	if err := g.preMetadata(network, aspects); err != nil {
		return err
	}
	for g.elements.hasNext() {
		if elementNetwork, _ := g.elements.peekNetwork(); elementNetwork != network {
			break
		}
		elementAspect, _ := g.elements.peekAspect()
		hasAspect := false
		for _, aspect := range aspects {
			if aspect == elementAspect {
				hasAspect = true
			}
		}
		if hasAspect {
			if err := g.rune(','); err != nil {
				return err
			}
			if err := g.aspect(network, elementAspect); err != nil {
				return err
			}
		} else {
			if _, err := g.elements.pop(); err != nil {
				return err
			}
		}

	}
	return g.closeBrackets("]")
}

// numberVerfication generates a numberVerification aspect.
func (g *Generator) numberVerification(network string) error {
	logDebugln("Generating number verification in", network)
	nv := map[string][]map[string]int64{
		"numberVerification": []map[string]int64{
			map[string]int64{"longNumber": 281474976710655},
		},
	}
	return g.value(nv)
}

// preMetadata generates the metadata aspect that goes before any user defined aspects. preMetadata takes a list of aspect names, which is uses to populate the name field for each aspect metadata element (the only required field in preMetadata).
func (g *Generator) preMetadata(network string, aspects []string) error {
	logDebugln("Generating preMetadata in", network)
	md := []Metadata{}
	for _, a := range aspects {
		md = append(md, Metadata{
			Name: a,
		})
	}
	pm := map[string][]Metadata{
		"metaData": md,
	}
	g.value(pm)
}

// aspect generates a single aspect by reading elements until the elements aspect or network no longer match the provided parameters.
func (g *Generator) aspect(network string, aspect string) error {
	logDebugln("Generating", aspect, "aspect in", network)
	if err := g.openBrackets("{"); err != nil {
		return err
	}
	if err := g.string("\"" + aspect + "\":"); err != nil {
		return err
	}
	if err := g.openBrackets("["); err != nil {
		return err
	}
	for i := 0; g.elements.hasNext(); i++ {
		elementAspect, _ := g.elements.peekAspect()
		if elementAspect != aspect {
			break
		}
		elementNetwork, _ := g.elements.peekNetwork()
		if elementNetwork != network {
			break
		}
		element, err := g.elements.pop()
		if err != nil {
			return err
		}
		if i != 0 {
			if err := g.rune(','); err != nil {
				return err
			}
		}
		proto.NetworkElementToJSON(g.w, element)
	}
	return g.closeBrackets("]}")
}

func (g *Generator) rawJSON(label string) error {
	logDebugln("Generating", label, "as raw JSON")
	element, err := g.elements.pop()
	if err != nil {
		return fmt.Errorf("Could not accept raw JSON for %s, error: %v", label, err)
	}
	proto.NetworkElementToJSON(g.w, element)
	return nil
}

// value generates a JSON encoded value.
func (g *Generator) value(v interface{}) error {
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}
	if _, err = g.w.Write(b); err != nil {
		return err
	}
	return nil
}

// rune generates a string.
func (g *Generator) string(s string) error {
	if _, err := g.w.Write([]byte(s)); err != nil {
		return err
	}
	return nil
}

// rune generates a single rune.
func (g *Generator) rune(r rune) error {
	if _, err := g.w.Write([]byte(string(r))); err != nil {
		return err
	}
	return nil
}

// openBrackets pushes each bracket in the bracket string onto the bracket stack and then generates the bracket.
func (g *Generator) openBrackets(brackets string) error {
	for _, b := range brackets {
		if err := g.brackets.pushOpen(b); err != nil {
			return err
		}
		if err := g.rune(b); err != nil {
			return err
		}
	}
	return nil
}

// closeBrackets pops a bracket from the bracket stack for each bracket in the bracket string. If the popped bracket and the bracket from the bracket string match, the bracket is emitted, otherwise an error is returned.
func (g *Generator) closeBrackets(brackets string) error {
	for _, b := range brackets {
		if m, ok := g.brackets.popClose(); !ok {
			return fmt.Errorf("generator: expected closing bracket %q found empty bracket stack", b)
		} else if m != b {
			return fmt.Errorf("generator: expected closing bracket %q found %q", m, b)
		}
		if err := g.rune(b); err != nil {
			return err
		}
	}
	return nil
}

// closeRemainingBrackets pops brackets off the bracket stack, and emits them until the bracket stack becomes empty.
func (g *Generator) closeRemainingBrackets() error {
	for {
		m, ok := g.brackets.popClose()
		if !ok {
			break
		}
		if err := g.rune(m); err != nil {
			return err
		}
	}
	return nil
}

// brackets is a stack that pushes the matching bracket of any brackets that are pushed onto it via its pushOpen method. Popping off the matched brackets will yielding the closing brackets in the correct order to close the opened brackets.
type brackets []rune

// newBracketStack creates an empty bracket stack.
func newBracketStack() brackets {
	return make(brackets, 0)
}

// pushOpen validates that b is a bracket ('[' or '{') before pushing the matching bracket onto the stack.
func (s *brackets) pushOpen(b rune) error {
	if b != '[' && b != '{' {
		return fmt.Errorf("brackets: %q is not a recognized opening bracket", b)
	}
	m, err := s.getMatch(b)
	if err != nil {
		return fmt.Errorf("brackets: error pushing bracket: %v", err)
	}
	*s = append(*s, m)
	return nil
}

// popClose pops off a bracket frop the top of the stack and returns it. An extra bool is returned indicating if the pop succeeded (true) or the stack was empty (false).
func (s *brackets) popClose() (rune, bool) {
	if len(*s) == 0 {
		return ' ', false
	}
	b := (*s)[len(*s)-1]
	*s = (*s)[:len(*s)-1]
	return b, true
}

// getMatch is a utility to get the closing bracket for a corrosponding opening bracket.
func (s *brackets) getMatch(b rune) (rune, error) {
	switch b {
	case '[':
		return ']', nil
	case '{':
		return '}', nil
	default:
		return ' ', fmt.Errorf("brackets: could not match bracket %q", b)
	}
}

// elements provides queue based access to a stream of protobuf messages. The queue prefetchesan element before it is popped so that it becomes possible to peek at its aspect and network identifers before consuming the element. If an error occurs when elements tries to fetch the next element, the error will be returned by the constructor or by pop, and it will no longer be possible to fetch elements.
type elements struct {
	stream  <-chan *Message
	curr    *proto.NetworkElement
	err     error
	aspect  string
	network string
}

// newElementStream creates a new stream that will read from the provided channel, and blocks until the first element can be received from the channel. An error fetching the first element will yield a nil stream and an error return value. Note that the element stream will never close the channel.
func newElementStream(s <-chan *Message) (*elements, error) {
	e := &elements{stream: s}
	e.next()
	if e.err != nil {
		return nil, e.err
	}
	return e, nil
}

// pop returns an element from the stream or an error. If an error occurs, the stream should be closed as no values can be read from it in the future. If an element is available, pop blocks until the next element can be fetched before returning the current element.
func (e *elements) pop() (*proto.NetworkElement, error) {
	if e.err != nil {
		return nil, e.err
	}
	c := e.curr
	e.next()
	return c, nil
}

// hasNext returns true if an element can be read from the stream. If hasNext returns false then then elements can no longer be read from.
func (e *elements) hasNext() bool {
	if e.err != nil {
		return false
	}
	return true
}

// peekAspect returns the aspect identifier of the next element or an empty string if the next element could not be read. A boolean is also returned to determine if this error occured.
func (e *elements) peekAspect() (string, bool) {
	if e.err != nil {
		return "", false
	}
	return e.aspect, true
}

// peekNetwork returns the network label of the next element or an empty string if the next element could not be read. A boolean is also returned to determine if this error occured.
func (e *elements) peekNetwork() (string, bool) {
	if e.err != nil {
		return "", false
	}
	return e.network, true
}

// Next moves to the next element in the stream unless an error is recieved.
func (e *elements) next() {
	element, err := ReceiveMessage(e.stream)
	e.err = err
	if err == nil {
		e.curr = element
		e.aspect = proto.GetAspectName(element)
		e.network = element.Label
	}
}
<commit_msg>Fix minor regression from removing err == nil checks<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"

	"github.com/cxmate/cxmate/proto"
)

// GeneratorConfig contains a description of each network that cxMate will generate for the service.
type GeneratorConfig []NetworkDescription

// validate performs validation on the GeneratorConfig.
func (c GeneratorConfig) validate() error {
	used := map[string]bool{}
	if len(c) == 0 {
		return errors.New("must have at least one output network")
	}
	for i, n := range c {
		if n.Label == "" {
			return fmt.Errorf("invalid input output position: %d error: label missing", i)
		}
		if _, exists := used[n.Label]; exists {
			return fmt.Errorf("invalid config: output position: %d error: duplicate label found: %s", i, n.Label)
		}
		used[n.Label] = true
		if n.Type == "" {
			if len(n.Aspects) == 0 {
				return fmt.Errorf("invalid config: output position: %d networks: %s error: aspect list must not be empty", i, n.Label)
			}
		}
		logDebugln("Config loaded: output position:", i, "network:", n.Label, "required apsects:", n.Aspects)
	}
	return nil
}

// Generator generates CX networks from protocol buffer messages.
type Generator struct {
	w        io.Writer
	elements *elements
	brackets brackets
}

// run initializes and runs a CX generator.
func (c GeneratorConfig) generate(w io.Writer, s <-chan *Message, singleton bool) error {
	logDebugln("Generator initializing")
	stream, err := newElementStream(s)
	if err != nil {
		return err
	}
	g := &Generator{
		w:        w,
		elements: stream,
		brackets: newBracketStack(),
	}
	defer g.closeRemainingBrackets()
	if singleton {
		if c[0].Type == "json" {
			err = g.rawJSON(c[0].Label)
		} else {
			err = g.network(c[0].Label, c[0].Aspects)
		}
	} else {
		err = g.stream(c)
	}
	if err != nil {
		return err
	}
	return nil
}

// stream generates a list of networks from a list of NetworkDescriptions.
func (g *Generator) stream(networks []NetworkDescription) error {
	logDebugln("Generating a stream of networks")
	if err := g.openBrackets("["); err != nil {
		return err
	}
	for i, n := range networks {
		if i == 1 {
			if err := g.rune(','); err != nil {
				return err
			}
		}
		if err := g.network(n.Label, n.Aspects); err != nil {
			return fmt.Errorf("error while generating %s at position %d: %v", n.Label, i, err)
		}
	}
	return g.closeBrackets("]")
}

// network generates a single network.
func (g *Generator) network(network string, aspects []string) error {
	logDebugln("Generating", network, "with aspects", aspects)
	if err := g.openBrackets("["); err != nil {
		return err
	}
	if err := g.numberVerification(network); err != nil {
		return err
	}
	if err := g.rune(','); err != nil {
		return err
	}

	if err := g.preMetadata(network, aspects); err != nil {
		return err
	}
	for g.elements.hasNext() {
		if elementNetwork, _ := g.elements.peekNetwork(); elementNetwork != network {
			break
		}
		elementAspect, _ := g.elements.peekAspect()
		hasAspect := false
		for _, aspect := range aspects {
			if aspect == elementAspect {
				hasAspect = true
			}
		}
		if hasAspect {
			if err := g.rune(','); err != nil {
				return err
			}
			if err := g.aspect(network, elementAspect); err != nil {
				return err
			}
		} else {
			if _, err := g.elements.pop(); err != nil {
				return err
			}
		}

	}
	return g.closeBrackets("]")
}

// numberVerfication generates a numberVerification aspect.
func (g *Generator) numberVerification(network string) error {
	logDebugln("Generating number verification in", network)
	nv := map[string][]map[string]int64{
		"numberVerification": []map[string]int64{
			map[string]int64{"longNumber": 281474976710655},
		},
	}
	return g.value(nv)
}

// preMetadata generates the metadata aspect that goes before any user defined aspects. preMetadata takes a list of aspect names, which is uses to populate the name field for each aspect metadata element (the only required field in preMetadata).
func (g *Generator) preMetadata(network string, aspects []string) error {
	logDebugln("Generating preMetadata in", network)
	md := []Metadata{}
	for _, a := range aspects {
		md = append(md, Metadata{
			Name: a,
		})
	}
	pm := map[string][]Metadata{
		"metaData": md,
	}
	return g.value(pm)
}

// aspect generates a single aspect by reading elements until the elements aspect or network no longer match the provided parameters.
func (g *Generator) aspect(network string, aspect string) error {
	logDebugln("Generating", aspect, "aspect in", network)
	if err := g.openBrackets("{"); err != nil {
		return err
	}
	if err := g.string("\"" + aspect + "\":"); err != nil {
		return err
	}
	if err := g.openBrackets("["); err != nil {
		return err
	}
	for i := 0; g.elements.hasNext(); i++ {
		elementAspect, _ := g.elements.peekAspect()
		if elementAspect != aspect {
			break
		}
		elementNetwork, _ := g.elements.peekNetwork()
		if elementNetwork != network {
			break
		}
		element, err := g.elements.pop()
		if err != nil {
			return err
		}
		if i != 0 {
			if err := g.rune(','); err != nil {
				return err
			}
		}
		proto.NetworkElementToJSON(g.w, element)
	}
	return g.closeBrackets("]}")
}

func (g *Generator) rawJSON(label string) error {
	logDebugln("Generating", label, "as raw JSON")
	element, err := g.elements.pop()
	if err != nil {
		return fmt.Errorf("Could not accept raw JSON for %s, error: %v", label, err)
	}
	proto.NetworkElementToJSON(g.w, element)
	return nil
}

// value generates a JSON encoded value.
func (g *Generator) value(v interface{}) error {
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}
	if _, err = g.w.Write(b); err != nil {
		return err
	}
	return nil
}

// rune generates a string.
func (g *Generator) string(s string) error {
	if _, err := g.w.Write([]byte(s)); err != nil {
		return err
	}
	return nil
}

// rune generates a single rune.
func (g *Generator) rune(r rune) error {
	if _, err := g.w.Write([]byte(string(r))); err != nil {
		return err
	}
	return nil
}

// openBrackets pushes each bracket in the bracket string onto the bracket stack and then generates the bracket.
func (g *Generator) openBrackets(brackets string) error {
	for _, b := range brackets {
		if err := g.brackets.pushOpen(b); err != nil {
			return err
		}
		if err := g.rune(b); err != nil {
			return err
		}
	}
	return nil
}

// closeBrackets pops a bracket from the bracket stack for each bracket in the bracket string. If the popped bracket and the bracket from the bracket string match, the bracket is emitted, otherwise an error is returned.
func (g *Generator) closeBrackets(brackets string) error {
	for _, b := range brackets {
		if m, ok := g.brackets.popClose(); !ok {
			return fmt.Errorf("generator: expected closing bracket %q found empty bracket stack", b)
		} else if m != b {
			return fmt.Errorf("generator: expected closing bracket %q found %q", m, b)
		}
		if err := g.rune(b); err != nil {
			return err
		}
	}
	return nil
}

// closeRemainingBrackets pops brackets off the bracket stack, and emits them until the bracket stack becomes empty.
func (g *Generator) closeRemainingBrackets() error {
	for {
		m, ok := g.brackets.popClose()
		if !ok {
			break
		}
		if err := g.rune(m); err != nil {
			return err
		}
	}
	return nil
}

// brackets is a stack that pushes the matching bracket of any brackets that are pushed onto it via its pushOpen method. Popping off the matched brackets will yielding the closing brackets in the correct order to close the opened brackets.
type brackets []rune

// newBracketStack creates an empty bracket stack.
func newBracketStack() brackets {
	return make(brackets, 0)
}

// pushOpen validates that b is a bracket ('[' or '{') before pushing the matching bracket onto the stack.
func (s *brackets) pushOpen(b rune) error {
	if b != '[' && b != '{' {
		return fmt.Errorf("brackets: %q is not a recognized opening bracket", b)
	}
	m, err := s.getMatch(b)
	if err != nil {
		return fmt.Errorf("brackets: error pushing bracket: %v", err)
	}
	*s = append(*s, m)
	return nil
}

// popClose pops off a bracket frop the top of the stack and returns it. An extra bool is returned indicating if the pop succeeded (true) or the stack was empty (false).
func (s *brackets) popClose() (rune, bool) {
	if len(*s) == 0 {
		return ' ', false
	}
	b := (*s)[len(*s)-1]
	*s = (*s)[:len(*s)-1]
	return b, true
}

// getMatch is a utility to get the closing bracket for a corrosponding opening bracket.
func (s *brackets) getMatch(b rune) (rune, error) {
	switch b {
	case '[':
		return ']', nil
	case '{':
		return '}', nil
	default:
		return ' ', fmt.Errorf("brackets: could not match bracket %q", b)
	}
}

// elements provides queue based access to a stream of protobuf messages. The queue prefetchesan element before it is popped so that it becomes possible to peek at its aspect and network identifers before consuming the element. If an error occurs when elements tries to fetch the next element, the error will be returned by the constructor or by pop, and it will no longer be possible to fetch elements.
type elements struct {
	stream  <-chan *Message
	curr    *proto.NetworkElement
	err     error
	aspect  string
	network string
}

// newElementStream creates a new stream that will read from the provided channel, and blocks until the first element can be received from the channel. An error fetching the first element will yield a nil stream and an error return value. Note that the element stream will never close the channel.
func newElementStream(s <-chan *Message) (*elements, error) {
	e := &elements{stream: s}
	e.next()
	if e.err != nil {
		return nil, e.err
	}
	return e, nil
}

// pop returns an element from the stream or an error. If an error occurs, the stream should be closed as no values can be read from it in the future. If an element is available, pop blocks until the next element can be fetched before returning the current element.
func (e *elements) pop() (*proto.NetworkElement, error) {
	if e.err != nil {
		return nil, e.err
	}
	c := e.curr
	e.next()
	return c, nil
}

// hasNext returns true if an element can be read from the stream. If hasNext returns false then then elements can no longer be read from.
func (e *elements) hasNext() bool {
	if e.err != nil {
		return false
	}
	return true
}

// peekAspect returns the aspect identifier of the next element or an empty string if the next element could not be read. A boolean is also returned to determine if this error occured.
func (e *elements) peekAspect() (string, bool) {
	if e.err != nil {
		return "", false
	}
	return e.aspect, true
}

// peekNetwork returns the network label of the next element or an empty string if the next element could not be read. A boolean is also returned to determine if this error occured.
func (e *elements) peekNetwork() (string, bool) {
	if e.err != nil {
		return "", false
	}
	return e.network, true
}

// Next moves to the next element in the stream unless an error is recieved.
func (e *elements) next() {
	element, err := ReceiveMessage(e.stream)
	e.err = err
	if err == nil {
		e.curr = element
		e.aspect = proto.GetAspectName(element)
		e.network = element.Label
	}
}
<|endoftext|>


<commit_before>package core

// VERSION number of current image server
const VERSION = "1.16.1"
<commit_msg>Version 1.16.2<commit_after>package core

// VERSION number of current image server
const VERSION = "1.16.2"
<|endoftext|>


<commit_before>package core2

import (
	//"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"

	"github.com/jhunt/go-log"

	"github.com/starkandwayne/shield/db"
	"github.com/starkandwayne/shield/route"
	"github.com/starkandwayne/shield/util"
)

const APIVersion = 2

func (core *Core) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch {
	case match(req, `GET /init.js`):
		core.initJS(w, req)
		return

	case match(req, `GET /auth/([^/]+)/(redir|web|cli)`):
		re := regexp.MustCompile("/auth/([^/]+)/(redir|web|cli)")
		m := re.FindStringSubmatch(req.URL.Path)

		name := m[1]
		provider, ok := core.providers[name]
		if !ok {
			w.WriteHeader(404)
			fmt.Fprintf(w, "Unrecognized authentication provider %s", name)
			return
		}

		if m[2] == "redir" {
			via := "web"
			if cookie, err := req.Cookie("via"); err == nil {
				via = cookie.Value
			}
			log.Debugf("handling redirection for authentication provider flow; via='%s'", via)

			user := provider.HandleRedirect(req)
			if user == nil {
				fmt.Fprintf(w, "The authentication process broke down\n")
				w.WriteHeader(500)
			}

			session, err := core.db.CreateSession(&db.Session{
				UserUUID:  user.UUID,
				IP:        util.RemoteIP(req),
				UserAgent: req.UserAgent(),
			})
			if err != nil {
				log.Errorf("failed to create a session for user %s@%s: %s", user.Account, user.Backend, err)
				w.Header().Set("Location", "/")
			} else if via == "cli" {
				w.Header().Set("Location", fmt.Sprintf("/#!/cliauth:s:%s", session.UUID))
			} else {
				w.Header().Set("Location", "/")
				http.SetCookie(w, SessionCookie(session.UUID, true))
			}
			w.WriteHeader(302)

		} else {
			http.SetCookie(w, &http.Cookie{
				Name:  "via",
				Value: m[2],
				Path:  "/auth",
			})
			provider.Initiate(w, req)
		}
		return

	case match(req, `GET /v1/meta/pubkey`):
		core.v1GetPublicKey(w, req)
		return
	}

	w.WriteHeader(501)
}

func match(req *http.Request, pattern string) bool {
	matched, _ := regexp.MatchString(
		fmt.Sprintf("^%s$", pattern),
		fmt.Sprintf("%s %s", req.Method, req.URL.Path))
	return matched
}

func (core *Core) initJS(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(200)

	fmt.Fprintf(w, "// init.js\n")
	fmt.Fprintf(w, "var $global = {}\n")

	const unauthFail = "// failed to determine user authentication state...\n"
	const unauthJS = "$global.auth = {\"unauthenticated\":true};\n"

	user, err := core.db.GetUserForSession(route.SessionID(req))
	if err != nil {
		log.Errorf("init.js: failed to get user from session: %s", err)
		fmt.Fprintf(w, unauthFail)
		fmt.Fprintf(w, unauthJS)
		return
	}

	b, err := json.Marshal(core.info)
	if err != nil {
		log.Errorf("init.js: failed to marshal SHIELD core info into JSON: %s", err)
		fmt.Fprintf(w, "// failed to retrieve SHIELD core info...\n")
		fmt.Fprintf(w, "$global.shield = {};\n")
	} else {
		fmt.Fprintf(w, "$global.shield = %s;\n", string(b))
	}

	if user == nil {
		fmt.Fprintf(w, unauthJS)
		return
	}

	id, err := core.checkAuth(user)
	if err != nil {
		log.Errorf("init.js: failed to obtain tenancy info about user: %s", err)
		fmt.Fprintf(w, unauthFail)
		fmt.Fprintf(w, unauthJS)
		return
	}
	b, err = json.Marshal(id)
	if err != nil {
		log.Errorf("init.js: failed to marshal auth id data into JSON: %s", err)
		fmt.Fprintf(w, unauthFail)
		fmt.Fprintf(w, unauthJS)
	} else {
		fmt.Fprintf(w, "$global.auth = %s;\n", string(b))
	}
}

func (core *Core) v1GetPublicKey(w http.ResponseWriter, req *http.Request) {
	//	pub := core.agent.key.PublicKey()
	w.WriteHeader(200)
	//	fmt.Fprintf(w, "%s %s\n", pub.Type(), base64.StdEncoding.EncodeToString(pub.Marshal()))
	fmt.Fprintf(w, "...\n")
}
<commit_msg>Remove /init.js<commit_after>package core2

import (
	//"encoding/base64"
	"fmt"
	"net/http"
	"regexp"

	"github.com/jhunt/go-log"

	"github.com/starkandwayne/shield/db"
	"github.com/starkandwayne/shield/util"
)

const APIVersion = 2

func (core *Core) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch {
	case match(req, `GET /auth/([^/]+)/(redir|web|cli)`):
		re := regexp.MustCompile("/auth/([^/]+)/(redir|web|cli)")
		m := re.FindStringSubmatch(req.URL.Path)

		name := m[1]
		provider, ok := core.providers[name]
		if !ok {
			w.WriteHeader(404)
			fmt.Fprintf(w, "Unrecognized authentication provider %s", name)
			return
		}

		if m[2] == "redir" {
			via := "web"
			if cookie, err := req.Cookie("via"); err == nil {
				via = cookie.Value
			}
			log.Debugf("handling redirection for authentication provider flow; via='%s'", via)

			user := provider.HandleRedirect(req)
			if user == nil {
				fmt.Fprintf(w, "The authentication process broke down\n")
				w.WriteHeader(500)
			}

			session, err := core.db.CreateSession(&db.Session{
				UserUUID:  user.UUID,
				IP:        util.RemoteIP(req),
				UserAgent: req.UserAgent(),
			})
			if err != nil {
				log.Errorf("failed to create a session for user %s@%s: %s", user.Account, user.Backend, err)
				w.Header().Set("Location", "/")
			} else if via == "cli" {
				w.Header().Set("Location", fmt.Sprintf("/#!/cliauth:s:%s", session.UUID))
			} else {
				w.Header().Set("Location", "/")
				http.SetCookie(w, SessionCookie(session.UUID, true))
			}
			w.WriteHeader(302)

		} else {
			http.SetCookie(w, &http.Cookie{
				Name:  "via",
				Value: m[2],
				Path:  "/auth",
			})
			provider.Initiate(w, req)
		}
		return

	case match(req, `GET /v1/meta/pubkey`):
		core.v1GetPublicKey(w, req)
		return
	}

	w.WriteHeader(501)
}

func match(req *http.Request, pattern string) bool {
	matched, _ := regexp.MatchString(
		fmt.Sprintf("^%s$", pattern),
		fmt.Sprintf("%s %s", req.Method, req.URL.Path))
	return matched
}

func (core *Core) v1GetPublicKey(w http.ResponseWriter, req *http.Request) {
	//	pub := core.agent.key.PublicKey()
	w.WriteHeader(200)
	//	fmt.Fprintf(w, "%s %s\n", pub.Type(), base64.StdEncoding.EncodeToString(pub.Marshal()))
	fmt.Fprintf(w, "...\n")
}
<|endoftext|>


<commit_before>package broker

import (
	"github.com/TheThingsNetwork/ttn/api"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// Validate implements the api.Validator interface
func (m *DownlinkOption) Validate() error {
	if m.Identifier == "" {
		return errors.NewErrInvalidArgument("Identifier", "can not be empty")
	}
	if m.GatewayId == "" {
		return errors.NewErrInvalidArgument("GatewayId", "can not be empty")
	}
	if err := api.NotNilAndValid(m.ProtocolConfig, "ProtocolConfig"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.GatewayConfig, "GatewayConfig"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *UplinkMessage) Validate() error {
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.GatewayMetadata, "GatewayMetadata"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *DownlinkMessage) Validate() error {
	if err := api.NotEmptyAndValidId(m.DevId, "DevId"); err != nil {
		return err
	}
	if err := api.NotEmptyAndValidId(m.AppId, "AppId"); err != nil {
		return err
	}

	if err := api.NotNilAndValid(m.DownlinkOption, "DownlinkOption"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *DeduplicatedUplinkMessage) Validate() error {
	if err := api.NotEmptyAndValidId(m.AppId, "AppId"); err != nil {
		return err
	}
	if err := api.NotEmptyAndValidId(m.DevId, "DevId"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.ResponseTemplate, "ResponseTemplate"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *DeviceActivationRequest) Validate() error {
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.GatewayMetadata, "GatewayMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.ActivationMetadata, "ActivationMetadata"); err != nil {
		return err
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *DeduplicatedDeviceActivationRequest) Validate() error {
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *ActivationChallengeRequest) Validate() error {
	return nil
}

// Validate implements the api.Validator interface
func (m *ApplicationHandlerRegistration) Validate() error {
	if err := api.NotEmptyAndValidId(m.AppId, "AppId"); err != nil {
		return err
	}
	if m.HandlerId == "" {
		return errors.NewErrInvalidArgument("HandlerId", "can not be empty")
	}
	return nil
}
<commit_msg>Make validation return informative error instead of true/false<commit_after>package broker

import (
	"github.com/TheThingsNetwork/ttn/api"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// Validate implements the api.Validator interface
func (m *DownlinkOption) Validate() error {
	if m.Identifier == "" {
		return errors.NewErrInvalidArgument("Identifier", "can not be empty")
	}
	if m.GatewayId == "" {
		return errors.NewErrInvalidArgument("GatewayId", "can not be empty")
	}
	if err := api.NotNilAndValid(m.ProtocolConfig, "ProtocolConfig"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.GatewayConfig, "GatewayConfig"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *UplinkMessage) Validate() error {
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.GatewayMetadata, "GatewayMetadata"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *DownlinkMessage) Validate() error {
	if err := api.NotEmptyAndValidId(m.DevId, "DevId"); err != nil {
		return err
	}
	if err := api.NotEmptyAndValidId(m.AppId, "AppId"); err != nil {
		return err
	}

	if err := api.NotNilAndValid(m.DownlinkOption, "DownlinkOption"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *DeduplicatedUplinkMessage) Validate() error {
	if err := api.NotEmptyAndValidId(m.AppId, "AppId"); err != nil {
		return err
	}
	if err := api.NotEmptyAndValidId(m.DevId, "DevId"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	if m.ResponseTemplate != nil {
		if err := m.ResponseTemplate.Validate(); err != nil {
			return errors.NewErrInvalidArgument("ResponseTemplate", err.Error())
		}
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *DeviceActivationRequest) Validate() error {
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.GatewayMetadata, "GatewayMetadata"); err != nil {
		return err
	}
	if err := api.NotNilAndValid(m.ActivationMetadata, "ActivationMetadata"); err != nil {
		return err
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *DeduplicatedDeviceActivationRequest) Validate() error {
	if err := api.NotNilAndValid(m.ProtocolMetadata, "ProtocolMetadata"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *ActivationChallengeRequest) Validate() error {
	return nil
}

// Validate implements the api.Validator interface
func (m *ApplicationHandlerRegistration) Validate() error {
	if err := api.NotEmptyAndValidId(m.AppId, "AppId"); err != nil {
		return err
	}
	if m.HandlerId == "" {
		return errors.NewErrInvalidArgument("HandlerId", "can not be empty")
	}
	return nil
}
<|endoftext|>


<commit_before>package dnssd

import (
	"github.com/brutella/dnssd/log"
	"github.com/miekg/dns"

	"context"
	"fmt"
	"net"
	_ "time"
)

type BrowseEntry struct {
	IPs       []net.IP
	Port      int
	IfaceName string
	Name      string
	Type      string
	Domain    string
	Text      map[string]string
}

type AddFunc func(BrowseEntry)
type RmvFunc func(BrowseEntry)

func LookupType(ctx context.Context, service string, add AddFunc, rmv RmvFunc) (err error) {
	conn, err := newMDNSConn()
	if err != nil {
		return err
	}
	defer conn.close()

	return lookupType(ctx, service, conn, add, rmv)
}

func (e BrowseEntry) ServiceInstanceName() string {
	return fmt.Sprintf("%s.%s.%s.", e.Name, e.Type, e.Domain)
}

func lookupType(ctx context.Context, service string, conn MDNSConn, add AddFunc, rmv RmvFunc) (err error) {
	var cache = NewCache()

	m := new(dns.Msg)
	m.Question = []dns.Question{
		dns.Question{service, dns.TypePTR, dns.ClassINET},
	}
	// TODO include known answers which current ttl is more than half of the correct ttl (see TFC6772 7.1: Known-Answer Supression)
	// m.Answer = ...
	// m.Authoritive = false // because our answers are *believes*

	readCtx, readCancel := context.WithCancel(ctx)
	defer readCancel()

	ch := conn.Read(readCtx)

	qs := make(chan *Query)
	go func() {
		for _, iface := range multicastInterfaces() {
			q := &Query{msg: m, iface: iface}
			qs <- q
		}
	}()

	es := []*BrowseEntry{}
	for {
		select {
		case q := <-qs:
			log.Debug.Printf("Send browsing query at %s\n%s\n", q.IfaceName(), q.msg)
			if err := conn.SendQuery(q); err != nil {
				log.Debug.Println("SendQuery:", err)
			}

		case req := <-ch:
			log.Debug.Printf("Receive message at %s\n%s\n", req.IfaceName(), req.msg)
			cache.UpdateFrom(req.msg, req.iface)
			for _, srv := range cache.Services() {
				if srv.ServiceName() != service {
					continue
				}

				for ifaceName, ips := range srv.ifaceIPs {
					var found = false
					for _, e := range es {
						if e.Name == srv.Name && e.IfaceName == ifaceName {
							found = true
							break
						}
					}
					if !found {
						e := BrowseEntry{
							IPs:       ips,
							Port:      srv.Port,
							IfaceName: ifaceName,
							Name:      srv.Name,
							Type:      srv.Type,
							Domain:    srv.Domain,
							Text:      srv.Text,
						}
						es = append(es, &e)
						add(e)
					}
				}
			}

			tmp := []*BrowseEntry{}
			for _, e := range es {
				var found = false
				for _, srv := range cache.Services() {
					if srv.ServiceInstanceName() == e.ServiceInstanceName() {
						found = true
						break
					}
				}

				if found {
					tmp = append(tmp, e)
				} else {
					// TODO
					rmv(*e)
				}
			}
			es = tmp
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}
<commit_msg>Add `Host` to `BrowseEntry`.<commit_after>package dnssd

import (
	"github.com/brutella/dnssd/log"
	"github.com/miekg/dns"

	"context"
	"fmt"
	"net"
	_ "time"
)

type BrowseEntry struct {
	IPs       []net.IP
	Host      string
	Port      int
	IfaceName string
	Name      string
	Type      string
	Domain    string
	Text      map[string]string
}

type AddFunc func(BrowseEntry)
type RmvFunc func(BrowseEntry)

func LookupType(ctx context.Context, service string, add AddFunc, rmv RmvFunc) (err error) {
	conn, err := newMDNSConn()
	if err != nil {
		return err
	}
	defer conn.close()

	return lookupType(ctx, service, conn, add, rmv)
}

func (e BrowseEntry) ServiceInstanceName() string {
	return fmt.Sprintf("%s.%s.%s.", e.Name, e.Type, e.Domain)
}

func lookupType(ctx context.Context, service string, conn MDNSConn, add AddFunc, rmv RmvFunc) (err error) {
	var cache = NewCache()

	m := new(dns.Msg)
	m.Question = []dns.Question{
		dns.Question{service, dns.TypePTR, dns.ClassINET},
	}
	// TODO include known answers which current ttl is more than half of the correct ttl (see TFC6772 7.1: Known-Answer Supression)
	// m.Answer = ...
	// m.Authoritive = false // because our answers are *believes*

	readCtx, readCancel := context.WithCancel(ctx)
	defer readCancel()

	ch := conn.Read(readCtx)

	qs := make(chan *Query)
	go func() {
		for _, iface := range multicastInterfaces() {
			q := &Query{msg: m, iface: iface}
			qs <- q
		}
	}()

	es := []*BrowseEntry{}
	for {
		select {
		case q := <-qs:
			log.Debug.Printf("Send browsing query at %s\n%s\n", q.IfaceName(), q.msg)
			if err := conn.SendQuery(q); err != nil {
				log.Debug.Println("SendQuery:", err)
			}

		case req := <-ch:
			log.Debug.Printf("Receive message at %s\n%s\n", req.IfaceName(), req.msg)
			cache.UpdateFrom(req.msg, req.iface)
			for _, srv := range cache.Services() {
				if srv.ServiceName() != service {
					continue
				}

				for ifaceName, ips := range srv.ifaceIPs {
					var found = false
					for _, e := range es {
						if e.Name == srv.Name && e.IfaceName == ifaceName {
							found = true
							break
						}
					}
					if !found {
						e := BrowseEntry{
							IPs:       ips,
							Host:      srv.Host,
							Port:      srv.Port,
							IfaceName: ifaceName,
							Name:      srv.Name,
							Type:      srv.Type,
							Domain:    srv.Domain,
							Text:      srv.Text,
						}
						es = append(es, &e)
						add(e)
					}
				}
			}

			tmp := []*BrowseEntry{}
			for _, e := range es {
				var found = false
				for _, srv := range cache.Services() {
					if srv.ServiceInstanceName() == e.ServiceInstanceName() {
						found = true
						break
					}
				}

				if found {
					tmp = append(tmp, e)
				} else {
					// TODO
					rmv(*e)
				}
			}
			es = tmp
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}
<|endoftext|>