<commit_before>package dsl

import (
	"goa.design/goa/v3/eval"
	"goa.design/goa/v3/expr"
)

// Service defines a group of remotely accessible methods that are hosted
// together. The service DSL makes it possible to define the methods, their
// input and output as well as the errors they may return independently of the
// underlying transport (HTTP or gRPC). The transport specific DSLs defined by
// the HTTP and GRPC functions define the mapping between the input, output and
// error type attributes and the transport data (e.g. HTTP headers, HTTP bodies
// or gRPC messages).
//
// The Service expression is leveraged by the code generators to define the
// business layer service interface, the endpoint layer as well as the transport
// layer including input validation, marshalling and unmarshalling. It also
// affects the generated OpenAPI specification.
//
// Service is as a top level expression.
//
// Service accepts two arguments: the name of the service - which must be unique
// in the design package - and its defining DSL.
//
// Example:
//
//    var _ = Service("divider", func() {
//        Title("divider service") // optional
//
//        Error("Unauthorized") // error that apply to all the service methods
//        HTTP(func() {         // HTTP mapping for error responses
//            // Use HTTP status 401 for 'Unauthorized' errors.
//            Response("Unauthorized", StatusUnauthorized)
//        })
//
//        Method("divide", func() {   // Defines a service method.
//            Description("Divide divides two value.") // optional
//            Payload(DividePayload)                   // input type
//            Result(Float64)                          // output type
//            Error("DivisionByZero")                  // method specific error
//            // No HTTP mapping for "DivisionByZero" means default of status
//            // 400 and error struct serialized in HTTP response body.
//
//            HTTP(func() {      // Defines HTTP transport mapping.
//                GET("/div")    // HTTP verb and path
//                Param("a")     // query string parameter
//                Param("b")     // 'a' and 'b' are attributes of DividePayload.
//                // No 'Response' DSL means default of status 200 and result
//                // marshaled in HTTP response body.
//            })
//        })
//    })
//
func Service(name string, fn func()) *expr.ServiceExpr {
	if _, ok := eval.Current().(eval.TopExpr); !ok {
		eval.IncompatibleDSL()
		return nil
	}
	if s := expr.Root.Service(name); s != nil {
		eval.ReportError("service %#v is defined twice", name)
		return nil
	}
	s := &expr.ServiceExpr{Name: name, DSLFunc: fn}
	expr.Root.Services = append(expr.Root.Services, s)
	return s
}
<commit_msg>Update example in Service.go (#2799)<commit_after>package dsl

import (
	"goa.design/goa/v3/eval"
	"goa.design/goa/v3/expr"
)

// Service defines a group of remotely accessible methods that are hosted
// together. The service DSL makes it possible to define the methods, their
// input and output as well as the errors they may return independently of the
// underlying transport (HTTP or gRPC). The transport specific DSLs defined by
// the HTTP and GRPC functions define the mapping between the input, output and
// error type attributes and the transport data (e.g. HTTP headers, HTTP bodies
// or gRPC messages).
//
// The Service expression is leveraged by the code generators to define the
// business layer service interface, the endpoint layer as well as the transport
// layer including input validation, marshalling and unmarshalling. It also
// affects the generated OpenAPI specification.
//
// Service is as a top level expression.
//
// Service accepts two arguments: the name of the service - which must be unique
// in the design package - and its defining DSL.
//
// Example:
//
//    var _ = Service("divider", func() {
//        Description("divider service") // optional
//
//        Error("Unauthorized") // error that apply to all the service methods
//        HTTP(func() {         // HTTP mapping for error responses
//            // Use HTTP status 401 for 'Unauthorized' errors.
//            Response("Unauthorized", StatusUnauthorized)
//        })
//
//        Method("divide", func() {   // Defines a service method.
//            Description("Divide divides two value.") // optional
//            Payload(DividePayload)                   // input type
//            Result(Float64)                          // output type
//            Error("DivisionByZero")                  // method specific error
//            // No HTTP mapping for "DivisionByZero" means default of status
//            // 400 and error struct serialized in HTTP response body.
//
//            HTTP(func() {      // Defines HTTP transport mapping.
//                GET("/div")    // HTTP verb and path
//                Param("a")     // query string parameter
//                Param("b")     // 'a' and 'b' are attributes of DividePayload.
//                // No 'Response' DSL means default of status 200 and result
//                // marshaled in HTTP response body.
//            })
//        })
//    })
//
func Service(name string, fn func()) *expr.ServiceExpr {
	if _, ok := eval.Current().(eval.TopExpr); !ok {
		eval.IncompatibleDSL()
		return nil
	}
	if s := expr.Root.Service(name); s != nil {
		eval.ReportError("service %#v is defined twice", name)
		return nil
	}
	s := &expr.ServiceExpr{Name: name, DSLFunc: fn}
	expr.Root.Services = append(expr.Root.Services, s)
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
)

func ohai(attribute string) ([]byte, error) {
	buff := new(bytes.Buffer)
	cmd := exec.Command("ohai", attribute)
	var stdout io.ReadCloser
	var err error
	if stdout, err = cmd.StdoutPipe(); err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	if _, err := io.Copy(buff, stdout); err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}

func main() {
	addr := os.Getenv("PORT")
	if addr == "" {
		addr = "8000"
	}
	addr = ":" + addr

	log.Println("ohai there! serving ohai data on " + addr)
	log.Fatal(http.ListenAndServe(addr,
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			switch req.Method {
			case "GET":
				log.Println(req.Method, req.RequestURI)
				output, err := ohai(req.RequestURI[1:])
				if err != nil {
					log.Println(err)
				}
				w.Header().Set("Content-Length", strconv.Itoa(len(output)))
				w.Header().Set("Content-Type", "application/json")
				w.Write(output)
			default:
				w.WriteHeader(http.StatusMethodNotAllowed)
			}
		}),
	))
}
<commit_msg>catching up<commit_after>package main

import (
	"bytes"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
)

func ohai(attribute string) ([]byte, error) {
	buff := new(bytes.Buffer)
	cmd := exec.Command("ohai", attribute)
	var stdout io.ReadCloser
	var err error
	if stdout, err = cmd.StdoutPipe(); err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	if _, err := io.Copy(buff, stdout); err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}

func main() {
	addr := os.Getenv("PORT")
	if addr == "" {
		addr = "8000"
	}
	addr = ":" + addr

	log.Println("ohai there! serving ohai data on " + addr)
	log.Fatal(http.ListenAndServe(addr,
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			switch req.Method {
			case "GET":
				log.Println(req.Method, req.RequestURI)
				output, err := ohai(req.RequestURI[1:])
				if err != nil {
					log.Println(err)
				}
				w.Header().Set("Content-Length", strconv.Itoa(len(output)))
				w.Header().Set("Content-Type", "application/json")
				w.Write(output)
			default:
				w.WriteHeader(http.StatusMethodNotAllowed)
			}
		}),
	))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/t3rm1n4l/go-mega"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

const CACHEDIR = "cache"
var megaSession *mega.Mega

func handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		node, err := node(r.URL.Path)
		if err != nil {
			// XXX 404
			fmt.Print(err)
			return
		}
		switch node.GetType() {
		case mega.FOLDER, mega.ROOT:
			list(w, r, node)
		default:
			get(w, r, node)
		}

	case "PUT":
		put(w, r)
	}
}

func list(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	children, err := megaSession.FS.GetChildren(node)
	if err != nil {
		fmt.Print(err)
		// XXX 500
		return
	}

	fmt.Fprint(w, "")
	fmt.Fprintf(w, "
%s
", html.EscapeString(r.URL.Path))
	fmt.Fprint(w, "
")
	if node != megaSession.FS.GetRoot() {
		fmt.Fprintf(w, "
..")
	}
	for _, child := range children {
		var folder string
		if child.GetType() == mega.FOLDER {
			folder = "/"
		}
		fmt.Fprintf(w, "
%s%s",
			html.EscapeString(child.GetName()), folder,
			html.EscapeString(child.GetName()), folder)
	}
	fmt.Fprint(w, "
")
}

func get(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	hash := node.GetHash()
	tempfile := fmt.Sprintf("%s/%s", CACHEDIR, hash)
	err := megaSession.DownloadFile(node, tempfile, nil)
	if err != nil {
		log.Printf("download failed: %s", err)
		os.Remove(tempfile)
		return
	}
	file, err := os.Open(tempfile) // For read access.
	if err != nil {
		log.Printf("error opening %s: %s", tempfile, err)
		return
	}
	defer file.Close()
	_, err = io.Copy(w, file)
	if err != nil {
		log.Print(err)
		return
	}
}

func put(w http.ResponseWriter, r *http.Request) {
	cachefile := CACHEDIR + r.URL.Path
	dir, name := path.Split(cachefile)
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	fp, err := os.Create(cachefile)
	if err != nil {
		log.Print(err)
		return
	}
	_, err = io.Copy(fp, r.Body)
	if err != nil {
		log.Print(err)
		return
	}

	dirarray := strings.Split(r.URL.Path, "/")
	root := megaSession.FS.GetRoot()
	n, err := mkpath(dirarray[1:len(dirarray)-1], root)
	if err != nil {
		log.Print("mkpath", err)
		return
	}
	_, err = megaSession.UploadFile(cachefile, n, name, nil)
	if err != nil {
		log.Print("upload", err)
		return
	}
}

func mkpath(p []string, parent *mega.Node) (*mega.Node, error) {
	var n *mega.Node
	var err error
	l := len(p)

	if l == 1{
		n = parent
	} else {
		// if a/b/c then parent = mkpath(a/b)
		n, err = mkpath(p[:l-1], parent)
		if err != nil {
			return n, err
		}
	}
	return megaSession.CreateDir(p[l-1], n)
}

func node(url string) (*mega.Node, error) {
	trimmedPath := strings.Trim(url, "/")
	path := strings.Split(trimmedPath, "/")
	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if path[0] == "" {
		return root, nil
	} else {
		paths, err := megaSession.FS.PathLookup(root, path)
		if err != nil {
			// XXX should be 404
			return nil, err
		}
		// We only care about the last path.
		return paths[len(paths)-1], nil
	}
}

func main() {
	user := os.Getenv("MEGA_USER")
	pass := os.Getenv("MEGA_PASSWD")
	megaSession = mega.New()
	if err := megaSession.Login(user, pass); err != nil {
		log.Fatal(err)
	}
	if err := os.Mkdir(CACHEDIR, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
	}
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
<commit_msg>Don't create duplicate directories.<commit_after>package main

import (
	"fmt"
	"github.com/t3rm1n4l/go-mega"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

const CACHEDIR = "cache"
var megaSession *mega.Mega

func handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		node, err := node(r.URL.Path)
		if err != nil {
			// XXX 404
			fmt.Print(err)
			return
		}
		switch node.GetType() {
		case mega.FOLDER, mega.ROOT:
			list(w, r, node)
		default:
			get(w, r, node)
		}

	case "PUT":
		put(w, r)
	}
}

func list(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	children, err := megaSession.FS.GetChildren(node)
	if err != nil {
		fmt.Print(err)
		// XXX 500
		return
	}

	fmt.Fprint(w, "")
	fmt.Fprintf(w, "
%s
", html.EscapeString(r.URL.Path))
	fmt.Fprint(w, "
")
	if node != megaSession.FS.GetRoot() {
		fmt.Fprintf(w, "
..")
	}
	for _, child := range children {
		var folder string
		if child.GetType() == mega.FOLDER {
			folder = "/"
		}
		fmt.Fprintf(w, "
%s%s",
			html.EscapeString(child.GetName()), folder,
			html.EscapeString(child.GetName()), folder)
	}
	fmt.Fprint(w, "
")
}

func get(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	hash := node.GetHash()
	tempfile := fmt.Sprintf("%s/%s", CACHEDIR, hash)
	err := megaSession.DownloadFile(node, tempfile, nil)
	if err != nil {
		log.Printf("download failed: %s", err)
		os.Remove(tempfile)
		return
	}
	file, err := os.Open(tempfile) // For read access.
	if err != nil {
		log.Printf("error opening %s: %s", tempfile, err)
		return
	}
	defer file.Close()
	_, err = io.Copy(w, file)
	if err != nil {
		log.Print(err)
		return
	}
}

func put(w http.ResponseWriter, r *http.Request) {
	cachefile := CACHEDIR + r.URL.Path
	dir, name := path.Split(cachefile)
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	fp, err := os.Create(cachefile)
	if err != nil {
		log.Print(err)
		return
	}
	_, err = io.Copy(fp, r.Body)
	if err != nil {
		log.Print(err)
		return
	}

	dirarray := strings.Split(r.URL.Path, "/")
	root := megaSession.FS.GetRoot()
	n, err := mkpath(dirarray[1:len(dirarray)-1], root)
	if err != nil {
		log.Print("mkpath", err)
		return
	}
	_, err = megaSession.UploadFile(cachefile, n, name, nil)
	if err != nil {
		log.Print("upload", err)
		return
	}
}

func mkpath(p []string, parent *mega.Node) (*mega.Node, error) {
	var n *mega.Node
	var err error

	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if p[0] == "" {
		return root, nil
	}

	paths, err := megaSession.FS.PathLookup(root, p)
	if err == nil {
		// We only care about the last path.
		return paths[len(paths)-1], nil
	} else if err.Error() != "Object (typically, node or user) not found" {
		log.Printf("not exist: %#v\n", err)
		return nil, err
	}

	l := len(p)
	if l == 1{
		n = parent
	} else {
		// if a/b/c then parent = mkpath(a/b)
		n, err = mkpath(p[:l-1], parent)
		if err != nil {
			return n, err
		}
	}
	return megaSession.CreateDir(p[l-1], n)
}

func node(url string) (*mega.Node, error) {
	trimmedPath := strings.Trim(url, "/")
	path := strings.Split(trimmedPath, "/")
	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if path[0] == "" {
		return root, nil
	} else {
		paths, err := megaSession.FS.PathLookup(root, path)
		if err != nil {
			// XXX should be 404
			return nil, err
		}
		// We only care about the last path.
		return paths[len(paths)-1], nil
	}
}

func main() {
	user := os.Getenv("MEGA_USER")
	pass := os.Getenv("MEGA_PASSWD")
	megaSession = mega.New()
	if err := megaSession.Login(user, pass); err != nil {
		log.Fatal(err)
	}
	if err := os.Mkdir(CACHEDIR, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
	}
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
<|endoftext|>


<commit_before>package core

import (
	"crypto/sha256"
	"errors"
	ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr"
	"gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/OpenBazaar/jsonpb"
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/pb"
	"golang.org/x/net/context"
)

// ModeratorPointerID  moderator ipfs multihash
var ModeratorPointerID multihash.Multihash

// ErrNoListings - no listing error
// FIXME : This is not used anywhere
var ErrNoListings = errors.New("no listings to set moderators on")

func init() {
	modHash := sha256.Sum256([]byte("moderators"))
	encoded, err := multihash.Encode(modHash[:], multihash.SHA2_256)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash encode)")
	}
	mh, err := multihash.Cast(encoded)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash cast)")
	}
	ModeratorPointerID = mh
}

// IsModerator - Am I a moderator?
func (n *OpenBazaarNode) IsModerator() bool {
	profile, err := n.GetProfile()
	if err != nil {
		return false
	}
	return profile.Moderator
}

// SetSelfAsModerator - set self as a moderator
func (n *OpenBazaarNode) SetSelfAsModerator(moderator *pb.Moderator) error {
	if moderator != nil {
		if moderator.Fee == nil {
			return errors.New("Moderator must have a fee set")
		}
		if (int(moderator.Fee.FeeType) == 0 || int(moderator.Fee.FeeType) == 2) && moderator.Fee.FixedFee == nil {
			return errors.New("Fixed fee must be set when using a fixed fee type")
		}

		// Update profile
		profile, err := n.GetProfile()
		if err != nil {
			return err
		}

		var currencies []string
		settingsData, _ := n.Datastore.Settings().Get()
		if settingsData.PreferredCurrencies != nil {
			currencies = append(currencies, *settingsData.PreferredCurrencies...)
		} else {
			for ct := range n.Multiwallet {
				currencies = append(currencies, ct.CurrencyCode())
			}
		}
		for _, cc := range currencies {
			moderator.AcceptedCurrencies = append(moderator.AcceptedCurrencies, NormalizeCurrencyCode(cc))
		}

		profile.Moderator = true
		profile.ModeratorInfo = moderator
		err = n.UpdateProfile(&profile)
		if err != nil {
			return err
		}
	}

	// Publish pointer
	pointers, err := n.Datastore.Pointers().GetByPurpose(ipfs.MODERATOR)
	ctx := context.Background()
	if err != nil || len(pointers) == 0 {
		addr, err := ma.NewMultiaddr("/ipfs/" + n.IpfsNode.Identity.Pretty())
		if err != nil {
			return err
		}
		pointer, err := ipfs.NewPointer(ModeratorPointerID, 64, addr, []byte(n.IpfsNode.Identity.Pretty()))
		if err != nil {
			return err
		}
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointer)
		pointer.Purpose = ipfs.MODERATOR
		err = n.Datastore.Pointers().Put(pointer)
		if err != nil {
			return err
		}
	} else {
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointers[0])
	}
	return nil
}

// RemoveSelfAsModerator - relinquish moderatorship
func (n *OpenBazaarNode) RemoveSelfAsModerator() error {
	// Update profile
	profile, err := n.GetProfile()
	if err != nil {
		return err
	}
	profile.Moderator = false
	err = n.UpdateProfile(&profile)
	if err != nil {
		return err
	}

	// Delete pointer from database
	err = n.Datastore.Pointers().DeleteAll(ipfs.MODERATOR)
	if err != nil {
		return err
	}
	return nil
}

// GetModeratorFee - fetch moderator fee
func (n *OpenBazaarNode) GetModeratorFee(transactionTotal uint64, paymentCoin, currencyCode string) (uint64, error) {
	file, err := ioutil.ReadFile(path.Join(n.RepoPath, "root", "profile.json"))
	if err != nil {
		return 0, err
	}
	profile := new(pb.Profile)
	err = jsonpb.UnmarshalString(string(file), profile)
	if err != nil {
		return 0, err
	}

	switch profile.ModeratorInfo.Fee.FeeType {
	case pb.Moderator_Fee_PERCENTAGE:
		return uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100)), nil
	case pb.Moderator_Fee_FIXED:

		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			if profile.ModeratorInfo.Fee.FixedFee.Amount >= transactionTotal {
				return 0, errors.New("Fixed moderator fee exceeds transaction amount")
			}
			return profile.ModeratorInfo.Fee.FixedFee.Amount, nil
		}
		fee, err := n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
		if err != nil {
			return 0, err
		} else if fee >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fee, err

	case pb.Moderator_Fee_FIXED_PLUS_PERCENTAGE:
		var fixed uint64
		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			fixed = profile.ModeratorInfo.Fee.FixedFee.Amount
		} else {
			fixed, err = n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
			if err != nil {
				return 0, err
			}
		}
		percentage := uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100))
		if fixed+percentage >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fixed + percentage, nil
	default:
		return 0, errors.New("Unrecognized fee type")
	}
}

// SetCurrencyOnListings - set currencies accepted for a listing
func (n *OpenBazaarNode) SetCurrencyOnListings(currencies []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}

	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() && filepath.Ext(p) == ".json" {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}
			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}

			sl.Listing.Metadata.AcceptedCurrencies = currencies
			n.UpdateListing(sl.Listing)

			return nil
		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	return nil
}

// SetModeratorsOnListings - set moderators for a listing
func (n *OpenBazaarNode) SetModeratorsOnListings(moderators []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}
	hashes := make(map[string]string)
	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}
			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}
			coupons, err := n.Datastore.Coupons().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}
			couponMap := make(map[string]string)
			for _, c := range coupons {
				couponMap[c.Hash] = c.Code
			}
			for _, coupon := range sl.Listing.Coupons {
				code, ok := couponMap[coupon.GetHash()]
				if ok {
					coupon.Code = &pb.Listing_Coupon_DiscountCode{DiscountCode: code}
				}
			}

			sl.Listing.Moderators = moderators
			sl, err = n.SignListing(sl.Listing)
			if err != nil {
				return err
			}
			m := jsonpb.Marshaler{
				EnumsAsInts:  false,
				EmitDefaults: false,
				Indent:       "    ",
				OrigName:     false,
			}
			fi, err := os.Create(p)
			if err != nil {
				return err
			}
			out, err := m.MarshalToString(sl)
			if err != nil {
				return err
			}
			if _, err := fi.WriteString(out); err != nil {
				return err
			}
			hash, err := ipfs.GetHashOfFile(n.IpfsNode, p)
			if err != nil {
				return err
			}
			hashes[sl.Listing.Slug] = hash

			return nil
		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	// Update moderators and hashes on index
	updater := func(listing *ListingData) error {
		listing.ModeratorIDs = moderators
		if hash, ok := hashes[listing.Slug]; ok {
			listing.Hash = hash
		}
		return nil
	}
	return n.UpdateEachListingOnIndex(updater)
}

// NotifyModerators - notify moderators(peers)
func (n *OpenBazaarNode) NotifyModerators(moderators []string) error {
	settings, err := n.Datastore.Settings().Get()
	if err != nil {
		return err
	}
	currentMods := make(map[string]bool)
	if settings.StoreModerators != nil {
		for _, mod := range *settings.StoreModerators {
			currentMods[mod] = true
		}
	}
	var addedMods []string
	for _, mod := range moderators {
		if !currentMods[mod] {
			addedMods = append(addedMods, mod)
		} else {
			delete(currentMods, mod)
		}
	}

	removedMods := currentMods

	for _, mod := range addedMods {
		go n.SendModeratorAdd(mod)
	}
	for mod := range removedMods {
		go n.SendModeratorRemove(mod)
	}
	return nil
}
<commit_msg>Handle coupons and inventory<commit_after>package core

import (
	"crypto/sha256"
	"errors"
	ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr"
	"gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/OpenBazaar/jsonpb"
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/pb"
	"golang.org/x/net/context"
)

// ModeratorPointerID  moderator ipfs multihash
var ModeratorPointerID multihash.Multihash

// ErrNoListings - no listing error
// FIXME : This is not used anywhere
var ErrNoListings = errors.New("no listings to set moderators on")

func init() {
	modHash := sha256.Sum256([]byte("moderators"))
	encoded, err := multihash.Encode(modHash[:], multihash.SHA2_256)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash encode)")
	}
	mh, err := multihash.Cast(encoded)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash cast)")
	}
	ModeratorPointerID = mh
}

// IsModerator - Am I a moderator?
func (n *OpenBazaarNode) IsModerator() bool {
	profile, err := n.GetProfile()
	if err != nil {
		return false
	}
	return profile.Moderator
}

// SetSelfAsModerator - set self as a moderator
func (n *OpenBazaarNode) SetSelfAsModerator(moderator *pb.Moderator) error {
	if moderator != nil {
		if moderator.Fee == nil {
			return errors.New("Moderator must have a fee set")
		}
		if (int(moderator.Fee.FeeType) == 0 || int(moderator.Fee.FeeType) == 2) && moderator.Fee.FixedFee == nil {
			return errors.New("Fixed fee must be set when using a fixed fee type")
		}

		// Update profile
		profile, err := n.GetProfile()
		if err != nil {
			return err
		}

		var currencies []string
		settingsData, _ := n.Datastore.Settings().Get()
		if settingsData.PreferredCurrencies != nil {
			currencies = append(currencies, *settingsData.PreferredCurrencies...)
		} else {
			for ct := range n.Multiwallet {
				currencies = append(currencies, ct.CurrencyCode())
			}
		}
		for _, cc := range currencies {
			moderator.AcceptedCurrencies = append(moderator.AcceptedCurrencies, NormalizeCurrencyCode(cc))
		}

		profile.Moderator = true
		profile.ModeratorInfo = moderator
		err = n.UpdateProfile(&profile)
		if err != nil {
			return err
		}
	}

	// Publish pointer
	pointers, err := n.Datastore.Pointers().GetByPurpose(ipfs.MODERATOR)
	ctx := context.Background()
	if err != nil || len(pointers) == 0 {
		addr, err := ma.NewMultiaddr("/ipfs/" + n.IpfsNode.Identity.Pretty())
		if err != nil {
			return err
		}
		pointer, err := ipfs.NewPointer(ModeratorPointerID, 64, addr, []byte(n.IpfsNode.Identity.Pretty()))
		if err != nil {
			return err
		}
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointer)
		pointer.Purpose = ipfs.MODERATOR
		err = n.Datastore.Pointers().Put(pointer)
		if err != nil {
			return err
		}
	} else {
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointers[0])
	}
	return nil
}

// RemoveSelfAsModerator - relinquish moderatorship
func (n *OpenBazaarNode) RemoveSelfAsModerator() error {
	// Update profile
	profile, err := n.GetProfile()
	if err != nil {
		return err
	}
	profile.Moderator = false
	err = n.UpdateProfile(&profile)
	if err != nil {
		return err
	}

	// Delete pointer from database
	err = n.Datastore.Pointers().DeleteAll(ipfs.MODERATOR)
	if err != nil {
		return err
	}
	return nil
}

// GetModeratorFee - fetch moderator fee
func (n *OpenBazaarNode) GetModeratorFee(transactionTotal uint64, paymentCoin, currencyCode string) (uint64, error) {
	file, err := ioutil.ReadFile(path.Join(n.RepoPath, "root", "profile.json"))
	if err != nil {
		return 0, err
	}
	profile := new(pb.Profile)
	err = jsonpb.UnmarshalString(string(file), profile)
	if err != nil {
		return 0, err
	}

	switch profile.ModeratorInfo.Fee.FeeType {
	case pb.Moderator_Fee_PERCENTAGE:
		return uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100)), nil
	case pb.Moderator_Fee_FIXED:

		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			if profile.ModeratorInfo.Fee.FixedFee.Amount >= transactionTotal {
				return 0, errors.New("Fixed moderator fee exceeds transaction amount")
			}
			return profile.ModeratorInfo.Fee.FixedFee.Amount, nil
		}
		fee, err := n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
		if err != nil {
			return 0, err
		} else if fee >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fee, err

	case pb.Moderator_Fee_FIXED_PLUS_PERCENTAGE:
		var fixed uint64
		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			fixed = profile.ModeratorInfo.Fee.FixedFee.Amount
		} else {
			fixed, err = n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
			if err != nil {
				return 0, err
			}
		}
		percentage := uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100))
		if fixed+percentage >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fixed + percentage, nil
	default:
		return 0, errors.New("Unrecognized fee type")
	}
}

// SetCurrencyOnListings - set currencies accepted for a listing
func (n *OpenBazaarNode) SetCurrencyOnListings(currencies []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}

	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() && filepath.Ext(p) == ".json" {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}

			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}

			sl.Listing.Metadata.AcceptedCurrencies = currencies

			savedCoupons, err := n.Datastore.Coupons().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}
			for _, coupon := range sl.Listing.Coupons {
				for _, c := range savedCoupons {
					if coupon.GetHash() == c.Hash {
						coupon.Code = &pb.Listing_Coupon_DiscountCode{c.Code}
						break
					}
				}
			}
			if sl.Listing.Metadata != nil && sl.Listing.Metadata.Version == 1 {
				for _, so := range sl.Listing.ShippingOptions {
					for _, ser := range so.Services {
						ser.AdditionalItemPrice = ser.Price
					}
				}
			}

			inventory, err := n.Datastore.Inventory().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}

			// Build the inventory list
			for variant, count := range inventory {
				for i, s := range sl.Listing.Item.Skus {
					if variant == i {
						s.Quantity = count
						break
					}
				}
			}

			n.UpdateListing(sl.Listing)

		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	return nil
}

// SetModeratorsOnListings - set moderators for a listing
func (n *OpenBazaarNode) SetModeratorsOnListings(moderators []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}
	hashes := make(map[string]string)
	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}
			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}
			coupons, err := n.Datastore.Coupons().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}
			couponMap := make(map[string]string)
			for _, c := range coupons {
				couponMap[c.Hash] = c.Code
			}
			for _, coupon := range sl.Listing.Coupons {
				code, ok := couponMap[coupon.GetHash()]
				if ok {
					coupon.Code = &pb.Listing_Coupon_DiscountCode{DiscountCode: code}
				}
			}

			sl.Listing.Moderators = moderators
			sl, err = n.SignListing(sl.Listing)
			if err != nil {
				return err
			}
			m := jsonpb.Marshaler{
				EnumsAsInts:  false,
				EmitDefaults: false,
				Indent:       "    ",
				OrigName:     false,
			}
			fi, err := os.Create(p)
			if err != nil {
				return err
			}
			out, err := m.MarshalToString(sl)
			if err != nil {
				return err
			}
			if _, err := fi.WriteString(out); err != nil {
				return err
			}
			hash, err := ipfs.GetHashOfFile(n.IpfsNode, p)
			if err != nil {
				return err
			}
			hashes[sl.Listing.Slug] = hash

			return nil
		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	// Update moderators and hashes on index
	updater := func(listing *ListingData) error {
		listing.ModeratorIDs = moderators
		if hash, ok := hashes[listing.Slug]; ok {
			listing.Hash = hash
		}
		return nil
	}
	return n.UpdateEachListingOnIndex(updater)
}

// NotifyModerators - notify moderators(peers)
func (n *OpenBazaarNode) NotifyModerators(moderators []string) error {
	settings, err := n.Datastore.Settings().Get()
	if err != nil {
		return err
	}
	currentMods := make(map[string]bool)
	if settings.StoreModerators != nil {
		for _, mod := range *settings.StoreModerators {
			currentMods[mod] = true
		}
	}
	var addedMods []string
	for _, mod := range moderators {
		if !currentMods[mod] {
			addedMods = append(addedMods, mod)
		} else {
			delete(currentMods, mod)
		}
	}

	removedMods := currentMods

	for _, mod := range addedMods {
		go n.SendModeratorAdd(mod)
	}
	for mod := range removedMods {
		go n.SendModeratorRemove(mod)
	}
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Added tests for location passing into delegates.<commit_after><|endoftext|>


<commit_before>package buffer

import (
	"bytes"
	"encoding/gob"
	"io"
)

type chain struct {
	Buf     Buffer
	Next    Buffer
	HasNext bool
}

// NewMulti returns a Buffer which is the logical concatenation of the passed buffers.
func NewMulti(buffers ...Buffer) Buffer {
	if len(buffers) == 0 {
		return nil
	} else if len(buffers) == 1 {
		return buffers[0]
	}

	buf := &chain{
		Buf:     buffers[0],
		Next:    NewMulti(buffers[1:]...),
		HasNext: len(buffers[1:]) != 0,
	}

	buf.Defrag()

	return buf
}

func (buf *chain) Reset() {
	if buf.HasNext {
		buf.Next.Reset()
	}
	buf.Buf.Reset()
}

func (buf *chain) Cap() (n int64) {
	if buf.HasNext {
		Next := buf.Next.Cap()
		if buf.Buf.Cap() > MAXINT64-Next {
			return MAXINT64
		}
		return buf.Buf.Cap() + Next
	}

	return buf.Buf.Cap()
}

func (buf *chain) Len() (n int64) {
	if buf.HasNext {
		Next := buf.Next.Len()
		if buf.Buf.Len() > MAXINT64-Next {
			return MAXINT64
		}
		return buf.Buf.Len() + Next
	}

	return buf.Buf.Len()
}

func (buf *chain) Defrag() {
	for !Full(buf.Buf) && buf.HasNext && !Empty(buf.Next) {
		r := io.LimitReader(buf.Next, Gap(buf.Buf))
		if _, err := io.Copy(buf.Buf, r); err != nil && err != io.EOF {
			return
		}
	}
}

func (buf *chain) Read(p []byte) (n int, err error) {
	n, err = buf.Buf.Read(p)
	p = p[n:]
	if len(p) > 0 && buf.HasNext && (err == nil || err == io.EOF) {
		m, err := buf.Next.Read(p)
		n += m
		if err != nil {
			return n, err
		}
	}

	buf.Defrag()

	return n, err
}

func (buf *chain) Write(p []byte) (n int, err error) {
	if n, err = buf.Buf.Write(p); err == io.ErrShortWrite && buf.HasNext {
		err = nil
	}
	p = p[n:]
	if len(p) > 0 && buf.HasNext && err == nil {
		m, err := buf.Next.Write(p)
		n += m
		if err != nil {
			return n, err
		}
	}
	return n, err
}

func init() {
	gob.Register(&chain{})
}

func (buf *chain) MarshalBinary() ([]byte, error) {
	b := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(b)
	enc.Encode(&buf.Buf)
	enc.Encode(buf.HasNext)
	if buf.HasNext {
		enc.Encode(&buf.Next)
	}
	return b.Bytes(), nil
}

func (buf *chain) UnmarshalBinary(data []byte) error {
	b := bytes.NewBuffer(data)
	dec := gob.NewDecoder(b)
	if err := dec.Decode(&buf.Buf); err != nil {
		return err
	}
	if err := dec.Decode(&buf.HasNext); err != nil {
		return err
	}
	if buf.HasNext {
		if err := dec.Decode(&buf.Next); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>HasNext is not req. it is implicitly true since we only create a chain when we have another buffer<commit_after>package buffer

import (
	"bytes"
	"encoding/gob"
	"io"
)

type chain struct {
	Buf  Buffer
	Next Buffer
}

// NewMulti returns a Buffer which is the logical concatenation of the passed buffers.
// If no buffers are passed, the returned Buffer is nil.
func NewMulti(buffers ...Buffer) Buffer {
	if len(buffers) == 0 {
		return nil
	} else if len(buffers) == 1 {
		return buffers[0]
	}

	buf := &chain{
		Buf:  buffers[0],
		Next: NewMulti(buffers[1:]...),
	}

	buf.Defrag()

	return buf
}

func (buf *chain) Reset() {
	buf.Next.Reset()
	buf.Buf.Reset()
}

func (buf *chain) Cap() (n int64) {
	Next := buf.Next.Cap()
	if buf.Buf.Cap() > MAXINT64-Next {
		return MAXINT64
	}
	return buf.Buf.Cap() + Next
}

func (buf *chain) Len() (n int64) {
	Next := buf.Next.Len()
	if buf.Buf.Len() > MAXINT64-Next {
		return MAXINT64
	}
	return buf.Buf.Len() + Next
}

func (buf *chain) Defrag() {
	for !Full(buf.Buf) && !Empty(buf.Next) {
		r := io.LimitReader(buf.Next, Gap(buf.Buf))
		if _, err := io.Copy(buf.Buf, r); err != nil && err != io.EOF {
			return
		}
	}
}

func (buf *chain) Read(p []byte) (n int, err error) {
	n, err = buf.Buf.Read(p)
	p = p[n:]
	if len(p) > 0 && (err == nil || err == io.EOF) {
		m, err := buf.Next.Read(p)
		n += m
		if err != nil {
			return n, err
		}
	}

	buf.Defrag()

	return n, err
}

func (buf *chain) Write(p []byte) (n int, err error) {
	if n, err = buf.Buf.Write(p); err == io.ErrShortWrite {
		err = nil
	}
	p = p[n:]
	if len(p) > 0 && err == nil {
		m, err := buf.Next.Write(p)
		n += m
		if err != nil {
			return n, err
		}
	}
	return n, err
}

func init() {
	gob.Register(&chain{})
}

func (buf *chain) MarshalBinary() ([]byte, error) {
	b := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(b)
	if err := enc.Encode(&buf.Buf); err != nil {
		return nil, err
	}
	if err := enc.Encode(&buf.Next); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}

func (buf *chain) UnmarshalBinary(data []byte) error {
	b := bytes.NewBuffer(data)
	dec := gob.NewDecoder(b)
	if err := dec.Decode(&buf.Buf); err != nil {
		return err
	}
	if err := dec.Decode(&buf.Next); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"io"
)

type Multi []Logger

func NewMulti(ll ...Logger) *Multi {
	return (*Multi)(&ll)
}

func (m *Multi) Add(ll ...Logger) {
	for _, l := range ll {
		if l != nil {
			*m = append(*m, l)
		}
	}
}
func (m Multi) Write(b []byte) (int, error) {
	ws := make([]io.Writer, 0, len(m))
	for _, l := range m {
		ws = append(ws, l)
	}
	return io.MultiWriter(ws...).Write(b)
}
func (m Multi) Log(msg ...interface{}) {
	for _, l := range m {
		l.Log(msg...)
	}
}
func (m Multi) Logf(msg string, args ...interface{}) {
	for _, l := range m {
		l.Logf(msg, args...)
	}
}
func (m Multi) Error(err error) {
	for _, l := range m {
		l.Error(err)
	}
}
func (m Multi) Errorf(msg string, args ...interface{}) {
	for _, l := range m {
		l.Errorf(msg, args...)
	}
}
func (m Multi) Panic(msg interface{}) {
	for _, l := range m {
		func() {
			defer recover() //Anywhere else this would be evil
			l.Panic(msg)
		}()
	}
	panic(msg)
}
func (m Multi) Panicf(msg string, args ...interface{}) {
	for _, l := range m {
		func() {
			defer recover() //Anywhere else this would be evil
			l.Panic(msg)
		}()
	}
	panic(fmt.Sprintf(msg, args...))
}
<commit_msg>Fix multi Panicf<commit_after>package log

import (
	"fmt"
	"io"
)

type Multi []Logger

func NewMulti(ll ...Logger) *Multi {
	return (*Multi)(&ll)
}

func (m *Multi) Add(ll ...Logger) {
	for _, l := range ll {
		if l != nil {
			*m = append(*m, l)
		}
	}
}
func (m Multi) Write(b []byte) (int, error) {
	ws := make([]io.Writer, 0, len(m))
	for _, l := range m {
		ws = append(ws, l)
	}
	return io.MultiWriter(ws...).Write(b)
}
func (m Multi) Log(msg ...interface{}) {
	for _, l := range m {
		l.Log(msg...)
	}
}
func (m Multi) Logf(msg string, args ...interface{}) {
	for _, l := range m {
		l.Logf(msg, args...)
	}
}
func (m Multi) Error(err error) {
	for _, l := range m {
		l.Error(err)
	}
}
func (m Multi) Errorf(msg string, args ...interface{}) {
	for _, l := range m {
		l.Errorf(msg, args...)
	}
}
func (m Multi) Panic(msg interface{}) {
	for _, l := range m {
		func() {
			defer recover() //Anywhere else this would be evil
			l.Panic(msg)
		}()
	}
	panic(msg)
}
func (m Multi) Panicf(msg string, args ...interface{}) {
	for _, l := range m {
		func() {
			defer recover() //Anywhere else this would be evil
			l.Panicf(msg, args...)
		}()
	}
	panic(fmt.Sprintf(msg, args...))
}
<|endoftext|>


<commit_before>package model

import (
	"regexp"
	"strings"
)

type Document struct {
	Title       string
	Description string
	Content     string
	MetaData    string
	Hash        string

	pattern  DocumentPattern
	rawLines []string
}

func CreateDocument(repositoryItem *RepositoryItem) *Document {
	doc := Document{
		Hash:     repositoryItem.GetHash(),
		pattern:  NewDocumentPattern(),
		rawLines: repositoryItem.GetLines(),
	}

	// parse
	return doc.parse()
}

func getLastElement(array []string) string {
	if array == nil {
		return ""
	}

	return array[len(array)-1]
}

func (doc *Document) parse() *Document {
	return doc.setTitle()
}

func (doc *Document) setTitle() *Document {
	titleLocation := doc.locateTitle()
	if !titleLocation.Found {
		return doc
	}

	doc.Title = getLastElement(titleLocation.Matches)
	return doc.setDescription()
}

func (doc *Document) setDescription() *Document {
	descriptionLocation := doc.locateDescription()
	if !descriptionLocation.Found {
		return doc
	}

	doc.Description = getLastElement(descriptionLocation.Matches)
	return doc.setContent()
}

func (doc *Document) setContent() *Document {
	contentLocation := doc.locateContent()
	if !contentLocation.Found {
		return doc
	}

	doc.Content = strings.Join(contentLocation.Matches, "\n")
	return doc
}

func (doc *Document) setMetaData() *Document {
	metaDataLocation := doc.locateMetaData()
	if !metaDataLocation.Found {
		return doc
	}

	doc.MetaData = strings.Join(metaDataLocation.Matches, "\n")
	return doc
}

type DocumentPattern struct {
	EmptyLine      regexp.Regexp
	Title          regexp.Regexp
	Description    regexp.Regexp
	HorizontalRule regexp.Regexp
	MetaData       regexp.Regexp
}

func NewDocumentPattern() DocumentPattern {
	emptyLineRegexp := regexp.MustCompile("^\\s*$")
	titleRegexp := regexp.MustCompile("\\s*#\\s*(.+)")
	descriptionRegexp := regexp.MustCompile("^\\w.+")
	horizontalRuleRegexp := regexp.MustCompile("^-{2,}")
	metaDataRegexp := regexp.MustCompile("^(\\w+):\\s*(\\w.+)$")

	return DocumentPattern{
		EmptyLine:      *emptyLineRegexp,
		Title:          *titleRegexp,
		Description:    *descriptionRegexp,
		HorizontalRule: *horizontalRuleRegexp,
		MetaData:       *metaDataRegexp,
	}
}

type LineSet struct {
	Start int
	End   int
}

func NewLineSet(start int, end int) LineSet {
	return LineSet{
		Start: start,
		End:   end,
	}
}

type MatchResult struct {
	Found   bool
	Lines   LineSet
	Matches []string
}

func Found(firstLine int, lastLine int, matches []string) *MatchResult {
	return &MatchResult{
		Found:   true,
		Lines:   NewLineSet(firstLine, lastLine),
		Matches: matches,
	}
}

func NotFound() *MatchResult {
	return &MatchResult{
		Found: false,
		Lines: NewLineSet(-1, -1),
	}
}

func IsMatch(line string, pattern regexp.Regexp) (isMatch bool, matches []string) {
	matches = pattern.FindStringSubmatch(line)
	return matches != nil, matches
}

// Check if the current Document contains a title
func (doc *Document) locateTitle() *MatchResult {

	// In order to be the "title" the line must either
	// be empty or match the title pattern.

	for lineNumber, line := range doc.rawLines {

		lineMatchesTitlePattern, matches := IsMatch(line, doc.pattern.Title)
		if lineMatchesTitlePattern {
			return Found(lineNumber, lineNumber, matches)
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			break
		}
	}

	return NotFound()
}

// Check if the current Document contains a description
func (doc *Document) locateDescription() *MatchResult {

	// The description must be preceeded by a title
	title := doc.locateTitle()
	if !title.Found {
		return NotFound()
	}

	// If the document has no more lines than the line
	// in which the title has been located, there
	// will be no room for a description
	startLine := title.Lines.Start + 1
	if len(doc.rawLines) <= startLine {
		return NotFound()
	}

	// In order to be a "description" the line must either
	// be empty or match the description pattern.
	for lineNumber, line := range doc.rawLines[startLine:] {

		lineMatchesDescriptionPattern, matches := IsMatch(line, doc.pattern.Description)
		if lineMatchesDescriptionPattern {
			return Found(lineNumber, lineNumber, matches)
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			break
		}
	}

	return NotFound()
}

// Check if the current Document contains meta data
func (doc *Document) locateMetaData() *MatchResult {

	// Find the last horizontal rule in the document
	lastFoundHorizontalRulePosition := -1
	for lineNumber, line := range doc.rawLines {

		lineMatchesHorizontalRulePattern := doc.pattern.HorizontalRule.MatchString(line)
		if lineMatchesHorizontalRulePattern {
			lastFoundHorizontalRulePosition = lineNumber
		}

	}

	// If there is no horizontal rule there is no meta data
	if lastFoundHorizontalRulePosition == -1 {
		return NotFound()
	}

	// If the document has no more lines than
	// the last found horizontal rule there is no
	// room for meta data
	metaDataStartLine := lastFoundHorizontalRulePosition + 1
	if len(doc.rawLines) <= metaDataStartLine {
		return NotFound()
	}

	// Check if the last horizontal rule is followed
	// either by white space or be meta data
	for _, line := range doc.rawLines[metaDataStartLine:] {

		lineMatchesMetaDataPattern := doc.pattern.MetaData.MatchString(line)
		if lineMatchesMetaDataPattern {

			endLine := len(doc.rawLines) - 1
			return Found(metaDataStartLine, endLine, doc.rawLines[metaDataStartLine:endLine])

		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			return NotFound()
		}

	}

	return NotFound()
}

// Check if the current Document contains content
func (doc *Document) locateContent() *MatchResult {

	// Content must be preceeded by a description
	description := doc.locateDescription()
	if !description.Found {
		return NotFound()
	}

	// If the document has no more lines than the line
	// in which the description has been located, there
	// will be no room for content
	startLine := description.Lines.Start + 1
	if len(doc.rawLines) <= startLine {
		return NotFound()
	}

	// If the document contains meta data
	// the content will be between the description
	// and the meta data. If not the content
	// will go up to the end of the document.
	endLine := 0
	metaData := doc.locateMetaData()
	if metaData.Found {
		endLine = metaData.Lines.Start - 1
	} else {
		endLine = len(doc.rawLines) - 1
	}

	// All lines between the start- and endLine are content
	return Found(startLine, endLine, doc.rawLines[startLine:endLine])
}
<commit_msg>The meta data was not being displayed because the setMetaData function was not called<commit_after>package model

import (
	"regexp"
	"strings"
)

type Document struct {
	Title       string
	Description string
	Content     string
	MetaData    string
	Hash        string

	pattern  DocumentPattern
	rawLines []string
}

func CreateDocument(repositoryItem *RepositoryItem) *Document {
	doc := Document{
		Hash:     repositoryItem.GetHash(),
		pattern:  NewDocumentPattern(),
		rawLines: repositoryItem.GetLines(),
	}

	// parse
	return doc.parse()
}

func getLastElement(array []string) string {
	if array == nil {
		return ""
	}

	return array[len(array)-1]
}

func (doc *Document) parse() *Document {
	return doc.setTitle()
}

func (doc *Document) setTitle() *Document {
	titleLocation := doc.locateTitle()
	if !titleLocation.Found {
		return doc
	}

	doc.Title = getLastElement(titleLocation.Matches)
	return doc.setDescription()
}

func (doc *Document) setDescription() *Document {
	descriptionLocation := doc.locateDescription()
	if !descriptionLocation.Found {
		return doc
	}

	doc.Description = getLastElement(descriptionLocation.Matches)
	return doc.setContent()
}

func (doc *Document) setContent() *Document {
	contentLocation := doc.locateContent()
	if !contentLocation.Found {
		return doc
	}

	doc.Content = strings.Join(contentLocation.Matches, "\n")
	return doc.setMetaData()
}

func (doc *Document) setMetaData() *Document {
	metaDataLocation := doc.locateMetaData()
	if !metaDataLocation.Found {
		return doc
	}

	doc.MetaData = strings.Join(metaDataLocation.Matches, "\n")
	return doc
}

type DocumentPattern struct {
	EmptyLine      regexp.Regexp
	Title          regexp.Regexp
	Description    regexp.Regexp
	HorizontalRule regexp.Regexp
	MetaData       regexp.Regexp
}

func NewDocumentPattern() DocumentPattern {
	emptyLineRegexp := regexp.MustCompile("^\\s*$")
	titleRegexp := regexp.MustCompile("\\s*#\\s*(.+)")
	descriptionRegexp := regexp.MustCompile("^\\w.+")
	horizontalRuleRegexp := regexp.MustCompile("^-{2,}")
	metaDataRegexp := regexp.MustCompile("^(\\w+):\\s*(\\w.+)$")

	return DocumentPattern{
		EmptyLine:      *emptyLineRegexp,
		Title:          *titleRegexp,
		Description:    *descriptionRegexp,
		HorizontalRule: *horizontalRuleRegexp,
		MetaData:       *metaDataRegexp,
	}
}

type LineSet struct {
	Start int
	End   int
}

func NewLineSet(start int, end int) LineSet {
	return LineSet{
		Start: start,
		End:   end,
	}
}

type MatchResult struct {
	Found   bool
	Lines   LineSet
	Matches []string
}

func Found(firstLine int, lastLine int, matches []string) *MatchResult {
	return &MatchResult{
		Found:   true,
		Lines:   NewLineSet(firstLine, lastLine),
		Matches: matches,
	}
}

func NotFound() *MatchResult {
	return &MatchResult{
		Found: false,
		Lines: NewLineSet(-1, -1),
	}
}

func IsMatch(line string, pattern regexp.Regexp) (isMatch bool, matches []string) {
	matches = pattern.FindStringSubmatch(line)
	return matches != nil, matches
}

// Check if the current Document contains a title
func (doc *Document) locateTitle() *MatchResult {

	// In order to be the "title" the line must either
	// be empty or match the title pattern.

	for lineNumber, line := range doc.rawLines {

		lineMatchesTitlePattern, matches := IsMatch(line, doc.pattern.Title)
		if lineMatchesTitlePattern {
			return Found(lineNumber, lineNumber, matches)
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			break
		}
	}

	return NotFound()
}

// Check if the current Document contains a description
func (doc *Document) locateDescription() *MatchResult {

	// The description must be preceeded by a title
	title := doc.locateTitle()
	if !title.Found {
		return NotFound()
	}

	// If the document has no more lines than the line
	// in which the title has been located, there
	// will be no room for a description
	startLine := title.Lines.Start + 1
	if len(doc.rawLines) <= startLine {
		return NotFound()
	}

	// In order to be a "description" the line must either
	// be empty or match the description pattern.
	for lineNumber, line := range doc.rawLines[startLine:] {

		lineMatchesDescriptionPattern, matches := IsMatch(line, doc.pattern.Description)
		if lineMatchesDescriptionPattern {
			return Found(lineNumber, lineNumber, matches)
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			break
		}
	}

	return NotFound()
}

// Check if the current Document contains meta data
func (doc *Document) locateMetaData() *MatchResult {

	// Find the last horizontal rule in the document
	lastFoundHorizontalRulePosition := -1
	for lineNumber, line := range doc.rawLines {

		lineMatchesHorizontalRulePattern := doc.pattern.HorizontalRule.MatchString(line)
		if lineMatchesHorizontalRulePattern {
			lastFoundHorizontalRulePosition = lineNumber
		}

	}

	// If there is no horizontal rule there is no meta data
	if lastFoundHorizontalRulePosition == -1 {
		return NotFound()
	}

	// If the document has no more lines than
	// the last found horizontal rule there is no
	// room for meta data
	metaDataStartLine := lastFoundHorizontalRulePosition + 1
	if len(doc.rawLines) <= metaDataStartLine {
		return NotFound()
	}

	// Check if the last horizontal rule is followed
	// either by white space or be meta data
	for _, line := range doc.rawLines[metaDataStartLine:] {

		lineMatchesMetaDataPattern := doc.pattern.MetaData.MatchString(line)
		if lineMatchesMetaDataPattern {

			endLine := len(doc.rawLines) - 1
			return Found(metaDataStartLine, endLine, doc.rawLines[metaDataStartLine:endLine])

		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			return NotFound()
		}

	}

	return NotFound()
}

// Check if the current Document contains content
func (doc *Document) locateContent() *MatchResult {

	// Content must be preceeded by a description
	description := doc.locateDescription()
	if !description.Found {
		return NotFound()
	}

	// If the document has no more lines than the line
	// in which the description has been located, there
	// will be no room for content
	startLine := description.Lines.Start + 1
	if len(doc.rawLines) <= startLine {
		return NotFound()
	}

	// If the document contains meta data
	// the content will be between the description
	// and the meta data. If not the content
	// will go up to the end of the document.
	endLine := 0
	metaData := doc.locateMetaData()
	if metaData.Found {
		endLine = metaData.Lines.Start - 1
	} else {
		endLine = len(doc.rawLines) - 1
	}

	// All lines between the start- and endLine are content
	return Found(startLine, endLine, doc.rawLines[startLine:endLine])
}
<|endoftext|>


<commit_before>package appclient

import (
	"net"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/miekg/dns"

	"github.com/weaveworks/scope/common/xfer"
)

const (
	dnsPollInterval = 10 * time.Second
)

var (
	tick = fastStartTicker
)

// fastStartTicker is a ticker that 'ramps up' from 1 sec to duration.
func fastStartTicker(duration time.Duration) <-chan time.Time {
	c := make(chan time.Time, 1)
	go func() {
		d := 1 * time.Second
		for {
			time.Sleep(d)
			d = d * 2
			if d > duration {
				d = duration
			}

			select {
			case c <- time.Now():
			default:
			}
		}
	}()
	return c
}

type setter func(string, []string)

// Resolver is a thing that can be stopped...
type Resolver interface {
	Stop()
}

type staticResolver struct {
	setters []setter
	targets []target
	quit    chan struct{}
	lookup  LookupIP
}

// LookupIP type is used for looking up IPs.
type LookupIP func(host string) (ips []net.IP, err error)

type target struct{ host, port string }

func (t target) String() string { return net.JoinHostPort(t.host, t.port) }

// NewResolver periodically resolves the targets, and calls the set
// function with all the resolved IPs. It explictiy supports targets which
// resolve to multiple IPs.  It uses the supplied DNS server name.
func NewResolver(targets []string, lookup LookupIP, setters ...setter) Resolver {
	r := staticResolver{
		targets: prepare(targets),
		setters: setters,
		quit:    make(chan struct{}),
		lookup:  lookup,
	}
	go r.loop()
	return r
}

// LookupUsing produces a LookupIP function for the given DNS server.
func LookupUsing(dnsServer string) func(host string) (ips []net.IP, err error) {
	client := dns.Client{
		Net: "tcp",
	}
	return func(host string) (ips []net.IP, err error) {
		m := &dns.Msg{}
		m.SetQuestion(dns.Fqdn(host), dns.TypeA)
		in, _, err := client.Exchange(m, dnsServer)
		if err != nil {
			return nil, err
		}
		result := []net.IP{}
		for _, answer := range in.Answer {
			if a, ok := answer.(*dns.A); ok {
				result = append(result, a.A)
			}
		}
		return result, nil
	}
}

func (r staticResolver) loop() {
	r.resolve()
	t := tick(dnsPollInterval)
	for {
		select {
		case <-t:
			r.resolve()
		case <-r.quit:
			return
		}
	}
}

func (r staticResolver) Stop() {
	close(r.quit)
}

func prepare(strs []string) []target {
	var targets []target
	for _, s := range strs {
		var host, port string
		if strings.Contains(s, ":") {
			var err error
			host, port, err = net.SplitHostPort(s)
			if err != nil {
				log.Errorf("invalid address %s: %v", s, err)
				continue
			}
		} else {
			host, port = s, strconv.Itoa(xfer.AppPort)
		}
		targets = append(targets, target{host, port})
	}
	return targets
}

func (r staticResolver) resolve() {
	for t, endpoints := range r.resolveMany(r.targets) {
		for _, setter := range r.setters {
			setter(t.String(), endpoints)
		}
	}
}

func (r staticResolver) resolveMany(targets []target) map[target][]string {
	result := map[target][]string{}
	for _, t := range targets {
		result[t] = r.resolveOne(t)
	}
	return result
}

func (r staticResolver) resolveOne(t target) []string {
	var addrs []net.IP
	if addr := net.ParseIP(t.host); addr != nil {
		addrs = []net.IP{addr}
	} else {
		var err error
		addrs, err = r.lookup(t.host)
		if err != nil {
			log.Errorf("Error resolving %s: %v", t.host, err)
			return []string{}
		}
	}
	endpoints := make([]string, 0, len(addrs))
	for _, addr := range addrs {
		// For now, ignore IPv6
		if addr.To4() == nil {
			continue
		}
		endpoints = append(endpoints, net.JoinHostPort(addr.String(), t.port))
	}
	return endpoints
}
<commit_msg>Review feedback<commit_after>package appclient

import (
	"net"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/miekg/dns"

	"github.com/weaveworks/scope/common/xfer"
)

const (
	dnsPollInterval = 10 * time.Second
)

var (
	tick = fastStartTicker
)

// fastStartTicker is a ticker that 'ramps up' from 1 sec to duration.
func fastStartTicker(duration time.Duration) <-chan time.Time {
	c := make(chan time.Time, 1)
	go func() {
		d := 1 * time.Second
		for {
			time.Sleep(d)
			d = d * 2
			if d > duration {
				d = duration
			}

			select {
			case c <- time.Now():
			default:
			}
		}
	}()
	return c
}

type setter func(string, []string)

// Resolver is a thing that can be stopped...
type Resolver interface {
	Stop()
}

type staticResolver struct {
	setters           []setter
	targets           []target
	failedResolutions map[string]struct{}
	quit              chan struct{}
	lookup            LookupIP
}

// LookupIP type is used for looking up IPs.
type LookupIP func(host string) (ips []net.IP, err error)

type target struct{ host, port string }

func (t target) String() string { return net.JoinHostPort(t.host, t.port) }

// NewResolver periodically resolves the targets, and calls the set
// function with all the resolved IPs. It explictiy supports targets which
// resolve to multiple IPs.  It uses the supplied DNS server name.
func NewResolver(targets []string, lookup LookupIP, setters ...setter) Resolver {
	r := staticResolver{
		targets:           prepare(targets),
		setters:           setters,
		failedResolutions: map[string]struct{}{},
		quit:              make(chan struct{}),
		lookup:            lookup,
	}
	go r.loop()
	return r
}

// LookupUsing produces a LookupIP function for the given DNS server.
func LookupUsing(dnsServer string) func(host string) (ips []net.IP, err error) {
	client := dns.Client{
		Net: "tcp",
	}
	return func(host string) (ips []net.IP, err error) {
		m := &dns.Msg{}
		m.SetQuestion(dns.Fqdn(host), dns.TypeA)
		in, _, err := client.Exchange(m, dnsServer)
		if err != nil {
			return nil, err
		}
		result := []net.IP{}
		for _, answer := range in.Answer {
			if a, ok := answer.(*dns.A); ok {
				result = append(result, a.A)
			}
		}
		return result, nil
	}
}

func (r staticResolver) loop() {
	r.resolve()
	t := tick(dnsPollInterval)
	for {
		select {
		case <-t:
			r.resolve()
		case <-r.quit:
			return
		}
	}
}

func (r staticResolver) Stop() {
	close(r.quit)
}

func prepare(strs []string) []target {
	var targets []target
	for _, s := range strs {
		var host, port string
		if strings.Contains(s, ":") {
			var err error
			host, port, err = net.SplitHostPort(s)
			if err != nil {
				log.Errorf("invalid address %s: %v", s, err)
				continue
			}
		} else {
			host, port = s, strconv.Itoa(xfer.AppPort)
		}
		targets = append(targets, target{host, port})
	}
	return targets
}

func (r staticResolver) resolve() {
	for t, endpoints := range r.resolveMany(r.targets) {
		for _, setter := range r.setters {
			setter(t.String(), endpoints)
		}
	}
}

func (r staticResolver) resolveMany(targets []target) map[target][]string {
	result := map[target][]string{}
	for _, t := range targets {
		result[t] = r.resolveOne(t)
	}
	return result
}

func (r staticResolver) resolveOne(t target) []string {
	var addrs []net.IP
	if addr := net.ParseIP(t.host); addr != nil {
		addrs = []net.IP{addr}
	} else {
		var err error
		addrs, err = r.lookup(t.host)
		if err != nil {
			if _, ok := r.failedResolutions[t.host]; !ok {
				log.Warnf("Cannot resolve %s: %v", t.host, err)
				// Only log the error once
				r.failedResolutions[t.host] = struct{}{}
			}
			return []string{}
		}
		// Allow logging errors in future resolutions
		delete(r.failedResolutions, t.host)
	}
	endpoints := make([]string, 0, len(addrs))
	for _, addr := range addrs {
		// For now, ignore IPv6
		if addr.To4() == nil {
			continue
		}
		endpoints = append(endpoints, net.JoinHostPort(addr.String(), t.port))
	}
	return endpoints
}
<|endoftext|>


<commit_before>package gorm

import (
	"fmt"
	"reflect"
	"time"
)

type mysql struct {
	commonDialect
}

func (mysql) SqlTag(value reflect.Value, size int, autoIncrease bool) string {
	switch value.Kind() {
	case reflect.Bool:
		return "boolean"
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
		if autoIncrease {
			return "int AUTO_INCREMENT"
		}
		return "int"
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
		if autoIncrease {
			return "int unsigned AUTO_INCREMENT"
		}
		return "int unsigned"
	case reflect.Int64:
		if autoIncrease {
			return "bigint AUTO_INCREMENT"
		}
		return "bigint"
	case reflect.Uint64:
		if autoIncrease {
			return "bigint unsigned AUTO_INCREMENT"
		}
		return "bigint unsigned"
	case reflect.Float32, reflect.Float64:
		return "double"
	case reflect.String:
		if size > 0 && size < 65532 {
			return fmt.Sprintf("varchar(%d)", size)
		}
		return "longtext"
	case reflect.Struct:
		if _, ok := value.Interface().(time.Time); ok {
			return "timestamp NULL"
		}
	default:
		if _, ok := value.Interface().([]byte); ok {
			if size > 0 && size < 65532 {
				return fmt.Sprintf("varbinary(%d)", size)
			}
			return "longblob"
		}
	}
	panic(fmt.Sprintf("invalid sql type %s (%s) for mysql", value.Type().Name(), value.Kind().String()))
}

func (mysql) Quote(key string) string {
	return fmt.Sprintf("`%s`", key)
}

func (mysql) SelectFromDummyTable() string {
	return "FROM DUAL"
}

func (s mysql) CurrentDatabase(scope *Scope) (name string) {
	s.RawScanString(scope, &name, "SELECT DATABASE()")
	return
}
<commit_msg>Issue #553 quoting breaks mariadb/mysql<commit_after>package gorm

import (
	"fmt"
	"reflect"
	"strings"
	"time"
)

type mysql struct {
	commonDialect
}

func (mysql) SqlTag(value reflect.Value, size int, autoIncrease bool) string {
	switch value.Kind() {
	case reflect.Bool:
		return "boolean"
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
		if autoIncrease {
			return "int AUTO_INCREMENT"
		}
		return "int"
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
		if autoIncrease {
			return "int unsigned AUTO_INCREMENT"
		}
		return "int unsigned"
	case reflect.Int64:
		if autoIncrease {
			return "bigint AUTO_INCREMENT"
		}
		return "bigint"
	case reflect.Uint64:
		if autoIncrease {
			return "bigint unsigned AUTO_INCREMENT"
		}
		return "bigint unsigned"
	case reflect.Float32, reflect.Float64:
		return "double"
	case reflect.String:
		if size > 0 && size < 65532 {
			return fmt.Sprintf("varchar(%d)", size)
		}
		return "longtext"
	case reflect.Struct:
		if _, ok := value.Interface().(time.Time); ok {
			return "timestamp NULL"
		}
	default:
		if _, ok := value.Interface().([]byte); ok {
			if size > 0 && size < 65532 {
				return fmt.Sprintf("varbinary(%d)", size)
			}
			return "longblob"
		}
	}
	panic(fmt.Sprintf("invalid sql type %s (%s) for mysql", value.Type().Name(), value.Kind().String()))
}

func (mysql) Quote(key string) string {
	if strings.Contains(key, "(") {
		pos1 := strings.Index(key, "(")
		pos2 := strings.Index(key, ")")
		return fmt.Sprintf("`%s`(`%s`)", key[0:pos1], key[pos1+1:pos2])
	}
	return fmt.Sprintf("`%s`", key)
}

func (mysql) SelectFromDummyTable() string {
	return "FROM DUAL"
}

func (s mysql) CurrentDatabase(scope *Scope) (name string) {
	s.RawScanString(scope, &name, "SELECT DATABASE()")
	return
}
<|endoftext|>


<commit_before>package sqlx

// Named Query Support
//
//  * BindStruct, BindMap - bind query bindvars to map/struct args
//	* NamedExec, NamedQuery - named query w/ struct
//  * NamedExecMap, NamedQueryMap - named query w/ maps
//  * NamedStmt - a pre-compiled named query which is a prepared statement
//
// Internal Interfaces:
//
//  * compileNamedQuery - rebind a named query, returning a query and list of names
//
import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"reflect"
	"strconv"
	"unicode"
)

// NamedStmt is a prepared statement that executes named queries.  Prepare it
// how you would execute a NamedQuery, but pass in a struct (or map for the map
// variants) when you go to execute.
type NamedStmt struct {
	Params      []string
	QueryString string
	Stmt        *Stmt
}

// Close closes the named statement.
func (n *NamedStmt) Close() error {
	return n.Stmt.Close()
}

// Exec executes a named statement using the struct passed.
func (n *NamedStmt) Exec(arg interface{}) (sql.Result, error) {
	args, err := bindStruct(n.Params, arg)
	if err != nil {
		return *new(sql.Result), err
	}
	return n.Stmt.Exec(args...)
}

// Query executes a named statement using the struct argument, returning rows.
func (n *NamedStmt) Query(arg interface{}) (*sql.Rows, error) {
	args, err := bindStruct(n.Params, arg)
	if err != nil {
		return nil, err
	}
	return n.Stmt.Query(args...)
}

// QueryRow executes a named statement against the database.  Because sqlx cannot
// create a *sql.Row with an error condition pre-set for binding errors, sqlx
// returns a *sqlx.Row instead.
func (n *NamedStmt) QueryRow(arg interface{}) *Row {
	args, err := bindStruct(n.Params, arg)
	if err != nil {
		return &Row{err: err}
	}
	return n.Stmt.QueryRowx(args...)
}

// Execv execs a NamedStmt with the given arg, printing errors and returning them
func (n *NamedStmt) Execv(arg interface{}) (sql.Result, error) {
	res, err := n.Exec(arg)
	if err != nil {
		log.Println(n.QueryString, res, err)
	}
	return res, err
}

// Execl execs a NamedStmt with the given arg, logging errors
func (n *NamedStmt) Execl(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		log.Println(n.QueryString, res, err)
	}
	return res
}

// Execf execs a NamedStmt, using log.fatal to print out errors
func (n *NamedStmt) Execf(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		log.Fatal(n.QueryString, res, err)
	}
	return res
}

// Execp execs a NamedStmt, panicing on error
func (n *NamedStmt) Execp(arg interface{}) sql.Result {
	return n.MustExec(arg)
}

// MustExec execs a NamedStmt, panicing on error
func (n *NamedStmt) MustExec(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		panic(err)
	}
	return res
}

// Queryx using this NamedStmt
func (n *NamedStmt) Queryx(arg interface{}) (*Rows, error) {
	r, err := n.Query(arg)
	if err != nil {
		return nil, err
	}
	return &Rows{Rows: *r}, err
}

// QueryRowx this NamedStmt.  Because of limitations with QueryRow, this is
// an alias for QueryRow.
func (n *NamedStmt) QueryRowx(arg interface{}) *Row {
	return n.QueryRow(arg)
}

// Select using this NamedStmt
func (n *NamedStmt) Select(dest interface{}, arg interface{}) error {
	rows, err := n.Query(arg)
	if err != nil {
		return err
	}
	// if something happens here, we want to make sure the rows are Closed
	defer rows.Close()
	return StructScan(rows, dest)
}

// Selectv using this NamedStmt
func (n *NamedStmt) Selectv(dest interface{}, arg interface{}) error {
	err := n.Select(dest, arg)
	if err != nil {
		log.Println(n.QueryString, err)
	}
	return err
}

// Selectf using this NamedStmt
func (n *NamedStmt) Selectf(dest interface{}, arg interface{}) {
	err := n.Select(dest, arg)
	if err != nil {
		log.Fatal(n.QueryString, err)
	}
}

// Get using this NamedStmt
func (n *NamedStmt) Get(dest interface{}, arg interface{}) error {
	r := n.QueryRowx(arg)
	return r.StructScan(dest)
}

// A union interface of preparer and binder, required to be able to prepare
// named statements (as the bindtype must be determined).
type namedPreparer interface {
	Preparer
	Binder
}

func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
	bindType := BindType(p.DriverName())
	q, args, err := compileNamedQuery([]byte(query), bindType)
	if err != nil {
		return nil, err
	}
	stmt, err := Preparex(p, q)
	if err != nil {
		return nil, err
	}
	return &NamedStmt{
		QueryString: q,
		Params:      args,
		Stmt:        stmt,
	}, nil
}

// private interface to generate a list of interfaces from a given struct
// type, given a list of names to pull out of the struct.  Used by public
// BindStruct interface.
func bindStruct(names []string, arg interface{}) ([]interface{}, error) {
	arglist := make([]interface{}, 0, len(names))

	t, err := BaseStructType(reflect.TypeOf(arg))
	if err != nil {
		return arglist, err
	}

	// resolve this arg's type into a map of fields to field positions
	fm, err := getFieldmap(t)
	if err != nil {
		return arglist, err
	}

	// grab the indirected value of arg
	v := reflect.ValueOf(arg)
	for v = reflect.ValueOf(arg); v.Kind() == reflect.Ptr; {
		v = v.Elem()
	}

	values := getValues(v)

	for _, name := range names {
		val, ok := fm[name]
		if !ok {
			return arglist, fmt.Errorf("could not find name %s in %v", name, arg)
		}
		arglist = append(arglist, values[val])
	}

	return arglist, nil
}

// BindStruct binds a named parameter query with fields from a struct argument.
// The rules for binding field names to parameter names follow the same
// conventions as for StructScan, including obeying the `db` struct tags.
func BindStruct(bindType int, query string, arg interface{}) (string, []interface{}, error) {
	bound, names, err := compileNamedQuery([]byte(query), bindType)
	if err != nil {
		return "", []interface{}{}, err
	}

	arglist, err := bindStruct(names, arg)
	if err != nil {
		return "", []interface{}{}, err
	}

	return bound, arglist, nil
}

// BindMap binds a named parameter query with a map of arguments.
func BindMap(bindType int, query string, args map[string]interface{}) (string, []interface{}, error) {
	bound, names, err := compileNamedQuery([]byte(query), bindType)
	if err != nil {
		return "", []interface{}{}, err
	}

	arglist := make([]interface{}, 0, len(names))

	for _, name := range names {
		val, ok := args[name]
		if !ok {
			return "", arglist, fmt.Errorf("could not find name %s in %v", name, args)
		}
		arglist = append(arglist, val)
	}

	return bound, arglist, nil
}

// -- Compilation of Named Queries

// Allow digits and letters in bind params;  additionally runes are
// checked against underscores, meaning that bind params can have be
// alphanumeric with underscores.  Mind the difference between unicode
// digits and numbers, where '5' is a digit but '五' is not.
var allowedBindRunes = []*unicode.RangeTable{unicode.Letter, unicode.Digit}

// FIXME: this function isn't safe for unicode named params, as a failing test
// can testify.  This is not a regression but a failure of the original code
// as well.  It should be modified to range over runes in a string rather than
// bytes, even though this is less convenient and slower.  Hopefully the
// addition of the prepared NamedStmt (which will only do this once) will make
// up for the slightly slower ad-hoc NamedExec/NamedQuery.

// compile a NamedQuery into an unbound query (using the '?' bindvar) and
// a list of names.
func compileNamedQuery(qs []byte, bindType int) (query string, names []string, err error) {
	names = make([]string, 0, 10)
	rebound := make([]byte, 0, len(qs))

	inName := false
	last := len(qs) - 1
	currentVar := 1
	name := make([]byte, 0, 10)

	for i, b := range qs {
		// a ':' while we're in a name is an error
		if b == ':' && inName {
			err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i))
			return query, names, err
			// if we encounter a ':' and we aren't in a name, it's a new parameter
			// FIXME: escaping?
		} else if b == ':' {
			inName = true
			name = []byte{}
			// if we're in a name, and this is an allowed character, continue
		} else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_') && i != last {
			// append the byte to the name if we are in a name and not on the last byte
			name = append(name, b)
			// if we're in a name and it's not an allowed character, the name is done
		} else if inName {
			inName = false
			// if this is the final byte of the string and it is part of the name, then
			// make sure to add it to the name
			if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) {
				name = append(name, b)
			}
			// add the string representation to the names list and reset our name buffer
			names = append(names, string(name))
			name = make([]byte, 0, 10)
			// add a proper bindvar for the bindType
			switch bindType {
			case QUESTION, UNKNOWN:
				rebound = append(rebound, '?')
			case DOLLAR:
				rebound = append(rebound, '$')
				for _, b := range strconv.Itoa(currentVar) {
					rebound = append(rebound, byte(b))
				}
				currentVar++
			}
			// add this byte to string unless it was not part of the name
			if i != last {
				rebound = append(rebound, b)
			} else if !unicode.IsOneOf(allowedBindRunes, rune(b)) {
				rebound = append(rebound, b)
			}
		} else {
			// this is a normal byte and should just go onto the rebound query
			rebound = append(rebound, b)
		}
	}

	return string(rebound), names, err
}
<commit_msg>restore oracle binding<commit_after>package sqlx

// Named Query Support
//
//  * BindStruct, BindMap - bind query bindvars to map/struct args
//	* NamedExec, NamedQuery - named query w/ struct
//  * NamedExecMap, NamedQueryMap - named query w/ maps
//  * NamedStmt - a pre-compiled named query which is a prepared statement
//
// Internal Interfaces:
//
//  * compileNamedQuery - rebind a named query, returning a query and list of names
//
import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"reflect"
	"strconv"
	"unicode"
)

// NamedStmt is a prepared statement that executes named queries.  Prepare it
// how you would execute a NamedQuery, but pass in a struct (or map for the map
// variants) when you go to execute.
type NamedStmt struct {
	Params      []string
	QueryString string
	Stmt        *Stmt
}

// Close closes the named statement.
func (n *NamedStmt) Close() error {
	return n.Stmt.Close()
}

// Exec executes a named statement using the struct passed.
func (n *NamedStmt) Exec(arg interface{}) (sql.Result, error) {
	args, err := bindStruct(n.Params, arg)
	if err != nil {
		return *new(sql.Result), err
	}
	return n.Stmt.Exec(args...)
}

// Query executes a named statement using the struct argument, returning rows.
func (n *NamedStmt) Query(arg interface{}) (*sql.Rows, error) {
	args, err := bindStruct(n.Params, arg)
	if err != nil {
		return nil, err
	}
	return n.Stmt.Query(args...)
}

// QueryRow executes a named statement against the database.  Because sqlx cannot
// create a *sql.Row with an error condition pre-set for binding errors, sqlx
// returns a *sqlx.Row instead.
func (n *NamedStmt) QueryRow(arg interface{}) *Row {
	args, err := bindStruct(n.Params, arg)
	if err != nil {
		return &Row{err: err}
	}
	return n.Stmt.QueryRowx(args...)
}

// Execv execs a NamedStmt with the given arg, printing errors and returning them
func (n *NamedStmt) Execv(arg interface{}) (sql.Result, error) {
	res, err := n.Exec(arg)
	if err != nil {
		log.Println(n.QueryString, res, err)
	}
	return res, err
}

// Execl execs a NamedStmt with the given arg, logging errors
func (n *NamedStmt) Execl(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		log.Println(n.QueryString, res, err)
	}
	return res
}

// Execf execs a NamedStmt, using log.fatal to print out errors
func (n *NamedStmt) Execf(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		log.Fatal(n.QueryString, res, err)
	}
	return res
}

// Execp execs a NamedStmt, panicing on error
func (n *NamedStmt) Execp(arg interface{}) sql.Result {
	return n.MustExec(arg)
}

// MustExec execs a NamedStmt, panicing on error
func (n *NamedStmt) MustExec(arg interface{}) sql.Result {
	res, err := n.Exec(arg)
	if err != nil {
		panic(err)
	}
	return res
}

// Queryx using this NamedStmt
func (n *NamedStmt) Queryx(arg interface{}) (*Rows, error) {
	r, err := n.Query(arg)
	if err != nil {
		return nil, err
	}
	return &Rows{Rows: *r}, err
}

// QueryRowx this NamedStmt.  Because of limitations with QueryRow, this is
// an alias for QueryRow.
func (n *NamedStmt) QueryRowx(arg interface{}) *Row {
	return n.QueryRow(arg)
}

// Select using this NamedStmt
func (n *NamedStmt) Select(dest interface{}, arg interface{}) error {
	rows, err := n.Query(arg)
	if err != nil {
		return err
	}
	// if something happens here, we want to make sure the rows are Closed
	defer rows.Close()
	return StructScan(rows, dest)
}

// Selectv using this NamedStmt
func (n *NamedStmt) Selectv(dest interface{}, arg interface{}) error {
	err := n.Select(dest, arg)
	if err != nil {
		log.Println(n.QueryString, err)
	}
	return err
}

// Selectf using this NamedStmt
func (n *NamedStmt) Selectf(dest interface{}, arg interface{}) {
	err := n.Select(dest, arg)
	if err != nil {
		log.Fatal(n.QueryString, err)
	}
}

// Get using this NamedStmt
func (n *NamedStmt) Get(dest interface{}, arg interface{}) error {
	r := n.QueryRowx(arg)
	return r.StructScan(dest)
}

// A union interface of preparer and binder, required to be able to prepare
// named statements (as the bindtype must be determined).
type namedPreparer interface {
	Preparer
	Binder
}

func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
	bindType := BindType(p.DriverName())
	q, args, err := compileNamedQuery([]byte(query), bindType)
	if err != nil {
		return nil, err
	}
	stmt, err := Preparex(p, q)
	if err != nil {
		return nil, err
	}
	return &NamedStmt{
		QueryString: q,
		Params:      args,
		Stmt:        stmt,
	}, nil
}

// private interface to generate a list of interfaces from a given struct
// type, given a list of names to pull out of the struct.  Used by public
// BindStruct interface.
func bindStruct(names []string, arg interface{}) ([]interface{}, error) {
	arglist := make([]interface{}, 0, len(names))

	t, err := BaseStructType(reflect.TypeOf(arg))
	if err != nil {
		return arglist, err
	}

	// resolve this arg's type into a map of fields to field positions
	fm, err := getFieldmap(t)
	if err != nil {
		return arglist, err
	}

	// grab the indirected value of arg
	v := reflect.ValueOf(arg)
	for v = reflect.ValueOf(arg); v.Kind() == reflect.Ptr; {
		v = v.Elem()
	}

	values := getValues(v)

	for _, name := range names {
		val, ok := fm[name]
		if !ok {
			return arglist, fmt.Errorf("could not find name %s in %v", name, arg)
		}
		arglist = append(arglist, values[val])
	}

	return arglist, nil
}

// BindStruct binds a named parameter query with fields from a struct argument.
// The rules for binding field names to parameter names follow the same
// conventions as for StructScan, including obeying the `db` struct tags.
func BindStruct(bindType int, query string, arg interface{}) (string, []interface{}, error) {
	bound, names, err := compileNamedQuery([]byte(query), bindType)
	if err != nil {
		return "", []interface{}{}, err
	}

	arglist, err := bindStruct(names, arg)
	if err != nil {
		return "", []interface{}{}, err
	}

	return bound, arglist, nil
}

// BindMap binds a named parameter query with a map of arguments.
func BindMap(bindType int, query string, args map[string]interface{}) (string, []interface{}, error) {
	bound, names, err := compileNamedQuery([]byte(query), bindType)
	if err != nil {
		return "", []interface{}{}, err
	}

	arglist := make([]interface{}, 0, len(names))

	for _, name := range names {
		val, ok := args[name]
		if !ok {
			return "", arglist, fmt.Errorf("could not find name %s in %v", name, args)
		}
		arglist = append(arglist, val)
	}

	return bound, arglist, nil
}

// -- Compilation of Named Queries

// Allow digits and letters in bind params;  additionally runes are
// checked against underscores, meaning that bind params can have be
// alphanumeric with underscores.  Mind the difference between unicode
// digits and numbers, where '5' is a digit but '五' is not.
var allowedBindRunes = []*unicode.RangeTable{unicode.Letter, unicode.Digit}

// FIXME: this function isn't safe for unicode named params, as a failing test
// can testify.  This is not a regression but a failure of the original code
// as well.  It should be modified to range over runes in a string rather than
// bytes, even though this is less convenient and slower.  Hopefully the
// addition of the prepared NamedStmt (which will only do this once) will make
// up for the slightly slower ad-hoc NamedExec/NamedQuery.

// compile a NamedQuery into an unbound query (using the '?' bindvar) and
// a list of names.
func compileNamedQuery(qs []byte, bindType int) (query string, names []string, err error) {
	names = make([]string, 0, 10)
	rebound := make([]byte, 0, len(qs))

	inName := false
	last := len(qs) - 1
	currentVar := 1
	name := make([]byte, 0, 10)

	for i, b := range qs {
		// a ':' while we're in a name is an error
		if b == ':' && inName {
			err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i))
			return query, names, err
			// if we encounter a ':' and we aren't in a name, it's a new parameter
			// FIXME: escaping?
		} else if b == ':' {
			inName = true
			name = []byte{}
			// if we're in a name, and this is an allowed character, continue
		} else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_') && i != last {
			// append the byte to the name if we are in a name and not on the last byte
			name = append(name, b)
			// if we're in a name and it's not an allowed character, the name is done
		} else if inName {
			inName = false
			// if this is the final byte of the string and it is part of the name, then
			// make sure to add it to the name
			if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) {
				name = append(name, b)
			}
			// add the string representation to the names list and reset our name buffer
			names = append(names, string(name))
			name = make([]byte, 0, 10)
			// add a proper bindvar for the bindType
			switch bindType {
			// oracle only supports named type bind vars even for positional
			case NAMED:
				rebound = append(rebound, ':')
				rebound = append(rebound, name...)
			case QUESTION, UNKNOWN:
				rebound = append(rebound, '?')
			case DOLLAR:
				rebound = append(rebound, '$')
				for _, b := range strconv.Itoa(currentVar) {
					rebound = append(rebound, byte(b))
				}
				currentVar++
			}
			// add this byte to string unless it was not part of the name
			if i != last {
				rebound = append(rebound, b)
			} else if !unicode.IsOneOf(allowedBindRunes, rune(b)) {
				rebound = append(rebound, b)
			}
		} else {
			// this is a normal byte and should just go onto the rebound query
			rebound = append(rebound, b)
		}
	}

	return string(rebound), names, err
}
<|endoftext|>


<commit_before>package webhookmodels

import (
	"encoding/json"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var subscriptionCreatedWebhookRequest = []byte(`{
      "id": "sub_00000000000000",
      "plan": {
        "interval": "year",
        "name": "Developer",
        "created": 1412968872,
        "amount": 23940,
        "currency": "usd",
        "id": "developer_00000000000000",
        "object": "plan",
        "livemode": false,
        "interval_count": 1,
        "trial_period_days": null,
        "metadata": {},
        "statement_descriptor": null,
        "statement_description": null
      },
      "object": "subscription",
      "start": 1422577711,
      "status": "active",
      "customer": "cus_00000000000000",
      "cancel_at_period_end": false,
      "current_period_start": 1422577711,
      "current_period_end": 1454113711,
      "ended_at": null,
      "trial_start": null,
      "trial_end": null,
      "canceled_at": null,
      "quantity": 1,
      "application_fee_percent": null,
      "discount": null,
      "tax_percent": null,
      "metadata": {}
    }
`)

var invoiceCreatedWebhookRequest = []byte(`{
		"date": 1422578141,
		"id": "in_00000000000000",
		"period_start": 1422578141,
		"period_end": 1422578141,
		"lines": {
			"data": [
				{
					"id": "sub_5bg7lgPIPCWEI3",
					"object": "line_item",
					"type": "subscription",
					"livemode": true,
					"amount": 23940,
					"currency": "usd",
					"proration": false,
					"period": {
						"start": 1454114143,
						"end": 1485736543
					},
					"subscription": null,
					"quantity": 1,
					"plan": {
						"interval": "year",
						"name": "Developer",
						"created": 1412968872,
						"amount": 23940,
						"currency": "usd",
						"id": "developer_year",
						"object": "plan",
						"livemode": false,
						"interval_count": 1,
						"trial_period_days": null,
						"metadata": {},
						"statement_descriptor": null,
						"statement_description": null
					},
					"description": null,
					"metadata": {}
				}
			],
			"total_count": 1,
			"object": "list",
			"url": "/v1/invoices/in_15QSl7Dy8g9bkw8yWuzjCCA0/lines"
		},
		"subtotal": 2450,
		"total": 2450,
		"customer": "cus_00000000000000",
		"object": "invoice",
		"attempted": false,
		"closed": true,
		"forgiven": false,
		"paid": true,
		"livemode": false,
		"attempt_count": 1,
		"amount_due": 2450,
		"currency": "usd",
		"starting_balance": 0,
		"ending_balance": 0,
		"next_payment_attempt": null,
		"webhooks_delivered_at": null,
		"charge": "ch_00000000000000",
		"discount": null,
		"application_fee": null,
		"subscription": "sub_00000000000000",
		"tax_percent": null,
		"metadata": {},
		"statement_descriptor": null,
		"description": null,
		"receipt_number": null,
		"statement_description": null
	}`)

func TestStripe(t *testing.T) {
	Convey("Given webhook from stripe", t, func() {
		Convey("Then it should unmarshal subscription", func() {
			var sub *StripeSubscription
			err := json.Unmarshal(subscriptionCreatedWebhookRequest, &sub)

			So(err, ShouldBeNil)

			So(sub.ID, ShouldEqual, "sub_00000000000000")
			So(sub.CustomerId, ShouldEqual, "cus_00000000000000")
			So(sub.Plan.ID, ShouldEqual, "developer_00000000000000")
			So(sub.Plan.Name, ShouldEqual, "Developer")
		})

		Convey("Then it should unmarshal invoice", func() {
			var invoice *StripeInvoice
			err := json.Unmarshal(invoiceCreatedWebhookRequest, &invoice)

			So(err, ShouldBeNil)

			So(invoice.ID, ShouldEqual, "in_00000000000000")
			So(invoice.CustomerId, ShouldEqual, "cus_00000000000000")
			So(invoice.AmountDue, ShouldEqual, 2450)
			So(invoice.Currency, ShouldEqual, "usd")
			So(invoice.Lines.Count, ShouldEqual, 1)
			So(len(invoice.Lines.Data), ShouldEqual, 1)

			data := invoice.Lines.Data[0]

			So(data.SubscriptionId, ShouldEqual, "sub_5bg7lgPIPCWEI3")
			So(data.Period.Start, ShouldEqual, 1454114143)
			So(data.Period.End, ShouldEqual, 1485736543)
			So(data.Plan.Name, ShouldEqual, "Developer")
			So(data.Plan.Interval, ShouldEqual, "year")
		})
	})
}
<commit_msg>paymentwebhook: fix failing tests<commit_after>package webhookmodels

import (
	"encoding/json"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var subscriptionCreatedWebhookRequest = []byte(`{
      "id": "sub_00000000000000",
      "plan": {
        "interval": "year",
        "name": "Developer",
        "created": 1412968872,
        "amount": 23940,
        "currency": "usd",
        "id": "developer_00000000000000",
        "object": "plan",
        "livemode": false,
        "interval_count": 1,
        "trial_period_days": null,
        "metadata": {},
        "statement_descriptor": null,
        "statement_description": null
      },
      "object": "subscription",
      "start": 1422577711,
      "status": "active",
      "customer": "cus_00000000000000",
      "cancel_at_period_end": false,
      "current_period_start": 1422577711,
      "current_period_end": 1454113711,
      "ended_at": null,
      "trial_start": null,
      "trial_end": null,
      "canceled_at": null,
      "quantity": 1,
      "application_fee_percent": null,
      "discount": null,
      "tax_percent": null,
      "metadata": {}
    }
`)

var invoiceCreatedWebhookRequest = []byte(`{
		"date": 1422578141,
		"id": "in_00000000000000",
		"period_start": 1422578141,
		"period_end": 1422578141,
		"lines": {
			"data": [
				{
					"id": "sub_5bg7lgPIPCWEI3",
					"object": "line_item",
					"type": "subscription",
					"livemode": true,
					"amount": 23940,
					"currency": "usd",
					"proration": false,
					"period": {
						"start": 1454114143,
						"end": 1485736543
					},
					"subscription": null,
					"quantity": 1,
					"plan": {
						"interval": "year",
						"name": "Developer",
						"created": 1412968872,
						"amount": 23940,
						"currency": "usd",
						"id": "developer_year",
						"object": "plan",
						"livemode": false,
						"interval_count": 1,
						"trial_period_days": null,
						"metadata": {},
						"statement_descriptor": null,
						"statement_description": null
					},
					"description": null,
					"metadata": {}
				}
			],
			"total_count": 1,
			"object": "list",
			"url": "/v1/invoices/in_15QSl7Dy8g9bkw8yWuzjCCA0/lines"
		},
		"subtotal": 2450,
		"total": 2450,
		"customer": "cus_00000000000000",
		"object": "invoice",
		"attempted": false,
		"closed": true,
		"forgiven": false,
		"paid": true,
		"livemode": false,
		"attempt_count": 1,
		"amount_due": 2450,
		"currency": "usd",
		"starting_balance": 0,
		"ending_balance": 0,
		"next_payment_attempt": null,
		"webhooks_delivered_at": null,
		"charge": "ch_00000000000000",
		"discount": null,
		"application_fee": null,
		"subscription": "sub_00000000000000",
		"tax_percent": null,
		"metadata": {},
		"statement_descriptor": null,
		"description": null,
		"receipt_number": null,
		"statement_description": null
	}`)

func TestStripe(t *testing.T) {
	Convey("Given webhook from stripe", t, func() {
		Convey("Then it should unmarshal subscription", func() {
			var sub *StripeSubscription
			err := json.Unmarshal(subscriptionCreatedWebhookRequest, &sub)

			So(err, ShouldBeNil)

			So(sub.ID, ShouldEqual, "sub_00000000000000")
			So(sub.CustomerId, ShouldEqual, "cus_00000000000000")
			So(sub.Plan.ID, ShouldEqual, "developer_00000000000000")
			So(sub.Plan.Name, ShouldEqual, "Developer")
		})

		Convey("Then it should unmarshal invoice", func() {
			var invoice *StripeInvoice
			err := json.Unmarshal(invoiceCreatedWebhookRequest, &invoice)

			So(err, ShouldBeNil)

			So(invoice.ID, ShouldEqual, "in_00000000000000")
			So(invoice.CustomerId, ShouldEqual, "cus_00000000000000")
			So(invoice.AmountDue, ShouldEqual, 2450)
			So(invoice.Currency, ShouldEqual, "usd")
			So(invoice.Lines.Count, ShouldEqual, 1)
			So(len(invoice.Lines.Data), ShouldEqual, 1)

			data := invoice.Lines.Data[0]

			So(data.Id, ShouldEqual, "sub_5bg7lgPIPCWEI3")
			So(data.Period.Start, ShouldEqual, 1454114143)
			So(data.Period.End, ShouldEqual, 1485736543)
			So(data.Plan.Name, ShouldEqual, "Developer")
			So(data.Plan.Interval, ShouldEqual, "year")
		})
	})
}
<|endoftext|>