<commit_before>package query

import (
	"fmt"
	"os"
	_ "path/filepath"
	"strings"
	"time"
)

type Modifier struct {
	Name      string
	Arguments []string
}

func (m *Modifier) String() string {
	return fmt.Sprintf("%s(%s)", m.Name, strings.Join(m.Arguments, ", "))
}

type modFnParams struct {
	Key   string
	Input interface{}
	Path  string
	Info  os.FileInfo
	Args  []string
}

func format(p *modFnParams) (interface{}, error) {
	if p.Key != "size" {
		return nil,
			fmt.Errorf("Function FORMAT not implemented for attribute %s.\n",
				p.Key)
	}

	size, _ := p.Input.(int64)
	var sizeStr string

	switch p.Args[0] {
	case "kb":
		sizeStr = fmt.Sprintf("%fkb", float64(size)/(1<<10))
	case "mb":
		sizeStr = fmt.Sprintf("%fmb", float64(size)/(1<<20))
	case "gb":
		sizeStr = fmt.Sprintf("%fgb", float64(size)/(1<<30))
	default:
		sizeStr = string(size)
	}

	return sizeStr, nil
}

func upper(p *modFnParams) (interface{}, error) {
	if p.Key != "name" {
		return nil, fmt.Errorf("Function UPPER not implemented for attribute %s.\n",
			p.Key)
	}

	return strings.ToUpper(p.Input.(string)), nil
}

func fullpath(p *modFnParams) (interface{}, error) {
	if p.Key != "name" {
		return nil,
			fmt.Errorf("Function FULLPATH not implemented for attribute %s.\n",
				p.Key)
	}

	return p.Path, nil
}

func defaultValue(key, path string, info os.FileInfo) interface{} {
	switch key {
	case "mode":
		return info.Mode()
	case "size":
		return info.Size()
	case "time":
		return info.ModTime().Format(time.Stamp)
	case "name":
		return info.Name()
	}

	return nil
}

var modifierFns = map[string]func(*modFnParams) (interface{}, error){
	"format":   format,
	"upper":    upper,
	"fullpath": fullpath,
}

// ApplyModifiers iterates through each SELECT attribute for this query
// and applies the associated modifier to the attribute's value.
func (q *Query) ApplyModifiers(path string, info os.FileInfo) map[string]interface{} {
	results := make(map[string]interface{}, len(q.Attributes))

	for k, _ := range q.Attributes {
		value := defaultValue(k, path, info)

		modifiers, ok := q.Modifiers[k]
		if !ok {
			results[k] = value
			continue
		}

		for _, m := range modifiers {
			fn, ok := modifierFns[m.Name]
			if !ok {
				fmt.Fprintf(os.Stderr, "Function %s is not implemented.\n",
					strings.ToUpper(m.Name))
				os.Exit(1)
			}

			var err error
			value, err = fn(&modFnParams{k, value, path, info, m.Arguments})
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
		}

		results[k] = value
	}

	return results
}
<commit_msg>Add format function for `time`<commit_after>package query

import (
	"fmt"
	"os"
	_ "path/filepath"
	"strings"
	"time"
)

type Modifier struct {
	Name      string
	Arguments []string
}

func (m *Modifier) String() string {
	return fmt.Sprintf("%s(%s)", m.Name, strings.Join(m.Arguments, ", "))
}

type modifierParams struct {
	Key   string
	Input interface{}
	Path  string
	Info  os.FileInfo
	Args  []string
}

func formatSize(p *modifierParams) (interface{}, error) {
	size, _ := p.Input.(int64)
	var s string
	switch p.Args[0] {
	case "kb":
		s = fmt.Sprintf("%fkb", float64(size)/(1<<10))
	case "mb":
		s = fmt.Sprintf("%fmb", float64(size)/(1<<20))
	case "gb":
		s = fmt.Sprintf("%fgb", float64(size)/(1<<30))
	default:
		s = string(size)
	}
	return s, nil
}

func formatTime(p *modifierParams) (interface{}, error) {
	var t string
	switch p.Args[0] {
	case "iso":
		t = p.Info.ModTime().Format(time.RFC3339)
	case "unix":
		t = p.Info.ModTime().Format(time.UnixDate)
	default:
		t = p.Info.ModTime().Format(time.Stamp)
	}
	return t, nil
}

func format(p *modifierParams) (interface{}, error) {
	switch p.Key {
	case "size":
		return formatSize(p)
	case "time":
		return formatTime(p)
	default:
		return nil,
			fmt.Errorf("Function FORMAT not implemented for attribute %s.\n",
				p.Key)
	}
}

func upper(p *modifierParams) (interface{}, error) {
	if p.Key != "name" {
		return nil, fmt.Errorf("Function UPPER not implemented for attribute %s.\n",
			p.Key)
	}

	return strings.ToUpper(p.Input.(string)), nil
}

func fullpath(p *modifierParams) (interface{}, error) {
	if p.Key != "name" {
		return nil,
			fmt.Errorf("Function FULLPATH not implemented for attribute %s.\n",
				p.Key)
	}

	return p.Path, nil
}

func defaultValue(key, path string, info os.FileInfo) interface{} {
	switch key {
	case "mode":
		return info.Mode()
	case "size":
		return info.Size()
	case "time":
		return info.ModTime().Format(time.Stamp)
	case "name":
		return info.Name()
	}

	return nil
}

var modifierFns = map[string]func(*modifierParams) (interface{}, error){
	"format":   format,
	"upper":    upper,
	"fullpath": fullpath,
}

// ApplyModifiers iterates through each SELECT attribute for this query
// and applies the associated modifier to the attribute's value.
func (q *Query) ApplyModifiers(path string, info os.FileInfo) map[string]interface{} {
	results := make(map[string]interface{}, len(q.Attributes))

	for k, _ := range q.Attributes {
		value := defaultValue(k, path, info)

		modifiers, ok := q.Modifiers[k]
		if !ok {
			results[k] = value
			continue
		}

		for _, m := range modifiers {
			fn, ok := modifierFns[m.Name]
			if !ok {
				fmt.Fprintf(os.Stderr, "Function %s is not implemented.\n",
					strings.ToUpper(m.Name))
				os.Exit(1)
			}

			var err error
			value, err = fn(&modifierParams{k, value, path, info, m.Arguments})
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
		}

		results[k] = value
	}

	return results
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
)

var port = flag.Int("port", 8000, "port number")

func init() {
	flag.Parse()
}

func main() {
	http.Handle("/", http.FileServer(http.Dir("public")))
	fmt.Printf("http://localhost:%d/\n", *port)
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*port), nil))
}
<commit_msg>doc: Improve server<commit_after>// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
)

var port = flag.Int("port", 8000, "port number")

func init() {
	flag.Parse()
}

var rootPath = ""

func init() {
	_, path, _, _ := runtime.Caller(0)
	rootPath = filepath.Join(filepath.Dir(path), "..", "public")
}

func main() {
	http.Handle("/", http.FileServer(http.Dir(rootPath)))
	fmt.Printf("http://localhost:%d/\n", *port)
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*port), nil))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"github.com/libgit2/git2go"
)

var clone string = "./test"
var repo string = "https://gitlab.com/rollbrettler/go-playground.git"

func main() {

	cloneOptions := &git.CloneOptions{
		Bare: true,
	}

	_, err := ioutil.ReadDir(clone)
	if err != nil {
		log.Println(err)

		repository, err := git.Clone(repo, clone, cloneOptions)
		if err != nil {
			log.Println(err)
			return
		}
		config, err := repository.Config()
		if err != nil {
			log.Println(err)
			return
		}
		err = changeToMirrorConfig(*config)
		if (err != nil) {
			log.Println(err)
		}
		// err = updateRepository(*repository)
		// if (err != nil) {
		// 	log.Println(err)
		// }
		fmt.Printf("%v\n", config)
		return
	}
	log.Println("Folder already cloned")

	repository, err := git.OpenRepository(clone)
	if err != nil {
		log.Println(err)
		return
	}
	err = updateRepository(*repository)
	if (err != nil) {
		log.Println(err)
	}
}

func changeToMirrorConfig(config git.Config) (err error) {
	fetch, err := config.LookupString("remote.origin.fetch")
	if (err != nil) {
		log.Println(err)
	}
	mirror, err := config.LookupString("remote.origin.mirror")
	if (err != nil) {
		log.Println(err)
	}
	fmt.Printf("%v %v\n", fetch, mirror)
	config.SetString("remote.origin.fetch", "+refs/*:refs/*")
	config.SetBool("remote.origin.mirror", true)
	return nil
}

func updateRepository(repository git.Repository) (err error) {
	remote, err := repository.LookupRemote("origin")
	defer remote.Free()
	refspecs := []string{"+refs/*:refs/*"}
	remote.Fetch(refspecs ,nil, "")
	return nil
}
<commit_msg>Cleanup git mirror draft<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/libgit2/git2go"
)

var clone string = "./test"
var repo string = "https://gitlab.com/rollbrettler/go-playground.git"

func main() {

	_, err := ioutil.ReadDir(clone)
	if err != nil {
		log.Println("Directory not pressent")

		err := cloneRepository()
		if err != nil {
			log.Println(err)
			os.Exit(1)
		}
	}

	log.Println("Folder already cloned")

	repository, err := git.OpenRepository(clone)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	err = updateRepository(*repository)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	log.Println("Sucessfully updated")
	os.Exit(0)
}

func cloneRepository() (err error) {

	cloneOptions := &git.CloneOptions{
		Bare: true,
	}

	repository, err := git.Clone(repo, clone, cloneOptions)
	if err != nil {
		log.Println(err)
		return
	}

	config, err := repository.Config()
	if err != nil {
		log.Println(err)
		return
	}

	err = changeToMirrorConfig(*config)
	if err != nil {
		log.Println(err)
	}

	err = updateRepository(*repository)
	if err != nil {
		log.Println(err)
	}

	fmt.Printf("%v\n", &config)

	return nil
}

func changeToMirrorConfig(config git.Config) (err error) {

	fetch, err := config.LookupString("remote.origin.fetch")
	if err != nil {
		return err
	}

	mirror, err := config.LookupBool("remote.origin.mirror")
	if err != nil {
		return err
	}

	log.Println(fmt.Sprintf("Refspec: %v\n", fetch))
	config.SetString("remote.origin.fetch", "+refs/*:refs/*")

	log.Println(fmt.Sprintf("Mirror: %v\n", mirror))
	config.SetBool("remote.origin.mirror", true)

	return nil
}

func updateRepository(repository git.Repository) (err error) {

	remote, err := repository.LookupRemote("origin")
	defer remote.Free()

	refspecs := []string{"+refs/*:refs/*"}
	err = remote.Fetch(refspecs, nil, "")
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package adcom1

// AgentType identifies the user agent types a user identifier is from.
type AgentType int64

// Agent types dewscribing where the user agent is from.
//
// Values of 500+ hold vendor-specific codes.
const (
	ATypeWeb    AgentType = 1 // An ID which is tied to a specific web browser or device (cookie-based, probabilistic, or other).
	ATypeApp    AgentType = 2 // In-app impressions, which will typically contain a type of device ID (or rather, the privacy-compliant versions of device IDs).
	ATypePerson AgentType = 3 // A person-based ID, i.e., that is the same across devices.
)
<commit_msg>adcom1: fix mistype<commit_after>package adcom1

// AgentType identifies the user agent types a user identifier is from.
type AgentType int64

// Agent types describing where the user agent is from.
//
// Values of 500+ hold vendor-specific codes.
const (
	ATypeWeb    AgentType = 1 // An ID which is tied to a specific web browser or device (cookie-based, probabilistic, or other).
	ATypeApp    AgentType = 2 // In-app impressions, which will typically contain a type of device ID (or rather, the privacy-compliant versions of device IDs).
	ATypePerson AgentType = 3 // A person-based ID, i.e., that is the same across devices.
)
<|endoftext|>


<commit_before>package conekta

import (
	"fmt"
)

type chargesResource struct {
	client *Client
	path   string
}

type Charge struct {
	Description         string         `json:"description"`
	Amount              int            `json:"amount"`
	Capture							*bool					 `json:"capture,omitempty"`
	Currency            string         `json:"currency"`
	Card                string         `json:"card,omitempty"`
	MonthlyInstallments int            `json:"monthly_installments,omitempty"`
	ReferenceId         string         `json:"reference_id,omitempty"`
	Cash                CashPayment    `json:"cash",omitempty"`
	Bank                BankPayment    `json:"bank,omitempty"`
	Id                  *string        `json:"id,omitempty"`
	Livemode            *bool          `json:"livemode,omitempty"`
	CreatedAt           *timestamp     `json:"created_at,omitempty"`
	Status              *string        `json:"status,omitempty"`
	FailureCode         *string        `json:"failure_code,omitempty"`
	FailureMessage      *string        `json:"failure_message,omitempty"`
	Object              *string        `json:"object,omitempty"`
	AmountRefunded      *int           `json:"amount_refunded,omitempty"`
	Fee                 *int           `json:"fee,omitempty"`
	PaymentMethod       *PaymentMethod `json:"payment_method,omitempty"`
	Details             *Details       `json:"details,omitempty"`
	Refunds             []Refund       `json:"refunds,omitempty"`
	Customer            *Customer      `json:"customer,omitempty"`
}

type Refund struct {
	CreatedAt   *timestamp `json:"created_at,omitempty"`
	Amount      int        `json:"amount,omitempty"`
	Currency    string     `json:"currency,omitempty"`
	Transaction string     `json:"transaction,omitempty"`
}

type Shipment struct {
	Carrier    string   `json:"carrier,omitempty"`
	Service    string   `json:"service,omitempty"`
	TrackingId string   `json:"tracking_id,omitempty"`
	Price      int      `json:"price,omitempty"`
	Address    *Address `json:"address,omitempty"`
}

type LineItem struct {
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	Sku         string `json:"sku,omitempty"`
	UnitPrice   int    `json:"unit_price,omitempty"`
	Quantity    int    `json:"quantity,omitempty"`
	Type        string `json:"type,omiempty"`
}

type Details struct {
	Name           string          `json:"name,omitempty"`
	Phone          string          `json:"phone,omitempty"`
	Email          string          `json:"email,omitempty"`
	DateOfBirth    string          `json:"date_of_birth,omitempty"`
	LineItems      []LineItem      `json:"line_items,omitempty"`
	BillingAddress *BillingAddress `json:"billing_address,omitempty"`
	Shipment       *Shipment       `json:"shipment,omitempty"`
}

type PaymentMethod struct {
	Object        string     `json:"object,omitempty"`
	Type          string     `json:"type,omitempty"`
	ServiceName   string     `json:"service_name,omitempty"` //Bank
	ServiceNumber string     `json:"service_number,omitempty"`
	Reference     string     `json:"reference,omitempty"`
	ExpiryDate    string     `json:"expiry_date,omitempty"` //Oxxo
	Barcode       string     `json:"barcode,omitempty"`
	BarcodeUrl    string     `json:"barcode_url,omitempty"`
	ExpiresAt     *timestamp `json:"expires_at,omitempty"`
	Brand         string     `json:"brand,omitempty"` // CC
	AuthCode      string     `json:"auth_code,omitempty"`
	Last4         string     `json:"last4,omitempty"`
	ExpMonth      string     `json:"exp_month,omitempty"`
	ExpYear       string     `json:"exp_year,omitempty"`
	Name          string     `json:"name,omitempty"`
	Address       *Address   `json:"address,omitempty"`
}

type CashPayment map[string]string
type BankPayment map[string]string

func newChargesResource(c *Client) *chargesResource {
	return &chargesResource{
		client: c,
		path:   "charges",
	}
}

func (s *chargesResource) Create(out *Charge) (*Charge, error) {
	in := new(Charge)
	err := s.client.execute("POST", s.path, in, out)
	if err != nil {
		return nil, err
	}
	return in, nil
}

func (s *chargesResource) Get(id string) (*Charge, error) {
	path := fmt.Sprintf("%s/%s", s.path, id)
	in := new(Charge)
	err := s.client.execute("GET", path, in, nil)
	if err != nil {
		return nil, err
	}
	return in, nil
}

// If amount is  empty  a full refund will be issued
func (s *chargesResource) Refund(id string, amount Param) (*Charge, error) {
	path := fmt.Sprintf("%s/%s/refund", s.path, id)
	in := new(Charge)
	err := s.client.execute("POST", path, in, amount)
	if err != nil {
		return nil, err
	}
	return in, nil
}

func (s *chargesResource) Filter(options FilterOptions) ([]Charge, error) {
	req, err := s.client.prepareRequest("GET", s.path, nil)
	if err != nil {
		return nil, err
	}

	var r []Charge
	err = s.client.executeRequest(req, &r)
	if err != nil {
		return nil, err
	}
	return r, nil
}
<commit_msg>changing type of 'capture' param<commit_after>package conekta

import (
	"fmt"
)

type chargesResource struct {
	client *Client
	path   string
}

type Charge struct {
	Description         string         `json:"description"`
	Amount              int            `json:"amount"`
	Capture							bool					 `json:"capture,omitempty"`
	Currency            string         `json:"currency"`
	Card                string         `json:"card,omitempty"`
	MonthlyInstallments int            `json:"monthly_installments,omitempty"`
	ReferenceId         string         `json:"reference_id,omitempty"`
	Cash                CashPayment    `json:"cash",omitempty"`
	Bank                BankPayment    `json:"bank,omitempty"`
	Id                  *string        `json:"id,omitempty"`
	Livemode            *bool          `json:"livemode,omitempty"`
	CreatedAt           *timestamp     `json:"created_at,omitempty"`
	Status              *string        `json:"status,omitempty"`
	FailureCode         *string        `json:"failure_code,omitempty"`
	FailureMessage      *string        `json:"failure_message,omitempty"`
	Object              *string        `json:"object,omitempty"`
	AmountRefunded      *int           `json:"amount_refunded,omitempty"`
	Fee                 *int           `json:"fee,omitempty"`
	PaymentMethod       *PaymentMethod `json:"payment_method,omitempty"`
	Details             *Details       `json:"details,omitempty"`
	Refunds             []Refund       `json:"refunds,omitempty"`
	Customer            *Customer      `json:"customer,omitempty"`
}

type Refund struct {
	CreatedAt   *timestamp `json:"created_at,omitempty"`
	Amount      int        `json:"amount,omitempty"`
	Currency    string     `json:"currency,omitempty"`
	Transaction string     `json:"transaction,omitempty"`
}

type Shipment struct {
	Carrier    string   `json:"carrier,omitempty"`
	Service    string   `json:"service,omitempty"`
	TrackingId string   `json:"tracking_id,omitempty"`
	Price      int      `json:"price,omitempty"`
	Address    *Address `json:"address,omitempty"`
}

type LineItem struct {
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	Sku         string `json:"sku,omitempty"`
	UnitPrice   int    `json:"unit_price,omitempty"`
	Quantity    int    `json:"quantity,omitempty"`
	Type        string `json:"type,omiempty"`
}

type Details struct {
	Name           string          `json:"name,omitempty"`
	Phone          string          `json:"phone,omitempty"`
	Email          string          `json:"email,omitempty"`
	DateOfBirth    string          `json:"date_of_birth,omitempty"`
	LineItems      []LineItem      `json:"line_items,omitempty"`
	BillingAddress *BillingAddress `json:"billing_address,omitempty"`
	Shipment       *Shipment       `json:"shipment,omitempty"`
}

type PaymentMethod struct {
	Object        string     `json:"object,omitempty"`
	Type          string     `json:"type,omitempty"`
	ServiceName   string     `json:"service_name,omitempty"` //Bank
	ServiceNumber string     `json:"service_number,omitempty"`
	Reference     string     `json:"reference,omitempty"`
	ExpiryDate    string     `json:"expiry_date,omitempty"` //Oxxo
	Barcode       string     `json:"barcode,omitempty"`
	BarcodeUrl    string     `json:"barcode_url,omitempty"`
	ExpiresAt     *timestamp `json:"expires_at,omitempty"`
	Brand         string     `json:"brand,omitempty"` // CC
	AuthCode      string     `json:"auth_code,omitempty"`
	Last4         string     `json:"last4,omitempty"`
	ExpMonth      string     `json:"exp_month,omitempty"`
	ExpYear       string     `json:"exp_year,omitempty"`
	Name          string     `json:"name,omitempty"`
	Address       *Address   `json:"address,omitempty"`
}

type CashPayment map[string]string
type BankPayment map[string]string

func newChargesResource(c *Client) *chargesResource {
	return &chargesResource{
		client: c,
		path:   "charges",
	}
}

func (s *chargesResource) Create(out *Charge) (*Charge, error) {
	in := new(Charge)
	err := s.client.execute("POST", s.path, in, out)
	if err != nil {
		return nil, err
	}
	return in, nil
}

func (s *chargesResource) Get(id string) (*Charge, error) {
	path := fmt.Sprintf("%s/%s", s.path, id)
	in := new(Charge)
	err := s.client.execute("GET", path, in, nil)
	if err != nil {
		return nil, err
	}
	return in, nil
}

// If amount is  empty  a full refund will be issued
func (s *chargesResource) Refund(id string, amount Param) (*Charge, error) {
	path := fmt.Sprintf("%s/%s/refund", s.path, id)
	in := new(Charge)
	err := s.client.execute("POST", path, in, amount)
	if err != nil {
		return nil, err
	}
	return in, nil
}

func (s *chargesResource) Filter(options FilterOptions) ([]Charge, error) {
	req, err := s.client.prepareRequest("GET", s.path, nil)
	if err != nil {
		return nil, err
	}

	var r []Charge
	err = s.client.executeRequest(req, &r)
	if err != nil {
		return nil, err
	}
	return r, nil
}
<|endoftext|>


<commit_before>package gapi

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// AlertNotification represents a Grafana alert notification.
type AlertNotification struct {
	ID                    int64       `json:"id,omitempty"`
	UID                   string      `json:"uid"`
	Name                  string      `json:"name"`
	Type                  string      `json:"type"`
	IsDefault             bool        `json:"isDefault"`
	DisableResolveMessage bool        `json:"disableResolveMessage"`
	SendReminder          bool        `json:"sendReminder"`
	Frequency             string      `json:"frequency"`
	Settings              interface{} `json:"settings"`
}

// AlertNotifications fetches and returns Grafana alert notifications.
func (c *Client) AlertNotifications() ([]AlertNotification, error) {
	alertnotifications := make([]AlertNotification, 0)

	err := c.request("GET", "/api/alert-notifications/", nil, nil, &alertnotifications)
	if err != nil {
		return nil, err
	}

	return alertnotifications, err
}

// AlertNotification fetches and returns a Grafana alert notification.
func (c *Client) AlertNotification(id int64) (*AlertNotification, error) {
	path := fmt.Sprintf("/api/alert-notifications/%d", id)
	result := &AlertNotification{}
	err := c.request("GET", path, nil, nil, result)
	if err != nil {
		return nil, err
	}

	return result, err
}

// NewAlertNotification creates a new Grafana alert notification.
func (c *Client) NewAlertNotification(a *AlertNotification) (int64, error) {
	data, err := json.Marshal(a)
	if err != nil {
		return 0, err
	}
	result := struct {
		ID int64 `json:"id"`
	}{}

	err = c.request("POST", "/api/alert-notifications", nil, bytes.NewBuffer(data), &result)
	if err != nil {
		return 0, err
	}

	return result.ID, err
}

// UpdateAlertNotification updates a Grafana alert notification.
func (c *Client) UpdateAlertNotification(a *AlertNotification) error {
	path := fmt.Sprintf("/api/alert-notifications/%d", a.ID)
	data, err := json.Marshal(a)
	if err != nil {
		return err
	}
	err = c.request("PUT", path, nil, bytes.NewBuffer(data), nil)

	return err
}

// DeleteAlertNotification deletes a Grafana alert notification.
func (c *Client) DeleteAlertNotification(id int64) error {
	path := fmt.Sprintf("/api/alert-notifications/%d", id)

	return c.request("DELETE", path, nil, nil, nil)
}
<commit_msg>updated with secureSettings and secureFields for tf provider support (#21)<commit_after>package gapi

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// AlertNotification represents a Grafana alert notification.
type AlertNotification struct {
	ID                    int64       `json:"id,omitempty"`
	UID                   string      `json:"uid"`
	Name                  string      `json:"name"`
	Type                  string      `json:"type"`
	IsDefault             bool        `json:"isDefault"`
	DisableResolveMessage bool        `json:"disableResolveMessage"`
	SendReminder          bool        `json:"sendReminder"`
	Frequency             string      `json:"frequency"`
	Settings              interface{} `json:"settings"`
	SecureFields          interface{} `json:"secureFields,omitempty"`
	SecureSettings        interface{} `json:"secureSettings,omitempty"`
}

// AlertNotifications fetches and returns Grafana alert notifications.
func (c *Client) AlertNotifications() ([]AlertNotification, error) {
	alertnotifications := make([]AlertNotification, 0)

	err := c.request("GET", "/api/alert-notifications/", nil, nil, &alertnotifications)
	if err != nil {
		return nil, err
	}

	return alertnotifications, err
}

// AlertNotification fetches and returns a Grafana alert notification.
func (c *Client) AlertNotification(id int64) (*AlertNotification, error) {
	path := fmt.Sprintf("/api/alert-notifications/%d", id)
	result := &AlertNotification{}
	err := c.request("GET", path, nil, nil, result)
	if err != nil {
		return nil, err
	}

	return result, err
}

// NewAlertNotification creates a new Grafana alert notification.
func (c *Client) NewAlertNotification(a *AlertNotification) (int64, error) {
	data, err := json.Marshal(a)
	if err != nil {
		return 0, err
	}
	result := struct {
		ID int64 `json:"id"`
	}{}

	err = c.request("POST", "/api/alert-notifications", nil, bytes.NewBuffer(data), &result)
	if err != nil {
		return 0, err
	}

	return result.ID, err
}

// UpdateAlertNotification updates a Grafana alert notification.
func (c *Client) UpdateAlertNotification(a *AlertNotification) error {
	path := fmt.Sprintf("/api/alert-notifications/%d", a.ID)
	data, err := json.Marshal(a)
	if err != nil {
		return err
	}
	err = c.request("PUT", path, nil, bytes.NewBuffer(data), nil)

	return err
}

// DeleteAlertNotification deletes a Grafana alert notification.
func (c *Client) DeleteAlertNotification(id int64) error {
	path := fmt.Sprintf("/api/alert-notifications/%d", id)

	return c.request("DELETE", path, nil, nil, nil)
}
<|endoftext|>


<commit_before>package chunk

import (
	"fmt"
	"time"

	"github.com/dgryski/go-tsz"
)

var TotalPoints chan int

func init() {
	// measurements can lag a bit, that's ok
	TotalPoints = make(chan int, 1000)
}

// Chunk is a chunk of data. not concurrency safe.
type Chunk struct {
	*tsz.Series
	LastTs    uint32 // last TS seen, not computed or anything
	NumPoints uint32
	Saved     bool
	Saving    bool
	LastWrite uint32
}

func New(t0 uint32) *Chunk {
	// we must set LastWrite here as well to make sure a new Chunk doesn't get immediately
	// garbage collected right after creating it, before we can push to it
	return &Chunk{tsz.New(t0), 0, 0, false, false, uint32(time.Now().Unix())}
}

func (c *Chunk) String() string {
	return fmt.Sprintf("<chunk T0=%d, LastTs=%d, NumPoints=%d, Saved=%t>", c.T0, c.LastTs, c.NumPoints, c.Saved)

}
func (c *Chunk) Push(t uint32, v float64) error {
	if t <= c.LastTs {
		return fmt.Errorf("Point must be newer than already added points. t:%d lastTs: %d", t, c.LastTs)
	}
	c.Series.Push(t, v)
	c.NumPoints += 1
	c.LastTs = t
	c.LastWrite = uint32(time.Now().Unix())
	TotalPoints <- 1
	return nil
}
func (c *Chunk) Clear() {
	TotalPoints <- -1 * int(c.NumPoints)
}
<commit_msg>remove unessecary pointer use<commit_after>package chunk

import (
	"fmt"
	"time"

	"github.com/dgryski/go-tsz"
)

var TotalPoints chan int

func init() {
	// measurements can lag a bit, that's ok
	TotalPoints = make(chan int, 1000)
}

// Chunk is a chunk of data. not concurrency safe.
type Chunk struct {
	tsz.Series
	LastTs    uint32 // last TS seen, not computed or anything
	NumPoints uint32
	Saved     bool
	Saving    bool
	LastWrite uint32
}

func New(t0 uint32) *Chunk {
	// we must set LastWrite here as well to make sure a new Chunk doesn't get immediately
	// garbage collected right after creating it, before we can push to it
	return &Chunk{*tsz.New(t0), 0, 0, false, false, uint32(time.Now().Unix())}
}

func (c *Chunk) String() string {
	return fmt.Sprintf("<chunk T0=%d, LastTs=%d, NumPoints=%d, Saved=%t>", c.T0, c.LastTs, c.NumPoints, c.Saved)

}
func (c *Chunk) Push(t uint32, v float64) error {
	if t <= c.LastTs {
		return fmt.Errorf("Point must be newer than already added points. t:%d lastTs: %d", t, c.LastTs)
	}
	c.Series.Push(t, v)
	c.NumPoints += 1
	c.LastTs = t
	c.LastWrite = uint32(time.Now().Unix())
	TotalPoints <- 1
	return nil
}
func (c *Chunk) Clear() {
	TotalPoints <- -1 * int(c.NumPoints)
}
<|endoftext|>


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

package redis

import (
	"errors"
	"testing"

	"github.com/garyburd/redigo/redis"
	"github.com/ligato/cn-infra/db"
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/onsi/gomega"
	"github.com/rafaeljusto/redigomock"
	"strconv"
)

var mockConn *redigomock.Conn
var mockPool *redis.Pool
var bytesBroker *BytesConnectionRedis
var iKeys = []interface{}{}
var iVals = []interface{}{}
var iAll = []interface{}{}

var keyValues = map[string]string{
	"keyWest": "a place",
	"keyMap":  "a map",
}

//func init() {
func TestMain(m *testing.M) {
	mockConn = redigomock.NewConn()
	for k, v := range keyValues {
		mockConn.Command("SET", k, v).Expect("not used")
		mockConn.Command("GET", k).Expect(v)
		iKeys = append(iKeys, k)
		iVals = append(iVals, v)
		iAll = append(append(iAll, k), v)
	}
	mockConn.Command("GET", "key").Expect(nil)
	mockConn.Command("GET", "bytes").Expect([]byte("bytes"))
	mockConn.Command("GET", "nil").Expect(nil)

	mockConn.Command("MGET", iKeys...).Expect(iVals)
	mockConn.Command("DEL", iKeys...).Expect(len(keyValues))

	// Additional SCAN (or previous, "KEYS") are set on demand in each test that implicitly calls them.
	mockConn.Command("SCAN", "0", "MATCH", "key*").Expect([]interface{}{[]byte("0"), iKeys})
	//mockConn.Command("KEYS", "key*").Expect(iKeys)

	mockConn.Command("MSET", []interface{}{"keyMap", keyValues["keyMap"]}...).Expect(nil)
	mockConn.Command("DEL", []interface{}{"keyWest"}...).Expect(1).Expect(nil)
	mockConn.Command("PSUBSCRIBE", []interface{}{keySpaceEventPrefix + "key*"}...).Expect(newSubscriptionResponse("psubscribe", keySpaceEventPrefix+"key*", 1))

	// for negative tests
	manufacturedError := errors.New("manufactured error")
	mockConn.Command("SET", "error", "error").ExpectError(manufacturedError)
	mockConn.Command("GET", "error").ExpectError(manufacturedError)
	mockConn.Command("GET", "redisError").Expect(redis.Error("Blah"))
	mockConn.Command("GET", "unknown").Expect(struct{}{})

	mockPool = &redis.Pool{
		Dial: func() (redis.Conn, error) { return mockConn, nil },
	}
	bytesBroker, _ = NewBytesConnectionRedis(mockPool)
}

func TestPut(t *testing.T) {
	gomega.RegisterTestingT(t)
	err := bytesBroker.Put("keyWest", []byte(keyValues["keyWest"]))
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestPutError(t *testing.T) {
	gomega.RegisterTestingT(t)
	err := bytesBroker.Put("error", []byte("error"))
	gomega.Expect(err).Should(gomega.HaveOccurred())
}

func TestGet(t *testing.T) {
	gomega.RegisterTestingT(t)
	val, found, _, err := bytesBroker.GetValue("keyWest")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeTrue())
	gomega.Expect(val).Should(gomega.Equal([]byte(keyValues["keyWest"])))

	val, found, _, err = bytesBroker.GetValue("bytes")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

	val, found, _, err = bytesBroker.GetValue("nil")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())
}

func TestGetError(t *testing.T) {
	gomega.RegisterTestingT(t)
	val, found, _, err := bytesBroker.GetValue("error")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())

	val, found, _, err = bytesBroker.GetValue("redisError")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())

	val, found, _, err = bytesBroker.GetValue("unknown")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())
}

func TestGetShouldNotApplyWildcard(t *testing.T) {
	gomega.RegisterTestingT(t)
	val, found, _, err := bytesBroker.GetValue("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())
}

func TestListValues(t *testing.T) {
	// Implicitly call SCAN (or previous, "KEYS")
	mockConn.Command("SCAN", "0", "MATCH", "key*").Expect([]interface{}{[]byte("0"), iKeys})
	//mockConn.Command("KEYS", "key*").Expect(iKeys)

	gomega.RegisterTestingT(t)
	keyVals, err := bytesBroker.ListValues("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	for {
		kv, last := keyVals.GetNext()
		if last {
			break
		}
		gomega.Expect(kv.GetKey()).Should(gomega.SatisfyAny(gomega.BeEquivalentTo("keyWest"), gomega.BeEquivalentTo("keyMap")))
		gomega.Expect(kv.GetValue()).Should(gomega.SatisfyAny(gomega.BeEquivalentTo(keyValues["keyWest"]), gomega.BeEquivalentTo(keyValues["keyMap"])))
	}
}

func TestListKeys(t *testing.T) {
	gomega.RegisterTestingT(t)
	keys, err := bytesBroker.ListKeys("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	for {
		k, _, last := keys.GetNext()
		if last {
			break
		}
		gomega.Expect(k).Should(gomega.SatisfyAny(gomega.BeEquivalentTo("keyWest"), gomega.BeEquivalentTo("keyMap")))
	}
}

func TestDel(t *testing.T) {
	// Implicitly call SCAN (or previous, "KEYS")
	mockConn.Command("SCAN", "0", "MATCH", "key*").Expect([]interface{}{[]byte("0"), iKeys})
	//mockConn.Command("KEYS", "key*").Expect(iKeys)

	gomega.RegisterTestingT(t)
	found, err := bytesBroker.Delete("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeTrue())
}

func TestTxn(t *testing.T) {
	gomega.RegisterTestingT(t)
	txn := bytesBroker.NewTxn()
	txn.Put("keyWest", []byte(keyValues["keyWest"])).Put("keyMap", []byte(keyValues["keyMap"]))
	txn.Delete("keyWest")
	err := txn.Commit()
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestWatch(t *testing.T) {
	gomega.RegisterTestingT(t)
	count := 0
	mockConn.AddSubscriptionMessage(newPMessage(keySpaceEventPrefix+"key*", keySpaceEventPrefix+"keyWest", "set"))
	count++
	mockConn.AddSubscriptionMessage(newPMessage(keySpaceEventPrefix+"key*", keySpaceEventPrefix+"keyWest", "del"))
	count++
	doneChan := make(chan struct{})
	respChan := make(chan keyval.BytesWatchResp)
	bytesBroker.Watch(respChan, "key")
	go func() {
		for {
			select {
			case r, ok := <-respChan:
				if ok {
					switch r.GetChangeType() {
					case db.Put:
						log.Debugf("Watcher received %v: %s=%s", r.GetChangeType(), r.GetKey(), string(r.GetValue()))
					case db.Delete:
						log.Debugf("Watcher received %v: %s", r.GetChangeType(), r.GetKey())
					}
					count--
					if count == 0 {
						doneChan <- struct{}{}
					}
				} else {
					log.Error("Something wrong with respChan... bail out")
					return
				}
			default:
				break
			}
		}
	}()
	<-doneChan
	log.Infof("TestWatch is done")
}

func newSubscriptionResponse(kind string, chanName string, count int) []interface{} {
	values := []interface{}{}
	values = append(values, interface{}([]byte(kind)))
	values = append(values, interface{}([]byte(chanName)))
	values = append(values, interface{}([]byte(strconv.Itoa(count))))
	return values
}

func newPMessage(pattern string, chanName string, data string) []interface{} {
	values := []interface{}{}
	values = append(values, interface{}([]byte("pmessage")))
	values = append(values, interface{}([]byte(pattern)))
	values = append(values, interface{}([]byte(chanName)))
	values = append(values, interface{}([]byte(data)))
	return values
}

func TestBrokerClosed(t *testing.T) {
	gomega.RegisterTestingT(t)
	bytesBroker.Close()

	err := bytesBroker.Put("any", []byte("any"))
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, _, _, err = bytesBroker.GetValue("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, err = bytesBroker.ListValues("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, err = bytesBroker.ListKeys("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, err = bytesBroker.Delete("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
}
<commit_msg>reverted TestMain() to init()<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package redis

import (
	"errors"
	"testing"

	"github.com/garyburd/redigo/redis"
	"github.com/ligato/cn-infra/db"
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/onsi/gomega"
	"github.com/rafaeljusto/redigomock"
	"strconv"
)

var mockConn *redigomock.Conn
var mockPool *redis.Pool
var bytesBroker *BytesConnectionRedis
var iKeys = []interface{}{}
var iVals = []interface{}{}
var iAll = []interface{}{}

var keyValues = map[string]string{
	"keyWest": "a place",
	"keyMap":  "a map",
}

// Seriously, must do init() instead of TestMain().
// Or test coverage will generated profile.
// func TestMain(m *testing.M) {
func init() {
	mockConn = redigomock.NewConn()
	for k, v := range keyValues {
		mockConn.Command("SET", k, v).Expect("not used")
		mockConn.Command("GET", k).Expect(v)
		iKeys = append(iKeys, k)
		iVals = append(iVals, v)
		iAll = append(append(iAll, k), v)
	}
	mockConn.Command("GET", "key").Expect(nil)
	mockConn.Command("GET", "bytes").Expect([]byte("bytes"))
	mockConn.Command("GET", "nil").Expect(nil)

	mockConn.Command("MGET", iKeys...).Expect(iVals)
	mockConn.Command("DEL", iKeys...).Expect(len(keyValues))

	mockConn.Command("MSET", []interface{}{"keyMap", keyValues["keyMap"]}...).Expect(nil)
	mockConn.Command("DEL", []interface{}{"keyWest"}...).Expect(1).Expect(nil)
	mockConn.Command("PSUBSCRIBE", []interface{}{keySpaceEventPrefix + "key*"}...).Expect(newSubscriptionResponse("psubscribe", keySpaceEventPrefix+"key*", 1))

	// for negative tests
	manufacturedError := errors.New("manufactured error")
	mockConn.Command("SET", "error", "error").ExpectError(manufacturedError)
	mockConn.Command("GET", "error").ExpectError(manufacturedError)
	mockConn.Command("GET", "redisError").Expect(redis.Error("Blah"))
	mockConn.Command("GET", "unknown").Expect(struct{}{})

	mockPool = &redis.Pool{
		Dial: func() (redis.Conn, error) { return mockConn, nil },
	}
	bytesBroker, _ = NewBytesConnectionRedis(mockPool)
}

func TestPut(t *testing.T) {
	gomega.RegisterTestingT(t)
	err := bytesBroker.Put("keyWest", []byte(keyValues["keyWest"]))
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestPutError(t *testing.T) {
	gomega.RegisterTestingT(t)
	err := bytesBroker.Put("error", []byte("error"))
	gomega.Expect(err).Should(gomega.HaveOccurred())
}

func TestGet(t *testing.T) {
	gomega.RegisterTestingT(t)
	val, found, _, err := bytesBroker.GetValue("keyWest")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeTrue())
	gomega.Expect(val).Should(gomega.Equal([]byte(keyValues["keyWest"])))

	val, found, _, err = bytesBroker.GetValue("bytes")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

	val, found, _, err = bytesBroker.GetValue("nil")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())
}

func TestGetError(t *testing.T) {
	gomega.RegisterTestingT(t)
	val, found, _, err := bytesBroker.GetValue("error")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())

	val, found, _, err = bytesBroker.GetValue("redisError")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())

	val, found, _, err = bytesBroker.GetValue("unknown")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())
}

func TestGetShouldNotApplyWildcard(t *testing.T) {
	gomega.RegisterTestingT(t)
	val, found, _, err := bytesBroker.GetValue("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeFalse())
	gomega.Expect(val).Should(gomega.BeNil())
}

func TestListValues(t *testing.T) {
	// Implicitly call SCAN (or previous, "KEYS")
	mockConn.Command("SCAN", "0", "MATCH", "key*").Expect([]interface{}{[]byte("0"), iKeys})
	//mockConn.Command("KEYS", "key*").Expect(iKeys)

	gomega.RegisterTestingT(t)
	keyVals, err := bytesBroker.ListValues("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	for {
		kv, last := keyVals.GetNext()
		if last {
			break
		}
		gomega.Expect(kv.GetKey()).Should(gomega.SatisfyAny(gomega.BeEquivalentTo("keyWest"), gomega.BeEquivalentTo("keyMap")))
		gomega.Expect(kv.GetValue()).Should(gomega.SatisfyAny(gomega.BeEquivalentTo(keyValues["keyWest"]), gomega.BeEquivalentTo(keyValues["keyMap"])))
	}
}

func TestListKeys(t *testing.T) {
	// Each SCAN (or previous, "KEYS") is set on demand in individual test that calls it.
	mockConn.Command("SCAN", "0", "MATCH", "key*").Expect([]interface{}{[]byte("0"), iKeys})
	//mockConn.Command("KEYS", "key*").Expect(iKeys)

	gomega.RegisterTestingT(t)
	keys, err := bytesBroker.ListKeys("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	for {
		k, _, last := keys.GetNext()
		if last {
			break
		}
		gomega.Expect(k).Should(gomega.SatisfyAny(gomega.BeEquivalentTo("keyWest"), gomega.BeEquivalentTo("keyMap")))
	}
}

func TestDel(t *testing.T) {
	// Implicitly call SCAN (or previous, "KEYS")
	mockConn.Command("SCAN", "0", "MATCH", "key*").Expect([]interface{}{[]byte("0"), iKeys})
	//mockConn.Command("KEYS", "key*").Expect(iKeys)

	gomega.RegisterTestingT(t)
	found, err := bytesBroker.Delete("key")
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
	gomega.Expect(found).Should(gomega.BeTrue())
}

func TestTxn(t *testing.T) {
	gomega.RegisterTestingT(t)
	txn := bytesBroker.NewTxn()
	txn.Put("keyWest", []byte(keyValues["keyWest"])).Put("keyMap", []byte(keyValues["keyMap"]))
	txn.Delete("keyWest")
	err := txn.Commit()
	gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestWatch(t *testing.T) {
	gomega.RegisterTestingT(t)
	count := 0
	mockConn.AddSubscriptionMessage(newPMessage(keySpaceEventPrefix+"key*", keySpaceEventPrefix+"keyWest", "set"))
	count++
	mockConn.AddSubscriptionMessage(newPMessage(keySpaceEventPrefix+"key*", keySpaceEventPrefix+"keyWest", "del"))
	count++
	doneChan := make(chan struct{})
	respChan := make(chan keyval.BytesWatchResp)
	bytesBroker.Watch(respChan, "key")
	go func() {
		for {
			select {
			case r, ok := <-respChan:
				if ok {
					switch r.GetChangeType() {
					case db.Put:
						log.Debugf("Watcher received %v: %s=%s", r.GetChangeType(), r.GetKey(), string(r.GetValue()))
					case db.Delete:
						log.Debugf("Watcher received %v: %s", r.GetChangeType(), r.GetKey())
					}
					count--
					if count == 0 {
						doneChan <- struct{}{}
					}
				} else {
					log.Error("Something wrong with respChan... bail out")
					return
				}
			default:
				break
			}
		}
	}()
	<-doneChan
	log.Infof("TestWatch is done")
}

func newSubscriptionResponse(kind string, chanName string, count int) []interface{} {
	values := []interface{}{}
	values = append(values, interface{}([]byte(kind)))
	values = append(values, interface{}([]byte(chanName)))
	values = append(values, interface{}([]byte(strconv.Itoa(count))))
	return values
}

func newPMessage(pattern string, chanName string, data string) []interface{} {
	values := []interface{}{}
	values = append(values, interface{}([]byte("pmessage")))
	values = append(values, interface{}([]byte(pattern)))
	values = append(values, interface{}([]byte(chanName)))
	values = append(values, interface{}([]byte(data)))
	return values
}

func TestBrokerClosed(t *testing.T) {
	gomega.RegisterTestingT(t)
	bytesBroker.Close()

	err := bytesBroker.Put("any", []byte("any"))
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, _, _, err = bytesBroker.GetValue("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, err = bytesBroker.ListValues("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, err = bytesBroker.ListKeys("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
	_, err = bytesBroker.Delete("any")
	gomega.Expect(err).Should(gomega.HaveOccurred())
}
<|endoftext|>


<commit_before>package render

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"time"

	"github.com/gogo/protobuf/proto"
	"go.uber.org/zap"

	"github.com/lomik/graphite-clickhouse/carbonzipperpb"
	"github.com/lomik/graphite-clickhouse/config"
	"github.com/lomik/graphite-clickhouse/finder"
	"github.com/lomik/graphite-clickhouse/helper/clickhouse"
	"github.com/lomik/graphite-clickhouse/helper/log"
	"github.com/lomik/graphite-clickhouse/helper/pickle"
	"github.com/lomik/graphite-clickhouse/helper/point"

	graphitePickle "github.com/lomik/graphite-pickle"
)

type Handler struct {
	config     *config.Config
	carbonlink *graphitePickle.CarbonlinkClient
}

func NewHandler(config *config.Config) *Handler {
	h := &Handler{
		config: config,
	}

	if config.Carbonlink.Server != "" {
		h.carbonlink = graphitePickle.NewCarbonlinkClient(
			config.Carbonlink.Server,
			config.Carbonlink.Retries,
			config.Carbonlink.Threads,
			config.Carbonlink.ConnectTimeout.Value(),
			config.Carbonlink.QueryTimeout.Value(),
		)
	}
	return h
}

// returns callable result fetcher
func (h *Handler) queryCarbonlink(parentCtx context.Context, logger *zap.Logger, merticsList [][]byte) func() []point.Point {
	if h.carbonlink == nil {
		return func() []point.Point { return nil }
	}

	metrics := make([]string, len(merticsList))
	for i := 0; i < len(metrics); i++ {
		metrics[i] = unsafeString(merticsList[i])
	}

	carbonlinkResponseChan := make(chan []point.Point, 1)

	fetchResult := func() []point.Point {
		result := <-carbonlinkResponseChan
		return result
	}

	go func() {
		ctx, cancel := context.WithTimeout(parentCtx, h.config.Carbonlink.TotalTimeout.Value())
		defer cancel()

		res, err := h.carbonlink.CacheQueryMulti(ctx, metrics)

		if err != nil {
			logger.Info("carbonlink failed", zap.Error(err))
		}

		var result []point.Point

		if res != nil && len(res) > 0 {
			sz := 0
			for _, points := range res {
				sz += len(points)
			}

			tm := int32(time.Now().Unix())

			result = make([]point.Point, sz)
			i := 0
			for metric, points := range res {
				for _, p := range points {
					result[i].Metric = metric
					result[i].Time = int32(p.Timestamp)
					result[i].Value = p.Value
					result[i].Timestamp = tm
				}
				i++
			}
		}

		carbonlinkResponseChan <- result
	}()

	return fetchResult
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logger := log.FromContext(r.Context())
	target := r.URL.Query().Get("target")

	var prefix string
	var err error

	fromTimestamp, err := strconv.ParseInt(r.URL.Query().Get("from"), 10, 32)
	if err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	untilTimestamp, err := strconv.ParseInt(r.URL.Query().Get("until"), 10, 32)
	if err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	// Search in small index table first
	finder := finder.New(r.Context(), h.config)

	err = finder.Execute(target)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	metricList := finder.Series()

	maxStep := int32(0)

	listBuf := bytes.NewBuffer(nil)

	// make Path IN (...), calculate max step
	for index, m := range metricList {
		if len(m) == 0 {
			continue
		}
		step := h.config.Rollup.Step(unsafeString(m), int32(fromTimestamp))
		if step > maxStep {
			maxStep = step
		}

		if index > 0 {
			listBuf.Write([]byte{','})
		}

		listBuf.WriteString("'" + clickhouse.Escape(unsafeString(m)) + "'")
	}

	if listBuf.Len() == 0 {
		// Return empty response
		h.Reply(w, r, &Data{Points: make([]point.Point, 0), Finder: finder}, 0, 0, "")
		return
	}

	var pathWhere = fmt.Sprintf(
		"Path IN (%s)",
		string(listBuf.Bytes()),
	)

	until := untilTimestamp - untilTimestamp%int64(maxStep) + int64(maxStep) - 1
	dateWhere := fmt.Sprintf(
		"(Date >='%s' AND Date <= '%s' AND Time >= %d AND Time <= %d)",
		time.Unix(fromTimestamp, 0).Format("2006-01-02"),
		time.Unix(untilTimestamp, 0).Format("2006-01-02"),
		fromTimestamp,
		until,
	)

	query := fmt.Sprintf(
		`
		SELECT
			Path, Time, Value, Timestamp
		FROM %s
		WHERE (%s) AND (%s)
		FORMAT RowBinary
		`,
		h.config.ClickHouse.DataTable,
		pathWhere,
		dateWhere,
	)

	// start carbonlink request
	carbonlinkResponseRead := h.queryCarbonlink(r.Context(), logger, metricList)

	body, err := clickhouse.Query(
		r.Context(),
		h.config.ClickHouse.Url,
		query,
		h.config.ClickHouse.DataTimeout.Value(),
	)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// fetch carbonlink response
	carbonlinkData := carbonlinkResponseRead()

	parseStart := time.Now()

	// pass carbonlinkData to DataParse
	data, err := DataParse(body, carbonlinkData)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	d := time.Since(parseStart)
	logger.Debug("parse", zap.String("runtime", d.String()), zap.Duration("runtime_ns", d))

	sortStart := time.Now()
	sort.Sort(data)
	d = time.Since(sortStart)
	logger.Debug("sort", zap.String("runtime", d.String()), zap.Duration("runtime_ns", d))

	data.Points = point.Uniq(data.Points)
	data.Finder = finder

	// pp.Println(points)
	h.Reply(w, r, data, int32(fromTimestamp), int32(untilTimestamp), prefix)
}

func (h *Handler) Reply(w http.ResponseWriter, r *http.Request, data *Data, from, until int32, prefix string) {
	start := time.Now()
	switch r.URL.Query().Get("format") {
	case "pickle":
		h.ReplyPickle(w, r, data, from, until, prefix)
	case "protobuf":
		h.ReplyProtobuf(w, r, data, from, until, prefix)
	}
	d := time.Since(start)
	log.FromContext(r.Context()).Debug("reply", zap.String("runtime", d.String()), zap.Duration("runtime_ns", d))
}

func (h *Handler) ReplyPickle(w http.ResponseWriter, r *http.Request, data *Data, from, until int32, prefix string) {
	var rollupTime time.Duration
	var pickleTime time.Duration

	points := data.Points

	defer func() {
		log.FromContext(r.Context()).Debug("rollup",
			zap.String("runtime", rollupTime.String()),
			zap.Duration("runtime_ns", rollupTime),
		)
		log.FromContext(r.Context()).Debug("pickle",
			zap.String("runtime", pickleTime.String()),
			zap.Duration("runtime_ns", pickleTime),
		)
	}()

	if len(points) == 0 {
		w.Write(pickle.EmptyList)
		return
	}

	writer := bufio.NewWriterSize(w, 1024*1024)
	p := pickle.NewWriter(writer)
	defer writer.Flush()

	p.List()

	writeMetric := func(points []point.Point) {
		rollupStart := time.Now()
		points, step := h.config.Rollup.RollupMetric(points)
		rollupTime += time.Since(rollupStart)

		pickleStart := time.Now()
		p.Dict()

		p.String("name")
		p.Bytes(data.Finder.Abs([]byte(points[0].Metric)))
		p.SetItem()

		p.String("step")
		p.Uint32(uint32(step))
		p.SetItem()

		start := from - (from % step)
		if start < from {
			start += step
		}
		end := until - (until % step)
		last := start - step

		p.String("values")
		p.List()
		for _, point := range points {
			if point.Time < start || point.Time > end {
				continue
			}

			if point.Time > last+step {
				p.AppendNulls(int(((point.Time - last) / step) - 1))
			}

			p.AppendFloat64(point.Value)

			last = point.Time
		}

		if end > last {
			p.AppendNulls(int((end - last) / step))
		}
		p.SetItem()

		p.String("start")
		p.Uint32(uint32(start))
		p.SetItem()

		p.String("end")
		p.Uint32(uint32(end))
		p.SetItem()

		p.Append()
		pickleTime += time.Since(pickleStart)
	}

	// group by Metric
	var i, n int
	// i - current position of iterator
	// n - position of the first record with current metric
	l := len(points)

	for i = 1; i < l; i++ {
		if points[i].Metric != points[n].Metric {
			writeMetric(points[n:i])
			n = i
			continue
		}
	}
	writeMetric(points[n:i])

	p.Stop()
}

func (h *Handler) ReplyProtobuf(w http.ResponseWriter, r *http.Request, data *Data, from, until int32, prefix string) {
	points := data.Points

	if len(points) == 0 {
		return
	}

	var multiResponse carbonzipperpb.MultiFetchResponse

	writeMetric := func(points []point.Point) {
		points, step := h.config.Rollup.RollupMetric(points)

		var name string
		name = string(data.Finder.Abs([]byte(points[0].Metric)))

		start := from - (from % step)
		if start < from {
			start += step
		}
		stop := until - (until % step)
		count := ((stop - start) / step) + 1

		response := carbonzipperpb.FetchResponse{
			Name:      proto.String(name),
			StartTime: &start,
			StopTime:  &stop,
			StepTime:  &step,
			Values:    make([]float64, count),
			IsAbsent:  make([]bool, count),
		}

		var index int32
		// skip points before start
		for index = 0; index < int32(len(points)) && points[index].Time < start; index++ {
		}

		for i := int32(0); i < count; i++ {
			if index < int32(len(points)) && points[index].Time == start+step*i {
				response.Values[i] = points[index].Value
				response.IsAbsent[i] = false
				index++
			} else {
				response.Values[i] = 0
				response.IsAbsent[i] = true
			}
		}

		multiResponse.Metrics = append(multiResponse.Metrics, &response)
	}

	// group by Metric
	var i, n int
	// i - current position of iterator
	// n - position of the first record with current metric
	l := len(points)

	for i = 1; i < l; i++ {
		if points[i].Metric != points[n].Metric {
			writeMetric(points[n:i])
			n = i
			continue
		}
	}
	writeMetric(points[n:i])

	body, _ := proto.Marshal(&multiResponse)
	w.Write(body)
}
<commit_msg>fix carbonlink<commit_after>package render

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"time"

	"github.com/gogo/protobuf/proto"
	"go.uber.org/zap"

	"github.com/lomik/graphite-clickhouse/carbonzipperpb"
	"github.com/lomik/graphite-clickhouse/config"
	"github.com/lomik/graphite-clickhouse/finder"
	"github.com/lomik/graphite-clickhouse/helper/clickhouse"
	"github.com/lomik/graphite-clickhouse/helper/log"
	"github.com/lomik/graphite-clickhouse/helper/pickle"
	"github.com/lomik/graphite-clickhouse/helper/point"

	graphitePickle "github.com/lomik/graphite-pickle"
)

type Handler struct {
	config     *config.Config
	carbonlink *graphitePickle.CarbonlinkClient
}

func NewHandler(config *config.Config) *Handler {
	h := &Handler{
		config: config,
	}

	if config.Carbonlink.Server != "" {
		h.carbonlink = graphitePickle.NewCarbonlinkClient(
			config.Carbonlink.Server,
			config.Carbonlink.Retries,
			config.Carbonlink.Threads,
			config.Carbonlink.ConnectTimeout.Value(),
			config.Carbonlink.QueryTimeout.Value(),
		)
	}
	return h
}

// returns callable result fetcher
func (h *Handler) queryCarbonlink(parentCtx context.Context, logger *zap.Logger, merticsList [][]byte) func() []point.Point {
	if h.carbonlink == nil {
		return func() []point.Point { return nil }
	}

	metrics := make([]string, len(merticsList))
	for i := 0; i < len(metrics); i++ {
		metrics[i] = unsafeString(merticsList[i])
	}

	carbonlinkResponseChan := make(chan []point.Point, 1)

	fetchResult := func() []point.Point {
		result := <-carbonlinkResponseChan
		return result
	}

	go func() {
		ctx, cancel := context.WithTimeout(parentCtx, h.config.Carbonlink.TotalTimeout.Value())
		defer cancel()

		res, err := h.carbonlink.CacheQueryMulti(ctx, metrics)

		if err != nil {
			logger.Info("carbonlink failed", zap.Error(err))
		}

		var result []point.Point

		if res != nil && len(res) > 0 {
			sz := 0
			for _, points := range res {
				sz += len(points)
			}

			tm := int32(time.Now().Unix())

			result = make([]point.Point, sz)
			i := 0
			for metric, points := range res {
				for _, p := range points {
					result[i].Metric = metric
					result[i].Time = int32(p.Timestamp)
					result[i].Value = p.Value
					result[i].Timestamp = tm
					i++
				}
			}
		}

		carbonlinkResponseChan <- result
	}()

	return fetchResult
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logger := log.FromContext(r.Context())
	target := r.URL.Query().Get("target")

	var prefix string
	var err error

	fromTimestamp, err := strconv.ParseInt(r.URL.Query().Get("from"), 10, 32)
	if err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	untilTimestamp, err := strconv.ParseInt(r.URL.Query().Get("until"), 10, 32)
	if err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	// Search in small index table first
	finder := finder.New(r.Context(), h.config)

	err = finder.Execute(target)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	metricList := finder.Series()

	maxStep := int32(0)

	listBuf := bytes.NewBuffer(nil)

	// make Path IN (...), calculate max step
	for index, m := range metricList {
		if len(m) == 0 {
			continue
		}
		step := h.config.Rollup.Step(unsafeString(m), int32(fromTimestamp))
		if step > maxStep {
			maxStep = step
		}

		if index > 0 {
			listBuf.Write([]byte{','})
		}

		listBuf.WriteString("'" + clickhouse.Escape(unsafeString(m)) + "'")
	}

	if listBuf.Len() == 0 {
		// Return empty response
		h.Reply(w, r, &Data{Points: make([]point.Point, 0), Finder: finder}, 0, 0, "")
		return
	}

	var pathWhere = fmt.Sprintf(
		"Path IN (%s)",
		string(listBuf.Bytes()),
	)

	until := untilTimestamp - untilTimestamp%int64(maxStep) + int64(maxStep) - 1
	dateWhere := fmt.Sprintf(
		"(Date >='%s' AND Date <= '%s' AND Time >= %d AND Time <= %d)",
		time.Unix(fromTimestamp, 0).Format("2006-01-02"),
		time.Unix(untilTimestamp, 0).Format("2006-01-02"),
		fromTimestamp,
		until,
	)

	query := fmt.Sprintf(
		`
		SELECT
			Path, Time, Value, Timestamp
		FROM %s
		WHERE (%s) AND (%s)
		FORMAT RowBinary
		`,
		h.config.ClickHouse.DataTable,
		pathWhere,
		dateWhere,
	)

	// start carbonlink request
	carbonlinkResponseRead := h.queryCarbonlink(r.Context(), logger, metricList)

	body, err := clickhouse.Query(
		r.Context(),
		h.config.ClickHouse.Url,
		query,
		h.config.ClickHouse.DataTimeout.Value(),
	)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// fetch carbonlink response
	carbonlinkData := carbonlinkResponseRead()

	parseStart := time.Now()

	// pass carbonlinkData to DataParse
	data, err := DataParse(body, carbonlinkData)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	d := time.Since(parseStart)
	logger.Debug("parse", zap.String("runtime", d.String()), zap.Duration("runtime_ns", d))

	sortStart := time.Now()
	sort.Sort(data)
	d = time.Since(sortStart)
	logger.Debug("sort", zap.String("runtime", d.String()), zap.Duration("runtime_ns", d))

	data.Points = point.Uniq(data.Points)
	data.Finder = finder

	// pp.Println(points)
	h.Reply(w, r, data, int32(fromTimestamp), int32(untilTimestamp), prefix)
}

func (h *Handler) Reply(w http.ResponseWriter, r *http.Request, data *Data, from, until int32, prefix string) {
	start := time.Now()
	switch r.URL.Query().Get("format") {
	case "pickle":
		h.ReplyPickle(w, r, data, from, until, prefix)
	case "protobuf":
		h.ReplyProtobuf(w, r, data, from, until, prefix)
	}
	d := time.Since(start)
	log.FromContext(r.Context()).Debug("reply", zap.String("runtime", d.String()), zap.Duration("runtime_ns", d))
}

func (h *Handler) ReplyPickle(w http.ResponseWriter, r *http.Request, data *Data, from, until int32, prefix string) {
	var rollupTime time.Duration
	var pickleTime time.Duration

	points := data.Points

	defer func() {
		log.FromContext(r.Context()).Debug("rollup",
			zap.String("runtime", rollupTime.String()),
			zap.Duration("runtime_ns", rollupTime),
		)
		log.FromContext(r.Context()).Debug("pickle",
			zap.String("runtime", pickleTime.String()),
			zap.Duration("runtime_ns", pickleTime),
		)
	}()

	if len(points) == 0 {
		w.Write(pickle.EmptyList)
		return
	}

	writer := bufio.NewWriterSize(w, 1024*1024)
	p := pickle.NewWriter(writer)
	defer writer.Flush()

	p.List()

	writeMetric := func(points []point.Point) {
		rollupStart := time.Now()
		points, step := h.config.Rollup.RollupMetric(points)
		rollupTime += time.Since(rollupStart)

		pickleStart := time.Now()
		p.Dict()

		p.String("name")
		p.Bytes(data.Finder.Abs([]byte(points[0].Metric)))
		p.SetItem()

		p.String("step")
		p.Uint32(uint32(step))
		p.SetItem()

		start := from - (from % step)
		if start < from {
			start += step
		}
		end := until - (until % step)
		last := start - step

		p.String("values")
		p.List()
		for _, point := range points {
			if point.Time < start || point.Time > end {
				continue
			}

			if point.Time > last+step {
				p.AppendNulls(int(((point.Time - last) / step) - 1))
			}

			p.AppendFloat64(point.Value)

			last = point.Time
		}

		if end > last {
			p.AppendNulls(int((end - last) / step))
		}
		p.SetItem()

		p.String("start")
		p.Uint32(uint32(start))
		p.SetItem()

		p.String("end")
		p.Uint32(uint32(end))
		p.SetItem()

		p.Append()
		pickleTime += time.Since(pickleStart)
	}

	// group by Metric
	var i, n int
	// i - current position of iterator
	// n - position of the first record with current metric
	l := len(points)

	for i = 1; i < l; i++ {
		if points[i].Metric != points[n].Metric {
			writeMetric(points[n:i])
			n = i
			continue
		}
	}
	writeMetric(points[n:i])

	p.Stop()
}

func (h *Handler) ReplyProtobuf(w http.ResponseWriter, r *http.Request, data *Data, from, until int32, prefix string) {
	points := data.Points

	if len(points) == 0 {
		return
	}

	var multiResponse carbonzipperpb.MultiFetchResponse

	writeMetric := func(points []point.Point) {
		points, step := h.config.Rollup.RollupMetric(points)

		var name string
		name = string(data.Finder.Abs([]byte(points[0].Metric)))

		start := from - (from % step)
		if start < from {
			start += step
		}
		stop := until - (until % step)
		count := ((stop - start) / step) + 1

		response := carbonzipperpb.FetchResponse{
			Name:      proto.String(name),
			StartTime: &start,
			StopTime:  &stop,
			StepTime:  &step,
			Values:    make([]float64, count),
			IsAbsent:  make([]bool, count),
		}

		var index int32
		// skip points before start
		for index = 0; index < int32(len(points)) && points[index].Time < start; index++ {
		}

		for i := int32(0); i < count; i++ {
			if index < int32(len(points)) && points[index].Time == start+step*i {
				response.Values[i] = points[index].Value
				response.IsAbsent[i] = false
				index++
			} else {
				response.Values[i] = 0
				response.IsAbsent[i] = true
			}
		}

		multiResponse.Metrics = append(multiResponse.Metrics, &response)
	}

	// group by Metric
	var i, n int
	// i - current position of iterator
	// n - position of the first record with current metric
	l := len(points)

	for i = 1; i < l; i++ {
		if points[i].Metric != points[n].Metric {
			writeMetric(points[n:i])
			n = i
			continue
		}
	}
	writeMetric(points[n:i])

	body, _ := proto.Marshal(&multiResponse)
	w.Write(body)
}
<|endoftext|>


<commit_before>package sepa

import (
	"encoding/xml"
	"errors"
	"math/big"
	"strconv"
	"strings"
	"time"
)

// Document is the SEPA format for the document containing all transfers
type Document struct {
	XMLName               xml.Name      `xml:"Document"`
	XMLNs                 string        `xml:"xmlns,attr"`
	XMLxsi                string        `xml:"xmlns:xsi,attr"`
	GroupheaderMsgID      string        `xml:"CstmrCdtTrfInitn>GrpHdr>MsgId"`
	GroupheaderCreateDate string        `xml:"CstmrCdtTrfInitn>GrpHdr>CreDtTm"`
	GroupheaderTransacNb  int           `xml:"CstmrCdtTrfInitn>GrpHdr>NbOfTxs"`
	GroupheaderCtrlSum    float64       `xml:"CstmrCdtTrfInitn>GrpHdr>CtrlSum"`
	GroupheaderEmiterName string        `xml:"CstmrCdtTrfInitn>GrpHdr>InitgPty>Nm"`
	PaymentInfoID         string        `xml:"CstmrCdtTrfInitn>PmtInf>PmtInfId"`
	PaymentInfoMethod     string        `xml:"CstmrCdtTrfInitn>PmtInf>PmtMtd"`
	PaymentInfoTransacNb  int           `xml:"CstmrCdtTrfInitn>PmtInf>NbOfTxs"`
	PaymentInfoCtrlSum    float64       `xml:"CstmrCdtTrfInitn>PmtInf>CtrlSum"`
	PaymentTypeInfo       string        `xml:"CstmrCdtTrfInitn>PmtInf>PmtTpInf>SvcLvl>Cd"`
	PaymentExecDate       string        `xml:"CstmrCdtTrfInitn>PmtInf>ReqdExctnDt"`
	PaymentEmiterName     string        `xml:"CstmrCdtTrfInitn>PmtInf>Dbtr>Nm"`
	PaymentEmiterIBAN     string        `xml:"CstmrCdtTrfInitn>PmtInf>DbtrAcct>Id>IBAN"`
	PaymentEmiterBIC      string        `xml:"CstmrCdtTrfInitn>PmtInf>DbtrAgt>FinInstnId>BIC"`
	PaymentCharge         string        `xml:"CstmrCdtTrfInitn>PmtInf>ChrgBr"`
	PaymentTransactions   []Transaction `xml:"CstmrCdtTrfInitn>PmtInf>CdtTrfTxInf"`
}

// Transaction is the transfer SEPA format
type Transaction struct {
	TransacID           string  `xml:"PmtId>InstrId"`
	TransacIDe2e        string  `xml:"PmtId>EndToEndId"`
	TransacAmount       TAmount `xml:"Amt>InstdAmt"`
	TransacCreditorName string  `xml:"Cdtr>Nm"`
	TransacCreditorIBAN string  `xml:"CdtrAcct>Id>IBAN"`
	TransacRegulatory   string  `xml:"RgltryRptg>Dtls>Cd"`
	TransacMotif        string  `xml:"RmtInf>Ustrd"`
}

// TAmount is the transaction amount with its currency
type TAmount struct {
	Amount   float64 `xml:",chardata"`
	Currency string  `xml:"Ccy,attr"`
}

// InitDoc fixes every constants in the document + emiter informations
func (doc *Document) InitDoc(msgID string, creationDate string, executionDate string, emiterName string, emiterIBAN string, emiterBIC string) error {
	_, err := time.Parse("2006-01-02T15:04:05", creationDate) // format : AAAA-MM-JJTHH:HH:SS
	if err != nil {
		return err
	}
	_, err = time.Parse("2006-01-02", executionDate) // format : AAAA-MM-JJ
	if err != nil {
		return err
	}
	if !IsValid(emiterIBAN) {
		return errors.New("Invalid IBAN")
	}
	doc.XMLNs = "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
	doc.XMLxsi = "http://www.w3.org/2001/XMLSchema-instance"
	doc.GroupheaderMsgID = msgID
	doc.PaymentInfoID = msgID
	doc.GroupheaderCreateDate = creationDate
	doc.PaymentExecDate = executionDate
	doc.GroupheaderEmiterName = emiterName
	doc.PaymentEmiterName = emiterName
	doc.PaymentEmiterIBAN = emiterIBAN
	doc.PaymentEmiterBIC = emiterBIC
	doc.PaymentInfoMethod = "TRF" // always TRF
	doc.PaymentTypeInfo = "SEPA"  // always SEPA
	doc.PaymentCharge = "SLEV"    // always SLEV
	return nil
}

// AddTransaction adds a transfer transaction and adjust the transaction number and the sum control
func (doc *Document) AddTransaction(id string, amount float64, currency string, creditorName string, creditorIBAN string) error {
	if !IsValid(creditorIBAN) {
		return errors.New("Invalid IBAN")
	}
	if DecimalsNumber(amount) > 2 {
		return errors.New("Amount 2 decimals only")
	}
	doc.PaymentTransactions = append(doc.PaymentTransactions, Transaction{
		TransacID:           id,
		TransacIDe2e:        id,
		TransacMotif:        id,
		TransacAmount:       TAmount{Amount: amount, Currency: currency},
		TransacCreditorName: creditorName,
		TransacCreditorIBAN: creditorIBAN,
		TransacRegulatory:   "150", // always 150
	})
	doc.GroupheaderTransacNb++
	doc.PaymentInfoTransacNb++

	amountCents, e := ToCents(amount)
	if e != nil {
		return errors.New("In AddTransaction can't convert amount in cents")
	}
	cumulCents, _ := ToCents(doc.GroupheaderCtrlSum)
	if e != nil {
		return errors.New("In AddTransaction can't convert control sum in cents")
	}

	cumulCents += amountCents

	cumulEuro, _ := ToEuro(cumulCents)
	if e != nil {
		return errors.New("In AddTransaction can't convert cumul in euro")
	}

	doc.GroupheaderCtrlSum = cumulEuro
	doc.PaymentInfoCtrlSum = cumulEuro
	return nil
}

// Serialize returns the xml document in byte stream
func (doc *Document) Serialize() ([]byte, error) {
	return xml.Marshal(doc)
}

// PrettySerialize returns the indented xml document in byte stream
func (doc *Document) PrettySerialize() ([]byte, error) {
	return xml.MarshalIndent(doc, "", "  ")
}

// IsValid IBAN
func IsValid(iban string) bool {
	i := new(big.Int)
	t := big.NewInt(10)
	if len(iban) < 4 || len(iban) > 34 {
		return false
	}
	for _, v := range iban[4:] + iban[:4] {
		switch {
		case v >= 'A' && v <= 'Z':
			ch := v - 'A' + 10
			i.Add(i.Mul(i, t), big.NewInt(int64(ch/10)))
			i.Add(i.Mul(i, t), big.NewInt(int64(ch%10)))
		case v >= '0' && v <= '9':
			i.Add(i.Mul(i, t), big.NewInt(int64(v-'0')))
		case v == ' ':
		default:
			return false
		}
	}
	return i.Mod(i, big.NewInt(97)).Int64() == 1
}

// DecimalsNumber returns the number of decimals in a float
func DecimalsNumber(f float64) int {
	s := strconv.FormatFloat(f, 'f', -1, 64)
	p := strings.Split(s, ".")
	if len(p) < 2 {
		return 0
	}
	return len(p[1])
}

// ToCents returns the cents representation in int64
func ToCents(f float64) (int64, error) {
	s := strconv.FormatFloat(f, 'f', 2, 64)
	sc := strings.Replace(s, ".", "", 1)
	return strconv.ParseInt(sc, 10, 64)
}

// ToEuro returns the euro representation in float64
func ToEuro(i int64) (float64, error) {
	d := strconv.FormatInt(i, 10)
	df := d[:len(d)-2] + "." + d[len(d)-2:]
	return strconv.ParseFloat(df, 64)
}
<commit_msg>more specific errors<commit_after>package sepa

import (
	"encoding/xml"
	"errors"
	"math/big"
	"strconv"
	"strings"
	"time"
)

// Document is the SEPA format for the document containing all transfers
type Document struct {
	XMLName               xml.Name      `xml:"Document"`
	XMLNs                 string        `xml:"xmlns,attr"`
	XMLxsi                string        `xml:"xmlns:xsi,attr"`
	GroupheaderMsgID      string        `xml:"CstmrCdtTrfInitn>GrpHdr>MsgId"`
	GroupheaderCreateDate string        `xml:"CstmrCdtTrfInitn>GrpHdr>CreDtTm"`
	GroupheaderTransacNb  int           `xml:"CstmrCdtTrfInitn>GrpHdr>NbOfTxs"`
	GroupheaderCtrlSum    float64       `xml:"CstmrCdtTrfInitn>GrpHdr>CtrlSum"`
	GroupheaderEmiterName string        `xml:"CstmrCdtTrfInitn>GrpHdr>InitgPty>Nm"`
	PaymentInfoID         string        `xml:"CstmrCdtTrfInitn>PmtInf>PmtInfId"`
	PaymentInfoMethod     string        `xml:"CstmrCdtTrfInitn>PmtInf>PmtMtd"`
	PaymentInfoTransacNb  int           `xml:"CstmrCdtTrfInitn>PmtInf>NbOfTxs"`
	PaymentInfoCtrlSum    float64       `xml:"CstmrCdtTrfInitn>PmtInf>CtrlSum"`
	PaymentTypeInfo       string        `xml:"CstmrCdtTrfInitn>PmtInf>PmtTpInf>SvcLvl>Cd"`
	PaymentExecDate       string        `xml:"CstmrCdtTrfInitn>PmtInf>ReqdExctnDt"`
	PaymentEmiterName     string        `xml:"CstmrCdtTrfInitn>PmtInf>Dbtr>Nm"`
	PaymentEmiterIBAN     string        `xml:"CstmrCdtTrfInitn>PmtInf>DbtrAcct>Id>IBAN"`
	PaymentEmiterBIC      string        `xml:"CstmrCdtTrfInitn>PmtInf>DbtrAgt>FinInstnId>BIC"`
	PaymentCharge         string        `xml:"CstmrCdtTrfInitn>PmtInf>ChrgBr"`
	PaymentTransactions   []Transaction `xml:"CstmrCdtTrfInitn>PmtInf>CdtTrfTxInf"`
}

// Transaction is the transfer SEPA format
type Transaction struct {
	TransacID           string  `xml:"PmtId>InstrId"`
	TransacIDe2e        string  `xml:"PmtId>EndToEndId"`
	TransacAmount       TAmount `xml:"Amt>InstdAmt"`
	TransacCreditorName string  `xml:"Cdtr>Nm"`
	TransacCreditorIBAN string  `xml:"CdtrAcct>Id>IBAN"`
	TransacRegulatory   string  `xml:"RgltryRptg>Dtls>Cd"`
	TransacMotif        string  `xml:"RmtInf>Ustrd"`
}

// TAmount is the transaction amount with its currency
type TAmount struct {
	Amount   float64 `xml:",chardata"`
	Currency string  `xml:"Ccy,attr"`
}

// InitDoc fixes every constants in the document + emiter informations
func (doc *Document) InitDoc(msgID string, creationDate string, executionDate string, emiterName string, emiterIBAN string, emiterBIC string) error {
	_, err := time.Parse("2006-01-02T15:04:05", creationDate) // format : AAAA-MM-JJTHH:HH:SS
	if err != nil {
		return err
	}
	_, err = time.Parse("2006-01-02", executionDate) // format : AAAA-MM-JJ
	if err != nil {
		return err
	}
	if !IsValid(emiterIBAN) {
		return errors.New("Invalid emiter IBAN")
	}
	doc.XMLNs = "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
	doc.XMLxsi = "http://www.w3.org/2001/XMLSchema-instance"
	doc.GroupheaderMsgID = msgID
	doc.PaymentInfoID = msgID
	doc.GroupheaderCreateDate = creationDate
	doc.PaymentExecDate = executionDate
	doc.GroupheaderEmiterName = emiterName
	doc.PaymentEmiterName = emiterName
	doc.PaymentEmiterIBAN = emiterIBAN
	doc.PaymentEmiterBIC = emiterBIC
	doc.PaymentInfoMethod = "TRF" // always TRF
	doc.PaymentTypeInfo = "SEPA"  // always SEPA
	doc.PaymentCharge = "SLEV"    // always SLEV
	return nil
}

// AddTransaction adds a transfer transaction and adjust the transaction number and the sum control
func (doc *Document) AddTransaction(id string, amount float64, currency string, creditorName string, creditorIBAN string) error {
	if !IsValid(creditorIBAN) {
		return errors.New("Invalid creditor IBAN")
	}
	if DecimalsNumber(amount) > 2 {
		return errors.New("Amount 2 decimals only")
	}
	doc.PaymentTransactions = append(doc.PaymentTransactions, Transaction{
		TransacID:           id,
		TransacIDe2e:        id,
		TransacMotif:        id,
		TransacAmount:       TAmount{Amount: amount, Currency: currency},
		TransacCreditorName: creditorName,
		TransacCreditorIBAN: creditorIBAN,
		TransacRegulatory:   "150", // always 150
	})
	doc.GroupheaderTransacNb++
	doc.PaymentInfoTransacNb++

	amountCents, e := ToCents(amount)
	if e != nil {
		return errors.New("In AddTransaction can't convert amount in cents")
	}
	cumulCents, _ := ToCents(doc.GroupheaderCtrlSum)
	if e != nil {
		return errors.New("In AddTransaction can't convert control sum in cents")
	}

	cumulCents += amountCents

	cumulEuro, _ := ToEuro(cumulCents)
	if e != nil {
		return errors.New("In AddTransaction can't convert cumul in euro")
	}

	doc.GroupheaderCtrlSum = cumulEuro
	doc.PaymentInfoCtrlSum = cumulEuro
	return nil
}

// Serialize returns the xml document in byte stream
func (doc *Document) Serialize() ([]byte, error) {
	return xml.Marshal(doc)
}

// PrettySerialize returns the indented xml document in byte stream
func (doc *Document) PrettySerialize() ([]byte, error) {
	return xml.MarshalIndent(doc, "", "  ")
}

// IsValid IBAN
func IsValid(iban string) bool {
	i := new(big.Int)
	t := big.NewInt(10)
	if len(iban) < 4 || len(iban) > 34 {
		return false
	}
	for _, v := range iban[4:] + iban[:4] {
		switch {
		case v >= 'A' && v <= 'Z':
			ch := v - 'A' + 10
			i.Add(i.Mul(i, t), big.NewInt(int64(ch/10)))
			i.Add(i.Mul(i, t), big.NewInt(int64(ch%10)))
		case v >= '0' && v <= '9':
			i.Add(i.Mul(i, t), big.NewInt(int64(v-'0')))
		case v == ' ':
		default:
			return false
		}
	}
	return i.Mod(i, big.NewInt(97)).Int64() == 1
}

// DecimalsNumber returns the number of decimals in a float
func DecimalsNumber(f float64) int {
	s := strconv.FormatFloat(f, 'f', -1, 64)
	p := strings.Split(s, ".")
	if len(p) < 2 {
		return 0
	}
	return len(p[1])
}

// ToCents returns the cents representation in int64
func ToCents(f float64) (int64, error) {
	s := strconv.FormatFloat(f, 'f', 2, 64)
	sc := strings.Replace(s, ".", "", 1)
	return strconv.ParseInt(sc, 10, 64)
}

// ToEuro returns the euro representation in float64
func ToEuro(i int64) (float64, error) {
	d := strconv.FormatInt(i, 10)
	df := d[:len(d)-2] + "." + d[len(d)-2:]
	return strconv.ParseFloat(df, 64)
}
<|endoftext|>


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

// Package metadata define the structure of the metadata supported by gRPC library.
// Please refer to https://grpc.io/docs/guides/wire.html for more information about custom-metadata.
package metadata // import "google.golang.org/grpc/metadata"

import (
	"fmt"
	"strings"

	"golang.org/x/net/context"
)

// DecodeKeyValue returns k, v, nil.  It is deprecated and should not be used.
func DecodeKeyValue(k, v string) (string, string, error) {
	return k, v, nil
}

// MD is a mapping from metadata keys to values. Users should use the following
// two convenience functions New and Pairs to generate MD.
type MD map[string][]string

// New creates an MD from a given key-value map.
//
// Only the following ASCII characters are allowed in keys:
//  - digits: 0-9
//  - uppercase letters: A-Z (normalized to lower)
//  - lowercase letters: a-z
//  - special characters: -_.
// Uppercase letters are automatically converted to lowercase.
//
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
// result in errors if set in metadata.
func New(m map[string]string) MD {
	md := MD{}
	for k, val := range m {
		key := strings.ToLower(k)
		md[key] = append(md[key], val)
	}
	return md
}

// Pairs returns an MD formed by the mapping of key, value ...
// Pairs panics if len(kv) is odd.
//
// Only the following ASCII characters are allowed in keys:
//  - digits: 0-9
//  - uppercase letters: A-Z (normalized to lower)
//  - lowercase letters: a-z
//  - special characters: -_.
// Uppercase letters are automatically converted to lowercase.
//
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
// result in errors if set in metadata.
func Pairs(kv ...string) MD {
	if len(kv)%2 == 1 {
		panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
	}
	md := MD{}
	var key string
	for i, s := range kv {
		if i%2 == 0 {
			key = strings.ToLower(s)
			continue
		}
		md[key] = append(md[key], s)
	}
	return md
}

// Len returns the number of items in md.
func (md MD) Len() int {
	return len(md)
}

// Copy returns a copy of md.
func (md MD) Copy() MD {
	return Join(md)
}

// Join joins any number of mds into a single MD.
// The order of values for each key is determined by the order in which
// the mds containing those values are presented to Join.
func Join(mds ...MD) MD {
	out := MD{}
	for _, md := range mds {
		for k, v := range md {
			out[k] = append(out[k], v...)
		}
	}
	return out
}

type mdIncomingKey struct{}
type mdOutgoingKey struct{}

// NewIncomingContext creates a new context with incoming md attached.
func NewIncomingContext(ctx context.Context, md MD) context.Context {
	return context.WithValue(ctx, mdIncomingKey{}, md)
}

// NewOutgoingContext creates a new context with outgoing md attached.
func NewOutgoingContext(ctx context.Context, md MD) context.Context {
	return context.WithValue(ctx, mdOutgoingKey{}, md)
}

// FromIncomingContext returns the incoming metadata in ctx if it exists.  The
// returned MD should not be modified. Writing to it may cause races.
// Modification should be made to copies of the returned MD.
func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
	md, ok = ctx.Value(mdIncomingKey{}).(MD)
	return
}

// FromOutgoingContext returns the outgoing metadata in ctx if it exists.  The
// returned MD should not be modified. Writing to it may cause races.
// Modification should be made to the copies of the returned MD.
func FromOutgoingContext(ctx context.Context) (md MD, ok bool) {
	md, ok = ctx.Value(mdOutgoingKey{}).(MD)
	return
}
<commit_msg>Documentation: update broken wire.html link in metadata package. (#1791)<commit_after>/*
 *
 * Copyright 2014 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Package metadata define the structure of the metadata supported by gRPC library.
// Please refer to https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
// for more information about custom-metadata.
package metadata // import "google.golang.org/grpc/metadata"

import (
	"fmt"
	"strings"

	"golang.org/x/net/context"
)

// DecodeKeyValue returns k, v, nil.  It is deprecated and should not be used.
func DecodeKeyValue(k, v string) (string, string, error) {
	return k, v, nil
}

// MD is a mapping from metadata keys to values. Users should use the following
// two convenience functions New and Pairs to generate MD.
type MD map[string][]string

// New creates an MD from a given key-value map.
//
// Only the following ASCII characters are allowed in keys:
//  - digits: 0-9
//  - uppercase letters: A-Z (normalized to lower)
//  - lowercase letters: a-z
//  - special characters: -_.
// Uppercase letters are automatically converted to lowercase.
//
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
// result in errors if set in metadata.
func New(m map[string]string) MD {
	md := MD{}
	for k, val := range m {
		key := strings.ToLower(k)
		md[key] = append(md[key], val)
	}
	return md
}

// Pairs returns an MD formed by the mapping of key, value ...
// Pairs panics if len(kv) is odd.
//
// Only the following ASCII characters are allowed in keys:
//  - digits: 0-9
//  - uppercase letters: A-Z (normalized to lower)
//  - lowercase letters: a-z
//  - special characters: -_.
// Uppercase letters are automatically converted to lowercase.
//
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
// result in errors if set in metadata.
func Pairs(kv ...string) MD {
	if len(kv)%2 == 1 {
		panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
	}
	md := MD{}
	var key string
	for i, s := range kv {
		if i%2 == 0 {
			key = strings.ToLower(s)
			continue
		}
		md[key] = append(md[key], s)
	}
	return md
}

// Len returns the number of items in md.
func (md MD) Len() int {
	return len(md)
}

// Copy returns a copy of md.
func (md MD) Copy() MD {
	return Join(md)
}

// Join joins any number of mds into a single MD.
// The order of values for each key is determined by the order in which
// the mds containing those values are presented to Join.
func Join(mds ...MD) MD {
	out := MD{}
	for _, md := range mds {
		for k, v := range md {
			out[k] = append(out[k], v...)
		}
	}
	return out
}

type mdIncomingKey struct{}
type mdOutgoingKey struct{}

// NewIncomingContext creates a new context with incoming md attached.
func NewIncomingContext(ctx context.Context, md MD) context.Context {
	return context.WithValue(ctx, mdIncomingKey{}, md)
}

// NewOutgoingContext creates a new context with outgoing md attached.
func NewOutgoingContext(ctx context.Context, md MD) context.Context {
	return context.WithValue(ctx, mdOutgoingKey{}, md)
}

// FromIncomingContext returns the incoming metadata in ctx if it exists.  The
// returned MD should not be modified. Writing to it may cause races.
// Modification should be made to copies of the returned MD.
func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
	md, ok = ctx.Value(mdIncomingKey{}).(MD)
	return
}

// FromOutgoingContext returns the outgoing metadata in ctx if it exists.  The
// returned MD should not be modified. Writing to it may cause races.
// Modification should be made to the copies of the returned MD.
func FromOutgoingContext(ctx context.Context) (md MD, ok bool) {
	md, ok = ctx.Value(mdOutgoingKey{}).(MD)
	return
}
<|endoftext|>