<commit_before>package main

import (
	"fmt"
	gocache "github.com/abhiyerra/gowebcommons/cache"
	render "github.com/abhiyerra/gowebcommons/render"
	"github.com/coreos/go-etcd/etcd"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"log"
	"net/http"
	"os"
	"strconv"
)

const (
	DatabaseUrlKey = "/treemap/database_url"
)

var (
	db    gorm.DB
	cache gocache.Cache
)

type Tree struct {
	Id         int64    `json:"id"`
	CommonName string   `json:"common_name"`
	LatinName  string   `json:"latin_name"`
	GeomData   []string `json:"geom",sql:"-"`
	Area       float64  `json:"area",sql:"-"`
}

func (t *Tree) GetGeodata() {
	rows, err := db.Table("tree_geoms").Select("ST_AsGeoJSON(ST_CollectionExtract(geom, 3)) as geom2").Where("latin_name = ?", t.LatinName).Rows()
	if err != nil {
		log.Println(err)
	}

	for rows.Next() {
		var geodata string
		rows.Scan(&geodata)
		t.GeomData = append(t.GeomData, geodata)
	}
}

func (t *Tree) GetArea() {
	var a struct {
		Area float64
	}
	db.Table("tree_geoms").Select("SUM(ST_Area(ST_Transform(geom, 900913))) as area").Where("latin_name = ?", t.LatinName).Scan(&a)

	t.Area = a.Area * 0.000189394 * 0.000189394 // Get the miles
	log.Println("Area:", t.Area)
}

type NationalPark struct {
	UnitName string `json:"name"`
	UnitCode string `json:"code"`
	GeomData string `json:"geom"`
}

func showTreesHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	treeId, _ := strconv.ParseInt(vars["treeId"], 10, 64)

	tree := cache.Get("tree/"+vars["treeId"], func() interface{} {
		tree := Tree{Id: int64(treeId)}
		db.First(&tree)
		tree.GetGeodata()
		tree.GetArea()

		return tree
	})

	render.RenderJson(w, tree)
}

func nearbyTreesHandler(w http.ResponseWriter, r *http.Request) {
	var trees []Tree

	longitude := r.URL.Query().Get("long")
	latitude := r.URL.Query().Get("lat")
	log.Println("Long:", longitude, "Lat:", latitude)

	err := db.Model(Tree{}).Select("id, latin_name, common_name").
		Where(fmt.Sprintf("latin_name in (select distinct(latin_name) From tree_geoms where ST_DWithin(ST_GeomFromText('POINT(%s %s)' , 4326)::geography, geom, 160934 , true))", longitude, latitude)).
		Scan(&trees)

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

	render.RenderJson(w, trees)
}

func treesHandler(w http.ResponseWriter, r *http.Request) {
	trees := cache.Get("trees", func() interface{} {
		var trees []Tree

		err := db.Model(Tree{}).Select("id, latin_name, common_name").Scan(&trees)
		if err != nil {
			log.Println(err)
		}

		return trees
	})

	render.RenderJson(w, trees)
}

func nearbyParksHandler(w http.ResponseWriter, r *http.Request) {
	var parks []NationalPark

	longitude := r.URL.Query().Get("long")
	latitude := r.URL.Query().Get("lat")
	log.Println("Long:", longitude, "Lat:", latitude)

	err := db.Model(NationalPark{}).
		Select("ST_AsGeoJSON(ST_CollectionExtract(geom, 3)) as geom_data, unit_name, unit_code").
		Where(fmt.Sprintf("ST_DWithin(ST_GeomFromText('POINT(%s %s)' , 4326)::geography, geom, 160934, true)", longitude, latitude)). // Within 100 miles -> 160934 meters
		Scan(&parks)
	if err != nil {
		log.Println(err)
	}

	render.RenderJson(w, parks)
}

func parksHandler(w http.ResponseWriter, r *http.Request) {
	parks := cache.Get("parks", func() interface{} {
		var parks []NationalPark
		db.Model(NationalPark{}).Select("ST_AsGeoJSON(ST_CollectionExtract(geom, 3)) as geom_data, unit_name, unit_code").Scan(&parks)
		return parks
	})

	render.RenderJson(w, parks)
}

func dbConnect(databaseUrl string) {
	log.Println("Connecting to database:", databaseUrl)
	var err error
	db, err = gorm.Open("postgres", databaseUrl)
	if err != nil {
		log.Println(err)
	}
	db.LogMode(true)
}

func init() {
	etcdHosts := os.Getenv("ETCD_HOSTS")
	if etcdHosts == "" {
		etcdHosts = "http://127.0.0.1:4001"
	}

	etcdClient := etcd.NewClient([]string{etcdHosts})

	resp, err := etcdClient.Get(DatabaseUrlKey, false, false)
	if err != nil {
		panic(err)
	}

	databaseUrl := resp.Node.Value
	dbConnect(databaseUrl)

	cache = gocache.New()
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/trees/nearby", nearbyTreesHandler)
	r.HandleFunc("/trees/{treeId}", showTreesHandler)
	r.HandleFunc("/trees", treesHandler)
	r.HandleFunc("/parks/nearby", nearbyParksHandler)
	r.HandleFunc("/parks", parksHandler)
	r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

	http.Handle("/", r)
	http.ListenAndServe(":3001", nil)
}
<commit_msg>Optimize the nearby tree query with an inner join<commit_after>package main

import (
	"fmt"
	gocache "github.com/abhiyerra/gowebcommons/cache"
	render "github.com/abhiyerra/gowebcommons/render"
	"github.com/coreos/go-etcd/etcd"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"log"
	"net/http"
	"os"
	"strconv"
)

const (
	DatabaseUrlKey = "/treemap/database_url"
)

var (
	db    gorm.DB
	cache gocache.Cache
)

type Tree struct {
	Id         int64    `json:"id"`
	CommonName string   `json:"common_name"`
	LatinName  string   `json:"latin_name"`
	GeomData   []string `json:"geom",sql:"-"`
	Area       float64  `json:"area",sql:"-"`
}

func (t *Tree) GetGeodata() {
	rows, err := db.Table("tree_geoms").Select("ST_AsGeoJSON(ST_CollectionExtract(geom, 3)) as geom2").Where("latin_name = ?", t.LatinName).Rows()
	if err != nil {
		log.Println(err)
	}

	for rows.Next() {
		var geodata string
		rows.Scan(&geodata)
		t.GeomData = append(t.GeomData, geodata)
	}
}

func (t *Tree) GetArea() {
	var a struct {
		Area float64
	}
	db.Table("tree_geoms").Select("SUM(ST_Area(ST_Transform(geom, 900913))) as area").Where("latin_name = ?", t.LatinName).Scan(&a)

	t.Area = a.Area * 0.000189394 * 0.000189394 // Get the miles
	log.Println("Area:", t.Area)
}

type NationalPark struct {
	UnitName string `json:"name"`
	UnitCode string `json:"code"`
	GeomData string `json:"geom"`
}

func showTreesHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	treeId, _ := strconv.ParseInt(vars["treeId"], 10, 64)

	tree := cache.Get("tree/"+vars["treeId"], func() interface{} {
		tree := Tree{Id: int64(treeId)}
		db.First(&tree)
		tree.GetGeodata()
		tree.GetArea()

		return tree
	})

	render.RenderJson(w, tree)
}

func nearbyTreesHandler(w http.ResponseWriter, r *http.Request) {
	var trees []Tree

	longitude := r.URL.Query().Get("long")
	latitude := r.URL.Query().Get("lat")
	log.Println("Long:", longitude, "Lat:", latitude)

	err := db.Model(Tree{}).Select("distinct trees.id, trees.latin_name, trees.common_name").
		Joins(fmt.Sprintf("INNER JOIN tree_geoms ON tree_geoms.latin_name = trees.latin_name AND ST_DWithin(ST_GeomFromText('POINT(%s %s)' , 4326)::geography, tree_geoms.geom, 160934 , true)", longitude, latitude)).
		Order("trees.latin_name asc").Scan(&trees)

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

	render.RenderJson(w, trees)
}

func treesHandler(w http.ResponseWriter, r *http.Request) {
	trees := cache.Get("trees", func() interface{} {
		var trees []Tree

		err := db.Model(Tree{}).Select("id, latin_name, common_name").Scan(&trees)
		if err != nil {
			log.Println(err)
		}

		return trees
	})

	render.RenderJson(w, trees)
}

func nearbyParksHandler(w http.ResponseWriter, r *http.Request) {
	var parks []NationalPark

	longitude := r.URL.Query().Get("long")
	latitude := r.URL.Query().Get("lat")
	log.Println("Long:", longitude, "Lat:", latitude)

	err := db.Model(NationalPark{}).
		Select("ST_AsGeoJSON(ST_CollectionExtract(geom, 3)) as geom_data, unit_name, unit_code").
		Where(fmt.Sprintf("ST_DWithin(ST_GeomFromText('POINT(%s %s)' , 4326)::geography, geom, 160934, true)", longitude, latitude)). // Within 100 miles -> 160934 meters
		Scan(&parks)
	if err != nil {
		log.Println(err)
	}

	render.RenderJson(w, parks)
}

func parksHandler(w http.ResponseWriter, r *http.Request) {
	parks := cache.Get("parks", func() interface{} {
		var parks []NationalPark
		db.Model(NationalPark{}).Select("ST_AsGeoJSON(ST_CollectionExtract(geom, 3)) as geom_data, unit_name, unit_code").Scan(&parks)
		return parks
	})

	render.RenderJson(w, parks)
}

func dbConnect(databaseUrl string) {
	log.Println("Connecting to database:", databaseUrl)
	var err error
	db, err = gorm.Open("postgres", databaseUrl)
	if err != nil {
		log.Println(err)
	}
	db.LogMode(true)
}

func init() {
	etcdHosts := os.Getenv("ETCD_HOSTS")
	if etcdHosts == "" {
		etcdHosts = "http://127.0.0.1:4001"
	}

	etcdClient := etcd.NewClient([]string{etcdHosts})

	resp, err := etcdClient.Get(DatabaseUrlKey, false, false)
	if err != nil {
		panic(err)
	}

	databaseUrl := resp.Node.Value
	dbConnect(databaseUrl)

	cache = gocache.New()
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/trees/nearby", nearbyTreesHandler)
	r.HandleFunc("/trees/{treeId}", showTreesHandler)
	r.HandleFunc("/trees", treesHandler)
	r.HandleFunc("/parks/nearby", nearbyParksHandler)
	r.HandleFunc("/parks", parksHandler)
	r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

	http.Handle("/", r)
	http.ListenAndServe(":3001", nil)
}
<|endoftext|>


<commit_before>package koding

import (
	"errors"
	"fmt"
	"strings"

	"github.com/dchest/validator"
	"github.com/koding/kite"
	"github.com/koding/kloud"
	"github.com/koding/kloud/eventer"
	"github.com/koding/kloud/protocol"
	"github.com/koding/logging"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/route53"
)

var ErrNoRecord = errors.New("no records available")

type DNS struct {
	Route53   *route53.Route53
	ZoneId    string
	Log       logging.Logger
	MachineId string
}

// Rename changes the domain from oldDomain to newDomain in a single transaction
func (d *DNS) Rename(oldDomain, newDomain string, currentIP string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Renaming domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    oldDomain,
					TTL:     300,
					Records: []string{currentIP},
				},
			},
			route53.Change{
				Action: "CREATE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    newDomain,
					TTL:     300,
					Records: []string{currentIP},
				},
			},
		},
	}

	d.Log.Info("[%s] Updating name of IP %s from %v to %v",
		d.MachineId, currentIP, oldDomain, newDomain)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

// Update changes the domains ip from oldIP to newIP in a single transaction
func (d *DNS) Update(domain string, oldIP, newIP string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Updating a domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: []string{oldIP}, // needs old ip
				},
			},
			route53.Change{
				Action: "CREATE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: []string{newIP},
				},
			},
		},
	}

	d.Log.Info("[%s] Updating domain %s IP from %v to %v",
		d.MachineId, domain, oldIP, newIP)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

func (d *DNS) DeleteDomain(domain string, ips ...string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Deleting domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: ips, // needs old ip
				},
			},
		},
	}

	d.Log.Info("[%s] Deleting domain name: %s which was associated to following ips: %v",
		d.MachineId, domain, ips)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

func (d *DNS) CreateDomain(domain string, ips ...string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Creating domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "CREATE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: ips,
				},
			},
		},
	}

	d.Log.Info("[%s] Creating domain name: %s to be associated with following ips: %v",
		d.MachineId, domain, ips)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

// Domain retrieves the record set for the given domain name
func (d *DNS) Domain(domain string) (route53.ResourceRecordSet, error) {
	lopts := &route53.ListOpts{
		Name: domain,
	}

	d.Log.Info("[%s] Fetching domain record for name: %s", d.MachineId, domain)

	resp, err := d.Route53.ListResourceRecordSets(d.ZoneId, lopts)
	if err != nil {
		return route53.ResourceRecordSet{}, err
	}

	if len(resp.Records) == 0 {
		return route53.ResourceRecordSet{}, ErrNoRecord
	}

	for _, r := range resp.Records {
		if strings.Contains(r.Name, domain) {
			return r, nil
		}

	}

	return route53.ResourceRecordSet{}, ErrNoRecord
}

func (p *Provider) InitDNS(opts *protocol.Machine) error {
	// If we have in cache use it
	if p.DNS != nil {
		return nil
	}

	a, err := p.NewClient(opts)
	if err != nil {
		return err
	}

	a.Log.Info("[%s] Creating Route53 instance", opts.MachineId)
	dns := route53.New(
		aws.Auth{
			AccessKey: a.Creds.AccessKey,
			SecretKey: a.Creds.SecretKey,
		},
		aws.Regions[DefaultRegion],
	)

	a.Log.Info("[%s] Searching for hosted zone: %s", opts.MachineId, p.HostedZone)
	hostedZones, err := dns.ListHostedZones("", 100)
	if err != nil {
		return err
	}

	var zoneId string
	for _, h := range hostedZones.HostedZones {
		// the "." point is here because hosteded zones are listed as
		// "dev.koding.io." , "koding.io." and so on
		if h.Name == p.HostedZone+"." {
			zoneId = route52.9.CleanZoneID(h.ID)
			break
		}
	}

	if zoneId == "" {
		return fmt.Errorf("Hosted zone with the name '%s' doesn't exist", p.HostedZone)
	}

	p.DNS = &DNS{
		Route53:   dns,
		ZoneId:    zoneId,
		Log:       p.Log,
		MachineId: opts.MachineId,
	}
	return nil
}

type domainSet struct {
	NewDomain string
}

func (p *Provider) DomainSet(r *kite.Request, c *kloud.Controller) (response interface{}, err error) {
	defer p.ResetAssignee(c.MachineId) // reset assignee after we are done

	args := &domainSet{}
	if err := r.Args.One().Unmarshal(args); err != nil {
		return nil, err
	}

	c.Eventer = &eventer.Events{}

	if args.NewDomain == "" {
		return nil, fmt.Errorf("newDomain argument is empty")
	}

	defer func() {
		if err != nil {
			p.Log.Error("Could not update domain. err: %s", err)

			//  change it that we don't leak information
			err = errors.New("Could not set domain. Please contact support")
		}
	}()

	machineData, ok := c.Machine.CurrentData.(*Machine)
	if !ok {
		return nil, fmt.Errorf("machine data is malformed %v", c.Machine.CurrentData)
	}

	if err := p.InitDNS(c.Machine); err != nil {
		return nil, err
	}

	if err := validateDomain(args.NewDomain, r.Username, p.HostedZone); err != nil {
		return nil, err
	}

	if err := p.DNS.Rename(machineData.Domain, args.NewDomain, machineData.IpAddress); err != nil {
		return nil, err
	}

	if err := p.Update(c.MachineId, &kloud.StorageData{
		Type: "domain",
		Data: map[string]interface{}{
			"domainName": args.NewDomain,
		},
	}); err != nil {
		return nil, err
	}

	return true, nil
}

func validateDomain(domain, username, hostedZone string) error {
	f := strings.TrimSuffix(domain, "."+username+"."+hostedZone)
	if f == domain {
		return fmt.Errorf("Domain is invalid (1) '%s'", domain)
	}

	if !strings.Contains(domain, username) {
		return fmt.Errorf("Domain doesn't contain username '%s'", username)
	}

	if !strings.Contains(domain, hostedZone) {
		return fmt.Errorf("Domain doesn't contain hostedzone '%s'", hostedZone)
	}

	if !validator.IsValidDomain(domain) {
		return fmt.Errorf("Domain is invalid (2) '%s'", domain)
	}

	return nil
}
<commit_msg>Route53: fix typo<commit_after>package koding

import (
	"errors"
	"fmt"
	"strings"

	"github.com/dchest/validator"
	"github.com/koding/kite"
	"github.com/koding/kloud"
	"github.com/koding/kloud/eventer"
	"github.com/koding/kloud/protocol"
	"github.com/koding/logging"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/route53"
)

var ErrNoRecord = errors.New("no records available")

type DNS struct {
	Route53   *route53.Route53
	ZoneId    string
	Log       logging.Logger
	MachineId string
}

// Rename changes the domain from oldDomain to newDomain in a single transaction
func (d *DNS) Rename(oldDomain, newDomain string, currentIP string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Renaming domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    oldDomain,
					TTL:     300,
					Records: []string{currentIP},
				},
			},
			route53.Change{
				Action: "CREATE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    newDomain,
					TTL:     300,
					Records: []string{currentIP},
				},
			},
		},
	}

	d.Log.Info("[%s] Updating name of IP %s from %v to %v",
		d.MachineId, currentIP, oldDomain, newDomain)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

// Update changes the domains ip from oldIP to newIP in a single transaction
func (d *DNS) Update(domain string, oldIP, newIP string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Updating a domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: []string{oldIP}, // needs old ip
				},
			},
			route53.Change{
				Action: "CREATE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: []string{newIP},
				},
			},
		},
	}

	d.Log.Info("[%s] Updating domain %s IP from %v to %v",
		d.MachineId, domain, oldIP, newIP)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

func (d *DNS) DeleteDomain(domain string, ips ...string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Deleting domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: ips, // needs old ip
				},
			},
		},
	}

	d.Log.Info("[%s] Deleting domain name: %s which was associated to following ips: %v",
		d.MachineId, domain, ips)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

func (d *DNS) CreateDomain(domain string, ips ...string) error {
	change := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Creating domain",
		Changes: []route53.Change{
			route53.Change{
				Action: "CREATE",
				Record: route53.ResourceRecordSet{
					Type:    "A",
					Name:    domain,
					TTL:     300,
					Records: ips,
				},
			},
		},
	}

	d.Log.Info("[%s] Creating domain name: %s to be associated with following ips: %v",
		d.MachineId, domain, ips)

	_, err := d.Route53.ChangeResourceRecordSets(d.ZoneId, change)
	if err != nil {
		return err
	}

	return nil
}

// Domain retrieves the record set for the given domain name
func (d *DNS) Domain(domain string) (route53.ResourceRecordSet, error) {
	lopts := &route53.ListOpts{
		Name: domain,
	}

	d.Log.Info("[%s] Fetching domain record for name: %s", d.MachineId, domain)

	resp, err := d.Route53.ListResourceRecordSets(d.ZoneId, lopts)
	if err != nil {
		return route53.ResourceRecordSet{}, err
	}

	if len(resp.Records) == 0 {
		return route53.ResourceRecordSet{}, ErrNoRecord
	}

	for _, r := range resp.Records {
		if strings.Contains(r.Name, domain) {
			return r, nil
		}

	}

	return route53.ResourceRecordSet{}, ErrNoRecord
}

func (p *Provider) InitDNS(opts *protocol.Machine) error {
	// If we have in cache use it
	if p.DNS != nil {
		return nil
	}

	a, err := p.NewClient(opts)
	if err != nil {
		return err
	}

	a.Log.Info("[%s] Creating Route53 instance", opts.MachineId)
	dns := route53.New(
		aws.Auth{
			AccessKey: a.Creds.AccessKey,
			SecretKey: a.Creds.SecretKey,
		},
		aws.Regions[DefaultRegion],
	)

	a.Log.Info("[%s] Searching for hosted zone: %s", opts.MachineId, p.HostedZone)
	hostedZones, err := dns.ListHostedZones("", 100)
	if err != nil {
		return err
	}

	var zoneId string
	for _, h := range hostedZones.HostedZones {
		// the "." point is here because hosteded zones are listed as
		// "dev.koding.io." , "koding.io." and so on
		if h.Name == p.HostedZone+"." {
			zoneId = route52.CleanZoneID(h.ID)
			break
		}
	}

	if zoneId == "" {
		return fmt.Errorf("Hosted zone with the name '%s' doesn't exist", p.HostedZone)
	}

	p.DNS = &DNS{
		Route53:   dns,
		ZoneId:    zoneId,
		Log:       p.Log,
		MachineId: opts.MachineId,
	}
	return nil
}

type domainSet struct {
	NewDomain string
}

func (p *Provider) DomainSet(r *kite.Request, c *kloud.Controller) (response interface{}, err error) {
	defer p.ResetAssignee(c.MachineId) // reset assignee after we are done

	args := &domainSet{}
	if err := r.Args.One().Unmarshal(args); err != nil {
		return nil, err
	}

	c.Eventer = &eventer.Events{}

	if args.NewDomain == "" {
		return nil, fmt.Errorf("newDomain argument is empty")
	}

	defer func() {
		if err != nil {
			p.Log.Error("Could not update domain. err: %s", err)

			//  change it that we don't leak information
			err = errors.New("Could not set domain. Please contact support")
		}
	}()

	machineData, ok := c.Machine.CurrentData.(*Machine)
	if !ok {
		return nil, fmt.Errorf("machine data is malformed %v", c.Machine.CurrentData)
	}

	if err := p.InitDNS(c.Machine); err != nil {
		return nil, err
	}

	if err := validateDomain(args.NewDomain, r.Username, p.HostedZone); err != nil {
		return nil, err
	}

	if err := p.DNS.Rename(machineData.Domain, args.NewDomain, machineData.IpAddress); err != nil {
		return nil, err
	}

	if err := p.Update(c.MachineId, &kloud.StorageData{
		Type: "domain",
		Data: map[string]interface{}{
			"domainName": args.NewDomain,
		},
	}); err != nil {
		return nil, err
	}

	return true, nil
}

func validateDomain(domain, username, hostedZone string) error {
	f := strings.TrimSuffix(domain, "."+username+"."+hostedZone)
	if f == domain {
		return fmt.Errorf("Domain is invalid (1) '%s'", domain)
	}

	if !strings.Contains(domain, username) {
		return fmt.Errorf("Domain doesn't contain username '%s'", username)
	}

	if !strings.Contains(domain, hostedZone) {
		return fmt.Errorf("Domain doesn't contain hostedzone '%s'", hostedZone)
	}

	if !validator.IsValidDomain(domain) {
		return fmt.Errorf("Domain is invalid (2) '%s'", domain)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package planbuilder

import (
	"fmt"
	"sync"
	"testing"

	"vitess.io/vitess/go/sync2"
	"vitess.io/vitess/go/vt/log"

	"github.com/stretchr/testify/require"

	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vtgate/semantics"
)

func TestSimplifyUnsupportedQuery(t *testing.T) {
	query := "select user.id, user.name, count(*), unsharded.name from user join unsharded where unsharded.id = 42 group by user.id"
	vschema := &vschemaWrapper{
		v: loadSchema(t, "schema_test.json", true),
	}
	vschema.version = Gen4
	stmt, reserved, err := sqlparser.Parse2(query)
	require.NoError(t, err)
	result, _ := sqlparser.RewriteAST(stmt, vschema.currentDb(), sqlparser.SQLSelectLimitUnset)
	vschema.currentDb()

	reservedVars := sqlparser.NewReservedVars("vtg", reserved)
	plan, err := BuildFromStmt(query, result.AST, reservedVars, vschema, result.BindVarNeeds, true, true)
	out := getPlanOrErrorOutput(err, plan)

	simplified := simplifyStatement(result.AST.(sqlparser.SelectStatement), vschema.currentDb(), vschema, func(statement sqlparser.SelectStatement) bool {
		plan, err := BuildFromStmt(query, statement, reservedVars, vschema, result.BindVarNeeds, true, true)
		out2 := getPlanOrErrorOutput(err, plan)
		return out == out2
	})

	t.Fatal(sqlparser.String(simplified))
}

func TestFindAllExpressions(t *testing.T) {
	query := "select user.id, count(*), unsharded.name from user join unsharded where unsharded.id = 42 and name = 'foo' and user.id = unsharded.id"
	ast, err := sqlparser.Parse(query)
	require.NoError(t, err)
	ch := make(chan cursorItem)
	abort := &sync2.AtomicBool{}
	findExpressions(ast.(sqlparser.SelectStatement), ch, abort)
	for cursor := range ch {
		fmt.Println(sqlparser.String(cursor.expr))
		cursor.replace(sqlparser.NewIntLiteral("1"))
		fmt.Println(sqlparser.String(ast))
		cursor.replace(cursor.expr)
		if _, ok := cursor.expr.(*sqlparser.FuncExpr); ok {
			abort.Set(true)
			break
		}
		cursor.wg.Done()
	}
}

func simplifyStatement(
	in sqlparser.SelectStatement,
	currentDB string,
	si semantics.SchemaInformation,
	test func(sqlparser.SelectStatement) bool,
) sqlparser.SelectStatement {
	semTable, err := semantics.Analyze(in, currentDB, si)
	if err != nil {
		return nil
	}

	// we start by removing one table at a time, and see if we still have an interesting plan
	for idx := range semTable.Tables {
		clone := sqlparser.CloneSelectStatement(in)
		inner, err := semantics.Analyze(clone, currentDB, si)
		if err != nil {
			panic(err) // this should never happen
		}
		searchedTS := semantics.SingleTableSet(idx)
		simplified := removeTable(clone, searchedTS, inner)
		if simplified && test(clone) {
			name, _ := semTable.Tables[idx].Name()
			log.Errorf("removed table %s", name)
			return simplifyStatement(clone, currentDB, si, test)
		}
	}

	// if we get here, we couldn't find a simpler query by just removing one table,
	// we try to remove select expressions next
	ch := make(chan cursorItem)
	abort := &sync2.AtomicBool{}
	findExpressions(in, ch, abort)
	for cursor := range ch {
		s := &sqlparser.Shrinker{Orig: cursor.expr}
		newExpr := s.Next()
		for newExpr != nil {
			cursor.replace(newExpr)
			if test(in) {
				log.Errorf("simplified expression: %s -> %s", sqlparser.String(cursor.expr), sqlparser.String(newExpr))
				return simplifyStatement(in, currentDB, si, test)
			}
			newExpr = s.Next()
		}
		// if we get here, we failed to simplify this expression,
		// so we put back in the original expression
		cursor.replace(cursor.expr)
		cursor.wg.Done()
	}

	return in
}

// removeTable removes the table with the given index from the select statement, which includes the FROM clause
// but also all expressions and predicates that depend on the table
func removeTable(clone sqlparser.SelectStatement, searchedTS semantics.TableSet, inner *semantics.SemTable) bool {
	simplified := false
	sqlparser.Rewrite(clone, func(cursor *sqlparser.Cursor) bool {
		switch node := cursor.Node().(type) {
		case *sqlparser.JoinTableExpr:
			lft, ok := node.LeftExpr.(*sqlparser.AliasedTableExpr)
			if ok {
				ts := inner.TableSetFor(lft)
				if ts == searchedTS {
					cursor.Replace(node.RightExpr)
					simplified = true
				}
			}
			rgt, ok := node.RightExpr.(*sqlparser.AliasedTableExpr)
			if ok {
				ts := inner.TableSetFor(rgt)
				if ts == searchedTS {
					cursor.Replace(node.LeftExpr)
					simplified = true
				}
			}
		case *sqlparser.Select:
			if len(node.From) == 1 {
				return true
			}
			for i, tbl := range node.From {
				lft, ok := tbl.(*sqlparser.AliasedTableExpr)
				if ok {
					ts := inner.TableSetFor(lft)
					if ts == searchedTS {
						node.From = append(node.From[:i], node.From[i+1:]...)
						simplified = true
						return true
					}
				}
			}
		case *sqlparser.Where:
			exprs := sqlparser.SplitAndExpression(nil, node.Expr)
			var newPredicate sqlparser.Expr
			for _, expr := range exprs {
				if !inner.RecursiveDeps(expr).IsOverlapping(searchedTS) {
					newPredicate = sqlparser.AndExpressions(newPredicate, expr)
				}
			}
			node.Expr = newPredicate
		case sqlparser.SelectExprs:
			_, isSel := cursor.Parent().(*sqlparser.Select)
			if !isSel {
				return true
			}

			var newExprs sqlparser.SelectExprs
			for _, ae := range node {
				expr, ok := ae.(*sqlparser.AliasedExpr)
				if !ok {
					newExprs = append(newExprs, ae)
					continue
				}
				if !inner.RecursiveDeps(expr.Expr).IsOverlapping(searchedTS) || sqlparser.ContainsAggregation(expr.Expr) {
					newExprs = append(newExprs, ae)
				}
			}
			cursor.Replace(newExprs)
		case sqlparser.GroupBy:
			var newExprs sqlparser.GroupBy
			for _, expr := range node {
				if !inner.RecursiveDeps(expr).IsOverlapping(searchedTS) || sqlparser.ContainsAggregation(expr) {
					newExprs = append(newExprs, expr)
				}
			}
			cursor.Replace(newExprs)
		case sqlparser.OrderBy:
			var newExprs sqlparser.OrderBy
			for _, expr := range node {
				if !inner.RecursiveDeps(expr.Expr).IsOverlapping(searchedTS) || sqlparser.ContainsAggregation(expr.Expr) {
					newExprs = append(newExprs, expr)
				}
			}

			cursor.Replace(newExprs)
		}
		return true
	}, nil)
	return simplified
}

type cursorItem struct {
	expr    sqlparser.Expr
	replace func(replaceWith sqlparser.Expr)
	wg      *sync.WaitGroup
}

func newCursorItem(ch chan<- cursorItem, expr sqlparser.Expr, replace func(replaceWith sqlparser.Expr)) {
	wg := &sync.WaitGroup{}
	wg.Add(1)
	ch <- cursorItem{
		expr:    expr,
		replace: replace,
		wg:      wg,
	}
	wg.Wait()
}

func findExpressions(clone sqlparser.SelectStatement, ch chan<- cursorItem, abort *sync2.AtomicBool) {
	go func() {
		sqlparser.Rewrite(clone, func(cursor *sqlparser.Cursor) bool {
			switch node := cursor.Node().(type) {
			case sqlparser.SelectExprs:
				_, isSel := cursor.Parent().(*sqlparser.Select)
				if !isSel {
					return true
				}
				for _, ae := range node {
					expr, ok := ae.(*sqlparser.AliasedExpr)
					if !ok {
						continue
					}
					newCursorItem(ch, expr.Expr, func(replaceWith sqlparser.Expr) {
						expr.Expr = replaceWith
					})
					if abort.Get() {
						close(ch)
						return false
					}
				}

			case *sqlparser.Where:
				exprs := sqlparser.SplitAndExpression(nil, node.Expr)
				for idx := 0; idx < len(exprs); idx++ {
					expr := exprs[idx]
					newCursorItem(ch, expr, func(replaceWith sqlparser.Expr) {
						exprs[idx] = replaceWith
						node.Expr = sqlparser.AndExpressions(exprs...)
					})
					if abort.Get() {
						close(ch)
						return false
					}
				}
			}
			return true
		}, nil)
		close(ch)
	}()
}
<commit_msg>feat: extended the cursor API for expression simplification - added remove and restore<commit_after>/*
Copyright 2021 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package planbuilder

import (
	"fmt"
	"sync"
	"testing"

	"vitess.io/vitess/go/sync2"
	"vitess.io/vitess/go/vt/log"

	"github.com/stretchr/testify/require"

	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vtgate/semantics"
)

func TestSimplifyUnsupportedQuery(t *testing.T) {
	query := "select user.id, user.name, count(*), unsharded.name, 12 from user join unsharded where unsharded.id = 42"
	vschema := &vschemaWrapper{
		v: loadSchema(t, "schema_test.json", true),
	}
	vschema.version = Gen4
	stmt, reserved, err := sqlparser.Parse2(query)
	require.NoError(t, err)
	result, _ := sqlparser.RewriteAST(stmt, vschema.currentDb(), sqlparser.SQLSelectLimitUnset)
	vschema.currentDb()

	reservedVars := sqlparser.NewReservedVars("vtg", reserved)
	plan, err := BuildFromStmt(query, result.AST, reservedVars, vschema, result.BindVarNeeds, true, true)
	out := getPlanOrErrorOutput(err, plan)

	simplified := simplifyStatement(result.AST.(sqlparser.SelectStatement), vschema.currentDb(), vschema, func(statement sqlparser.SelectStatement) bool {
		plan, err := BuildFromStmt(query, statement, reservedVars, vschema, result.BindVarNeeds, true, true)
		out2 := getPlanOrErrorOutput(err, plan)
		return out == out2
	})

	t.Fatal(sqlparser.String(simplified))
}

func TestFindAllExpressions(t *testing.T) {
	query := "select user.selectExpr1, unsharded.selectExpr2 from user join unsharded on user.joinCond = unsharded.joinCond where unsharded.wherePred = 42 and wherePred = 'foo' and user.id = unsharded.id"
	ast, err := sqlparser.Parse(query)
	require.NoError(t, err)
	ch := make(chan cursorItem)
	findExpressions(ast.(sqlparser.SelectStatement), ch)
	for cursor := range ch {
		exploreExpression(cursor, ast)
	}
}

func exploreExpression(cursor cursorItem, ast sqlparser.Statement) {
	defer cursor.wg.Done()
	fmt.Printf(">> found expression: %s\n", sqlparser.String(cursor.expr))
	cursor.replace(sqlparser.NewIntLiteral("1"))
	fmt.Printf("replace it with literal: %s\n", sqlparser.String(ast))
	cursor.restore()
	fmt.Printf("restore: %s\n", sqlparser.String(ast))
	cursor.remove()
	fmt.Printf("remove: %s\n", sqlparser.String(ast))
	cursor.restore()
	fmt.Printf("restore: %s\n", sqlparser.String(ast))
}

func TestAbortExpressionCursor(t *testing.T) {
	query := "select user.id, count(*), unsharded.name from user join unsharded on 13 = 14 where unsharded.id = 42 and name = 'foo' and user.id = unsharded.id"
	ast, err := sqlparser.Parse(query)
	require.NoError(t, err)
	ch := make(chan cursorItem)
	findExpressions(ast.(sqlparser.SelectStatement), ch)
	for cursor := range ch {
		fmt.Println(sqlparser.String(cursor.expr))
		cursor.replace(sqlparser.NewIntLiteral("1"))
		fmt.Println(sqlparser.String(ast))
		cursor.replace(cursor.expr)
		if _, ok := cursor.expr.(*sqlparser.FuncExpr); ok {
			cursor.abort()
			break
		}
		cursor.wg.Done()
	}
}

func simplifyStatement(
	in sqlparser.SelectStatement,
	currentDB string,
	si semantics.SchemaInformation,
	test func(sqlparser.SelectStatement) bool,
) sqlparser.SelectStatement {
	semTable, err := semantics.Analyze(in, currentDB, si)
	if err != nil {
		return nil
	}

	// we start by removing one table at a time, and see if we still have an interesting plan
	for idx := range semTable.Tables {
		clone := sqlparser.CloneSelectStatement(in)
		inner, err := semantics.Analyze(clone, currentDB, si)
		if err != nil {
			panic(err) // this should never happen
		}
		searchedTS := semantics.SingleTableSet(idx)
		simplified := removeTable(clone, searchedTS, inner)
		if simplified && test(clone) {
			name, _ := semTable.Tables[idx].Name()
			log.Errorf("removed table %s", name)
			return simplifyStatement(clone, currentDB, si, test)
		}
	}

	// if we get here, we couldn't find a simpler query by just removing one table,
	// we try to remove select expressions next
	ch := make(chan cursorItem)
	findExpressions(in, ch)
	for cursor := range ch {
		s := &sqlparser.Shrinker{Orig: cursor.expr}
		newExpr := s.Next()
		for newExpr != nil {
			cursor.replace(newExpr)
			if test(in) {
				log.Errorf("simplified expression: %s -> %s", sqlparser.String(cursor.expr), sqlparser.String(newExpr))
				cursor.abort()
				return simplifyStatement(in, currentDB, si, test)
			}
			newExpr = s.Next()
		}
		// if we get here, we failed to simplify this expression,
		// so we put back in the original expression
		cursor.replace(cursor.expr)
		cursor.wg.Done()
	}

	return in
}

// removeTable removes the table with the given index from the select statement, which includes the FROM clause
// but also all expressions and predicates that depend on the table
func removeTable(clone sqlparser.SelectStatement, searchedTS semantics.TableSet, inner *semantics.SemTable) bool {
	simplified := false
	sqlparser.Rewrite(clone, func(cursor *sqlparser.Cursor) bool {
		switch node := cursor.Node().(type) {
		case *sqlparser.JoinTableExpr:
			lft, ok := node.LeftExpr.(*sqlparser.AliasedTableExpr)
			if ok {
				ts := inner.TableSetFor(lft)
				if ts == searchedTS {
					cursor.Replace(node.RightExpr)
					simplified = true
				}
			}
			rgt, ok := node.RightExpr.(*sqlparser.AliasedTableExpr)
			if ok {
				ts := inner.TableSetFor(rgt)
				if ts == searchedTS {
					cursor.Replace(node.LeftExpr)
					simplified = true
				}
			}
		case *sqlparser.Select:
			if len(node.From) == 1 {
				return true
			}
			for i, tbl := range node.From {
				lft, ok := tbl.(*sqlparser.AliasedTableExpr)
				if ok {
					ts := inner.TableSetFor(lft)
					if ts == searchedTS {
						node.From = append(node.From[:i], node.From[i+1:]...)
						simplified = true
						return true
					}
				}
			}
		case *sqlparser.Where:
			exprs := sqlparser.SplitAndExpression(nil, node.Expr)
			var newPredicate sqlparser.Expr
			for _, expr := range exprs {
				if !inner.RecursiveDeps(expr).IsOverlapping(searchedTS) {
					newPredicate = sqlparser.AndExpressions(newPredicate, expr)
				}
			}
			node.Expr = newPredicate
		case sqlparser.SelectExprs:
			_, isSel := cursor.Parent().(*sqlparser.Select)
			if !isSel {
				return true
			}

			var newExprs sqlparser.SelectExprs
			for _, ae := range node {
				expr, ok := ae.(*sqlparser.AliasedExpr)
				if !ok {
					newExprs = append(newExprs, ae)
					continue
				}
				if !inner.RecursiveDeps(expr.Expr).IsOverlapping(searchedTS) || sqlparser.ContainsAggregation(expr.Expr) {
					newExprs = append(newExprs, ae)
				}
			}
			cursor.Replace(newExprs)
		case sqlparser.GroupBy:
			var newExprs sqlparser.GroupBy
			for _, expr := range node {
				if !inner.RecursiveDeps(expr).IsOverlapping(searchedTS) || sqlparser.ContainsAggregation(expr) {
					newExprs = append(newExprs, expr)
				}
			}
			cursor.Replace(newExprs)
		case sqlparser.OrderBy:
			var newExprs sqlparser.OrderBy
			for _, expr := range node {
				if !inner.RecursiveDeps(expr.Expr).IsOverlapping(searchedTS) || sqlparser.ContainsAggregation(expr.Expr) {
					newExprs = append(newExprs, expr)
				}
			}

			cursor.Replace(newExprs)
		}
		return true
	}, nil)
	return simplified
}

type cursorItem struct {
	expr        sqlparser.Expr
	replace     func(replaceWith sqlparser.Expr)
	remove      func()
	restore     func()
	wg          *sync.WaitGroup
	abortMarker *sync2.AtomicBool
}

func (i cursorItem) abort() {
	i.abortMarker.Set(true)
}

func newCursorItem(
	ch chan<- cursorItem,
	expr sqlparser.Expr,
	abort *sync2.AtomicBool,
	replace func(replaceWith sqlparser.Expr),
	remove func(),
	restore func(),
) {
	wg := &sync.WaitGroup{}
	wg.Add(1)
	ch <- cursorItem{
		expr:        expr,
		replace:     replace,
		remove:      remove,
		restore:     restore,
		wg:          wg,
		abortMarker: abort,
	}
	wg.Wait()
}

func findExpressions(clone sqlparser.SelectStatement, ch chan<- cursorItem) {
	abort := &sync2.AtomicBool{}
	go func() {
		sqlparser.Rewrite(clone, func(cursor *sqlparser.Cursor) bool {
			switch node := cursor.Node().(type) {
			case sqlparser.SelectExprs:
				_, isSel := cursor.Parent().(*sqlparser.Select)
				if !isSel {
					return true
				}
				for idx := 0; idx < len(node); idx++ {
					ae := node[idx]
					expr, ok := ae.(*sqlparser.AliasedExpr)
					if !ok {
						continue
					}
					removed := false
					original := sqlparser.CloneExpr(expr.Expr)
					newCursorItem(
						ch, expr.Expr, abort,
						/*replace*/ func(replaceWith sqlparser.Expr) {
							if removed {
								panic("cant replace after remove without restore")
							}
							expr.Expr = replaceWith
						},
						/*remove*/ func() {
							if removed {
								panic("can't remove twice, silly")
							}
							withoutElement := append(node[:idx], node[idx+1:]...)
							cursor.Replace(withoutElement)
							node = withoutElement
							removed = true
						},
						/*restore*/ func() {
							if removed {
								front := make(sqlparser.SelectExprs, idx)
								copy(front, node[:idx])
								back := make(sqlparser.SelectExprs, len(node)-idx)
								copy(back, node[idx:])
								frontWithRestoredExpr := append(front, ae)
								node = append(frontWithRestoredExpr, back...)
								cursor.Replace(node)
								removed = false
								return
							}
							expr.Expr = original
						},
					)
					if abort.Get() {
						close(ch)
						return false
					}
				}
			case *sqlparser.Where:
				exprs := sqlparser.SplitAndExpression(nil, node.Expr)
				set := func(input []sqlparser.Expr) {
					node.Expr = sqlparser.AndExpressions(input...)
					exprs = input
				}
				for idx := 0; idx < len(exprs); idx++ {
					expr := exprs[idx]
					removed := false
					newCursorItem(ch, expr, abort,
						func(replaceWith sqlparser.Expr) {
							if removed {
								panic("cant replace after remove without restore")
							}
							exprs[idx] = replaceWith
							set(exprs)
						},
						/*remove*/ func() {
							if removed {
								panic("can't remove twice, silly")
							}
							set(append(exprs[:idx], exprs[idx+1:]...))
							removed = true
						},
						/*restore*/ func() {
							if removed {
								front := make([]sqlparser.Expr, idx)
								copy(front, exprs[:idx])
								back := make([]sqlparser.Expr, len(exprs)-idx)
								copy(back, exprs[idx:])
								frontWithRestoredExpr := append(front, expr)
								set(append(frontWithRestoredExpr, back...))
								removed = false
								return
							}
							exprs[idx] = expr
							set(exprs)
						})
					if abort.Get() {
						return false
					}
				}
			case *sqlparser.JoinCondition:

			}
			return true
		}, nil)
		close(ch)
	}()
}
<|endoftext|>


<commit_before>package main

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

const (
    _ = iota
    TakeBook
    ReturnBook
    GetAvailability
)

type SimpleLibrary struct {
    Books map[string]*Book
    registeredCopyCount map[string]int
    availableCopyCount map[string]int
    librarians chan struct{}
}

type BookError struct {
    ISBN string
}

type TooManyCopiesBookError struct {
    BookError
}

type NotFoundBookError struct {
    BookError
}

type NotAvailableBookError struct {
    BookError
}

type AllCopiesAvailableBookError struct {
    BookError
}

func (sl *SimpleLibrary) MarshalJSON() ([]byte, error) {
    return json.Marshal(sl)
}

func (sl *SimpleLibrary) UnmarshalJSON(data []byte) error {
    return json.Unmarshal(data, sl)
}

func (sl *SimpleLibrary) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    return e.EncodeElement(sl, start)
}

func (sl *SimpleLibrary) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    return d.DecodeElement(sl, &start)
}

func (e *TooManyCopiesBookError) Error() string {
    return fmt.Sprintf("Има 4 копия на книга %v", e.ISBN)
}

func (e *NotFoundBookError) Error() string {
    return fmt.Sprintf("Непозната книга %v", e.ISBN)
}

func (e *NotAvailableBookError) Error() string {
    return fmt.Sprintf("Няма наличност на книга %v", e.ISBN)
}

func (e *AllCopiesAvailableBookError) Error() string {
    return fmt.Sprintf("Всички копия са налични %v", e.ISBN)
}

func (sl *SimpleLibrary) addBook(book *Book) (registeredCopyCount int, err error) {
    if sl.registeredCopyCount[book.ISBN] >= 4 {
        err = &TooManyCopiesBookError{BookError{book.ISBN}}
    } else {
        sl.Books[book.ISBN] = book
        sl.registeredCopyCount[book.ISBN]++
        sl.availableCopyCount[book.ISBN]++
        registeredCopyCount = sl.registeredCopyCount[book.ISBN]
    }

    return
}

func (sl *SimpleLibrary) AddBookJSON(data []byte) (int, error) {
    var book *Book
    json.Unmarshal(data, book)
    return sl.addBook(book)
}

func (sl *SimpleLibrary) AddBookXML(data []byte) (int, error) {
    var book *Book
    xml.Unmarshal(data, book)
    return sl.addBook(book)
}

func (sl *SimpleLibrary) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
    requests := make(chan LibraryRequest)
    responses := make(chan LibraryResponse)

    go func() {
        for request := range requests {
            <-sl.librarians
            isbn := request.GetISBN()
            response := new(SimpleLibraryResponse)

            switch request.GetType() {
            case TakeBook:
                if book, isBookRegistered := sl.Books[isbn]; isBookRegistered && sl.availableCopyCount[isbn] > 0 {
                    response.book = book
                    sl.availableCopyCount[isbn]--
                } else if !isBookRegistered {
                    response.err = &NotFoundBookError{BookError{isbn}}
                } else {
                    response.err = &NotAvailableBookError{BookError{isbn}}
                }

            case ReturnBook:
                if _, isBookRegistered := sl.Books[isbn]; isBookRegistered && sl.availableCopyCount[isbn] < sl.registeredCopyCount[isbn] {
                    sl.availableCopyCount[isbn]++
                } else if !isBookRegistered {
                    response.err = &NotFoundBookError{BookError{isbn}}
                } else {
                    response.err = &AllCopiesAvailableBookError{BookError{isbn}}
                }

            case GetAvailability:
                response.registeredCopyCount = sl.registeredCopyCount[isbn]
                response.availableCopyCount = sl.availableCopyCount[isbn]
            }

            responses <- response
            sl.librarians <- struct{}{}
        }
    }()

    return requests, responses
}

func NewLibrary(librarians int) Library {
    return &SimpleLibrary{
        Books: make(map[string]*Book),
        registeredCopyCount: make(map[string]int),
        availableCopyCount: make(map[string]int),
        librarians: make(chan struct{}, librarians),
    }
}
<commit_msg>fix: Book struct must be initialized before unmarshalling through a pointer to it<commit_after>package main

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

const (
    _ = iota
    TakeBook
    ReturnBook
    GetAvailability
)

type SimpleLibrary struct {
    Books map[string]*Book
    registeredCopyCount map[string]int
    availableCopyCount map[string]int
    librarians chan struct{}
}

type BookError struct {
    ISBN string
}

type TooManyCopiesBookError struct {
    BookError
}

type NotFoundBookError struct {
    BookError
}

type NotAvailableBookError struct {
    BookError
}

type AllCopiesAvailableBookError struct {
    BookError
}

func (sl *SimpleLibrary) MarshalJSON() ([]byte, error) {
    return json.Marshal(sl)
}

func (sl *SimpleLibrary) UnmarshalJSON(data []byte) error {
    return json.Unmarshal(data, sl)
}

func (sl *SimpleLibrary) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    return e.EncodeElement(sl, start)
}

func (sl *SimpleLibrary) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    return d.DecodeElement(sl, &start)
}

func (e *TooManyCopiesBookError) Error() string {
    return fmt.Sprintf("Има 4 копия на книга %v", e.ISBN)
}

func (e *NotFoundBookError) Error() string {
    return fmt.Sprintf("Непозната книга %v", e.ISBN)
}

func (e *NotAvailableBookError) Error() string {
    return fmt.Sprintf("Няма наличност на книга %v", e.ISBN)
}

func (e *AllCopiesAvailableBookError) Error() string {
    return fmt.Sprintf("Всички копия са налични %v", e.ISBN)
}

func (sl *SimpleLibrary) addBook(book *Book) (registeredCopyCount int, err error) {
    if sl.registeredCopyCount[book.ISBN] >= 4 {
        err = &TooManyCopiesBookError{BookError{book.ISBN}}
    } else {
        sl.Books[book.ISBN] = book
        sl.registeredCopyCount[book.ISBN]++
        sl.availableCopyCount[book.ISBN]++
        registeredCopyCount = sl.registeredCopyCount[book.ISBN]
    }

    return
}

func (sl *SimpleLibrary) AddBookJSON(data []byte) (int, error) {
    book := &Book{}
    json.Unmarshal(data, book)
    return sl.addBook(book)
}

func (sl *SimpleLibrary) AddBookXML(data []byte) (int, error) {
    book := &Book{}
    xml.Unmarshal(data, book)
    return sl.addBook(book)
}

func (sl *SimpleLibrary) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
    requests := make(chan LibraryRequest)
    responses := make(chan LibraryResponse)

    go func() {
        for request := range requests {
            <-sl.librarians
            isbn := request.GetISBN()
            response := new(SimpleLibraryResponse)

            switch request.GetType() {
            case TakeBook:
                if book, isBookRegistered := sl.Books[isbn]; isBookRegistered && sl.availableCopyCount[isbn] > 0 {
                    response.book = book
                    sl.availableCopyCount[isbn]--
                } else if !isBookRegistered {
                    response.err = &NotFoundBookError{BookError{isbn}}
                } else {
                    response.err = &NotAvailableBookError{BookError{isbn}}
                }

            case ReturnBook:
                if _, isBookRegistered := sl.Books[isbn]; isBookRegistered && sl.availableCopyCount[isbn] < sl.registeredCopyCount[isbn] {
                    sl.availableCopyCount[isbn]++
                } else if !isBookRegistered {
                    response.err = &NotFoundBookError{BookError{isbn}}
                } else {
                    response.err = &AllCopiesAvailableBookError{BookError{isbn}}
                }

            case GetAvailability:
                response.registeredCopyCount = sl.registeredCopyCount[isbn]
                response.availableCopyCount = sl.availableCopyCount[isbn]
            }

            responses <- response
            sl.librarians <- struct{}{}
        }
    }()

    return requests, responses
}

func NewLibrary(librarians int) Library {
    return &SimpleLibrary{
        Books: make(map[string]*Book),
        registeredCopyCount: make(map[string]int),
        availableCopyCount: make(map[string]int),
        librarians: make(chan struct{}, librarians),
    }
}
<|endoftext|>


<commit_before>package validation

import (
	"testing"

	"github.com/bytom/consensus"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
	"github.com/bytom/protocol/state"
)

func TestCheckBlockTime(t *testing.T) {
	cases := []struct {
		blockTime  uint64
		parentTime uint64
		err        error
	}{
		{
			blockTime:  1520000001,
			parentTime: 1520000000,
			err:        nil,
		},
		{
			blockTime:  1510000000,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
		{
			blockTime:  9999999999,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
	}

	parent := &state.BlockNode{}
	block := &bc.Block{
		BlockHeader: &bc.BlockHeader{},
	}

	for i, c := range cases {
		parent.Timestamp = c.parentTime
		block.Timestamp = c.blockTime
		if err := checkBlockTime(block, parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestCheckCoinbaseAmount(t *testing.T) {
	cases := []struct {
		txs    []*types.Tx
		amount uint64
		err    error
	}{
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 5000,
			err:    nil,
		},
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 6000,
			err:    errWrongCoinbaseTransaction,
		},
		{
			txs:    []*types.Tx{},
			amount: 5000,
			err:    errWrongCoinbaseTransaction,
		},
	}

	block := new(types.Block)
	for i, c := range cases {
		block.Transactions = c.txs
		if err := checkCoinbaseAmount(types.MapBlock(block), c.amount); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}
<commit_msg>add unit test for block header<commit_after>package validation

import (
	"testing"

	"github.com/bytom/consensus"
	"github.com/bytom/mining/tensority"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
	"github.com/bytom/protocol/state"
)

func TestCheckBlockTime(t *testing.T) {
	cases := []struct {
		blockTime  uint64
		parentTime uint64
		err        error
	}{
		{
			blockTime:  1520000001,
			parentTime: 1520000000,
			err:        nil,
		},
		{
			blockTime:  1510000000,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
		{
			blockTime:  9999999999,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
	}

	parent := &state.BlockNode{}
	block := &bc.Block{
		BlockHeader: &bc.BlockHeader{},
	}

	for i, c := range cases {
		parent.Timestamp = c.parentTime
		block.Timestamp = c.blockTime
		if err := checkBlockTime(block, parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestCheckCoinbaseAmount(t *testing.T) {
	cases := []struct {
		txs    []*types.Tx
		amount uint64
		err    error
	}{
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 5000,
			err:    nil,
		},
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 6000,
			err:    errWrongCoinbaseTransaction,
		},
		{
			txs:    []*types.Tx{},
			amount: 5000,
			err:    errWrongCoinbaseTransaction,
		},
	}

	block := new(types.Block)
	for i, c := range cases {
		block.Transactions = c.txs
		if err := checkCoinbaseAmount(types.MapBlock(block), c.amount); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestValidateBlockHeader(t *testing.T) {
	// add (hash, seed) --> (tensority hash) to the  tensority cache for avoid
	// real matrix calculate cost.
	tensority.AIHash.AddCache(&bc.Hash{V0: 0}, &bc.Hash{}, &bc.Hash{V0: 1<<64 - 1, V1: 1<<64 - 1, V2: 1<<64 - 1, V3: 1<<64 - 1})
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, &bc.Hash{}, &bc.Hash{})

	cases := []struct {
		block  *bc.Block
		parent *state.BlockNode
		err    error
	}{
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Version: 1,
			}},
			parent: &state.BlockNode{
				Version: 2,
			},
			err: errVersionRegression,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height: 20,
			}},
			parent: &state.BlockNode{
				Height: 18,
			},
			err: errMisorderedBlockHeight,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height: 20,
				Bits:   0,
			}},
			parent: &state.BlockNode{
				Height: 19,
				Bits:   2305843009214532812,
			},
			err: errBadBits,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height:          20,
				PreviousBlockId: &bc.Hash{V0: 18},
			}},
			parent: &state.BlockNode{
				Height: 19,
				Hash:   bc.Hash{V0: 19},
			},
			err: errMismatchedBlock,
		},
		{
			block: &bc.Block{
				ID: bc.Hash{V0: 0},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: errWorkProof,
		},
		{
			block: &bc.Block{
				ID: bc.Hash{V0: 1},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: nil,
		},
	}

	for i, c := range cases {
		if err := ValidateBlockHeader(c.block, c.parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}
<|endoftext|>


<commit_before>package tumblr

import (
	"fmt"
	"github.com/bitly/go-simplejson"
	"github.com/kr/pretty"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
)

type TumblrAPIResponse struct {
	Meta     Meta
	Response interface{}
}

type Meta struct {
	Status int64
	Msg    string
}

type APICredentials struct {
	Key string
	Secret string
}

type LimitOffset struct {
	Limit  int
	Offset int
}

type Tumblr struct {
	Credentials APICredentials
}

// API Functions

func callAPI(u *url.URL) (*simplejson.Json, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	json, err := simplejson.NewJson(data)
	if err != nil {
		return nil, err
	}

	// Handle Meta
	statCode, err := json.Get("meta").Get("status").Int64()
	if err != nil {
		return nil, err
	}
	statMsg, err := json.Get("meta").Get("msg").String()
	if err != nil {
		return nil, err
	}
	meta := Meta{
		Status: statCode,
		Msg: statMsg,
	}
	if meta.Status != 200 {
		err = fmt.Errorf("tumblr API responded with HTTP status %d: %s",
			meta.Status,
			meta.Msg)
		return nil, err
	}

	res := json.Get("response")

	fmt.Printf("response for %v:\n%v\n", u, pretty.Formatter(res))

	return res, nil
}

func (t Tumblr) NewBlog(baseHostname string) (Blog) {
	return Blog{
		BaseHostname: baseHostname,
		t: t,
	}
}

type Post struct{}

const (
	apiBaseURL = "http://api.tumblr.com/v2/"
)

// Post Types
const (
	Text   = "text"
	Quote  = "quote"
	Link   = "link"
	Answer = "answer"
	Video  = "video"
	Audio  = "audio"
	Photo  = "photo"
	Chat   = "chat"
)

func (t Tumblr) apiURL() (*url.URL, error) {
	url, err := url.Parse(apiBaseURL)
	if err != nil {
		return nil, err
	}
	addCredentials(url, t.Credentials)
	fmt.Printf("made api url: %v\n", url)
	return url, nil
}

// Request Parameter Types

func addCredentials(url *url.URL, credentials APICredentials) {
	fmt.Printf("credentials.Key: %v\n", credentials.Key)
	vals := url.Query()
	vals.Set("api_key", credentials.Key)
	url.RawQuery = vals.Encode()
}

func addLimitOffset(url *url.URL, params LimitOffset) {
	// Limit
	if params.Limit != 0 {
		url.Query().Set("limit", string(params.Limit))
	}

	// Offset
	if params.Offset != 0 {
		url.Query().Set("offset", string(params.Offset))
	}
}
<commit_msg>Wrong format flag<commit_after>package tumblr

import (
	"fmt"
	"github.com/bitly/go-simplejson"
	"github.com/kr/pretty"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
)

type TumblrAPIResponse struct {
	Meta     Meta
	Response interface{}
}

type Meta struct {
	Status int64
	Msg    string
}

type APICredentials struct {
	Key string
	Secret string
}

type LimitOffset struct {
	Limit  int
	Offset int
}

type Tumblr struct {
	Credentials APICredentials
}

// API Functions

func callAPI(u *url.URL) (*simplejson.Json, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	json, err := simplejson.NewJson(data)
	if err != nil {
		return nil, err
	}

	// Handle Meta
	statCode, err := json.Get("meta").Get("status").Int64()
	if err != nil {
		return nil, err
	}
	statMsg, err := json.Get("meta").Get("msg").String()
	if err != nil {
		return nil, err
	}
	meta := Meta{
		Status: statCode,
		Msg: statMsg,
	}
	if meta.Status != 200 {
		err = fmt.Errorf("tumblr API responded with HTTP status %d: %s",
			meta.Status,
			meta.Msg)
		return nil, err
	}

	res := json.Get("response")

	fmt.Printf("response for %v:\n%s\n", u, pretty.Formatter(res))

	return res, nil
}

func (t Tumblr) NewBlog(baseHostname string) (Blog) {
	return Blog{
		BaseHostname: baseHostname,
		t: t,
	}
}

type Post struct{}

const (
	apiBaseURL = "http://api.tumblr.com/v2/"
)

// Post Types
const (
	Text   = "text"
	Quote  = "quote"
	Link   = "link"
	Answer = "answer"
	Video  = "video"
	Audio  = "audio"
	Photo  = "photo"
	Chat   = "chat"
)

func (t Tumblr) apiURL() (*url.URL, error) {
	url, err := url.Parse(apiBaseURL)
	if err != nil {
		return nil, err
	}
	addCredentials(url, t.Credentials)
	fmt.Printf("made api url: %v\n", url)
	return url, nil
}

// Request Parameter Types

func addCredentials(url *url.URL, credentials APICredentials) {
	fmt.Printf("credentials.Key: %v\n", credentials.Key)
	vals := url.Query()
	vals.Set("api_key", credentials.Key)
	url.RawQuery = vals.Encode()
}

func addLimitOffset(url *url.URL, params LimitOffset) {
	// Limit
	if params.Limit != 0 {
		url.Query().Set("limit", string(params.Limit))
	}

	// Offset
	if params.Offset != 0 {
		url.Query().Set("offset", string(params.Offset))
	}
}
<|endoftext|>


<commit_before>package tumblr

import (
	"encoding/json"
	"fmt"
	"github.com/kr/pretty"
	"io/ioutil"
	"net/http"
	"net/url"
)

type TumblrAPIResponse struct {
	Meta     Meta
	Response *json.RawMessage
}

type Meta struct {
	Status int64
	Msg    string
}

type APICredentials struct {
	Key    string
	Secret string
}

type LimitOffset struct {
	Limit  int
	Offset int
}

type Tumblr struct {
	Credentials APICredentials
}

// API Functions

func callAPI(u *url.URL) (*json.RawMessage, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	res := &TumblrAPIResponse{}
	err = json.Unmarshal(data, res)
	if err != nil {
		return nil, err
	}

	if res.Meta.Status != 200 {
		err = fmt.Errorf("tumblr API responded with HTTP status %d: %s",
			res.Meta.Status,
			res.Meta.Msg)
		return nil, err
	}

	fmt.Printf("response for %v:\n%# v\n", u, pretty.Formatter(res))

	return res.Response, nil
}

func (t Tumblr) NewBlog(baseHostname string) Blog {
	return Blog{
		BaseHostname: baseHostname,
		t:            t,
	}
}

const (
	apiBaseURL = "http://api.tumblr.com/v2/"
)

func (t Tumblr) apiURL() (*url.URL, error) {
	url, err := url.Parse(apiBaseURL)
	if err != nil {
		return nil, err
	}
	addCredentials(url, t.Credentials)
	return url, nil
}

func addCredentials(url *url.URL, credentials APICredentials) {
	vals := url.Query()
	vals.Set("api_key", credentials.Key)
	url.RawQuery = vals.Encode()
}

func addLimitOffset(url *url.URL, params LimitOffset) {
	// Limit
	if params.Limit != 0 {
		url.Query().Set("limit", string(params.Limit))
	}

	// Offset
	if params.Offset != 0 {
		url.Query().Set("offset", string(params.Offset))
	}
}
<commit_msg>Add temporary debugging stuff so that I can see what JSON i'm deserializing<commit_after>package tumblr

import (
	"encoding/json"
	"fmt"
	"github.com/kr/pretty"
	"io/ioutil"
	"net/http"
	"net/url"
)

type TumblrAPIResponse struct {
	Meta     Meta
	Response *json.RawMessage
}

type Meta struct {
	Status int64
	Msg    string
}

type APICredentials struct {
	Key    string
	Secret string
}

type LimitOffset struct {
	Limit  int
	Offset int
}

type Tumblr struct {
	Credentials APICredentials
}

// API Functions

func callAPI(u *url.URL) (*json.RawMessage, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	res := &TumblrAPIResponse{}
	err = json.Unmarshal(data, res)
	if err != nil {
		return nil, err
	}

	if res.Meta.Status != 200 {
		err = fmt.Errorf("tumblr API responded with HTTP status %d: %s",
			res.Meta.Status,
			res.Meta.Msg)
		return nil, err
	}

	subJson, _ := res.Response.MarshalJSON()
	fmt.Printf("response for %v:\n%# v\n%s\n", u, pretty.Formatter(res), string(subJson))

	return res.Response, nil
}

func (t Tumblr) NewBlog(baseHostname string) Blog {
	return Blog{
		BaseHostname: baseHostname,
		t:            t,
	}
}

const (
	apiBaseURL = "http://api.tumblr.com/v2/"
)

func (t Tumblr) apiURL() (*url.URL, error) {
	url, err := url.Parse(apiBaseURL)
	if err != nil {
		return nil, err
	}
	addCredentials(url, t.Credentials)
	return url, nil
}

func addCredentials(url *url.URL, credentials APICredentials) {
	vals := url.Query()
	vals.Set("api_key", credentials.Key)
	url.RawQuery = vals.Encode()
}

func addLimitOffset(url *url.URL, params LimitOffset) {
	// Limit
	if params.Limit != 0 {
		url.Query().Set("limit", string(params.Limit))
	}

	// Offset
	if params.Offset != 0 {
		url.Query().Set("offset", string(params.Offset))
	}
}
<|endoftext|>


<commit_before>package spotify

import (
	"os"
	"testing"

	"github.com/markbates/goth"
	"github.com/stretchr/testify/assert"
)

func provider() *Provider {
	return New(os.Getenv("SPOTIFY_KEY"), os.Getenv("SPOTIFY_SECRET"), "/foo", "user")
}

func Test_New(t *testing.T) {
	t.Parallel()
	a := assert.New(t)
	p := provider()

	a.Equal(p.ClientKey, os.Getenv("SPOTIFY_KEY"))
	a.Equal(p.Secret, os.Getenv("SPOTIFY_SECRET"))
	a.Equal(p.CallbackURL, "/foo")
}

func Test_ImplementsProvider(t *testing.T) {
	t.Parallel()
	a := assert.New(t)
	a.Implements((*goth.Provider)(nil), provider())
}

func Test_BeginAuth(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	p := provider()
	session, err := p.BeginAuth("test_state")
	s := session.(*Session)
	a.NoError(err)
	a.Contains(s.AuthURL, "accounts.spotify.com/authorize")
}

func Test_SessionFromJSON(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	p := provider()
	session, err := p.UnmarshalSession(`{"AuthURL":"http://accounts.spotify.com/authorize","AccessToken":"1234567890"}`)
	a.NoError(err)

	s := session.(*Session)
	a.Equal(s.AuthURL, "http://accounts.spotify.com/authorize")
	a.Equal(s.AccessToken, "1234567890")
}
<commit_msg>updated package name from spotify to spotify_test to stay consistent with other provider packages. translated package change accordingly.<commit_after>package spotify_test

import (
	"os"
	"testing"

	"github.com/markbates/goth"
	"github.com/markbates/goth/providers/spotify"
	"github.com/stretchr/testify/assert"
)

func provider() *spotify.Provider {
	return spotify.New(os.Getenv("SPOTIFY_KEY"), os.Getenv("SPOTIFY_SECRET"), "/foo", "user")
}

func Test_New(t *testing.T) {
	t.Parallel()
	a := assert.New(t)
	p := provider()

	a.Equal(p.ClientKey, os.Getenv("SPOTIFY_KEY"))
	a.Equal(p.Secret, os.Getenv("SPOTIFY_SECRET"))
	a.Equal(p.CallbackURL, "/foo")
}

func Test_ImplementsProvider(t *testing.T) {
	t.Parallel()
	a := assert.New(t)
	a.Implements((*goth.Provider)(nil), provider())
}

func Test_BeginAuth(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	p := provider()
	session, err := p.BeginAuth("test_state")
	s := session.(*spotify.Session)
	a.NoError(err)
	a.Contains(s.AuthURL, "accounts.spotify.com/authorize")
}

func Test_SessionFromJSON(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	p := provider()
	session, err := p.UnmarshalSession(`{"AuthURL":"http://accounts.spotify.com/authorize","AccessToken":"1234567890"}`)
	a.NoError(err)

	s := session.(*spotify.Session)
	a.Equal(s.AuthURL, "http://accounts.spotify.com/authorize")
	a.Equal(s.AccessToken, "1234567890")
}
<|endoftext|>


<commit_before>package scrub

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"reflect"

	"code.gitea.io/git"
	"code.gitea.io/gitea/modules/log"
)

var JSON_FILES_TO_SCRUB = [...]string{
	"project.json",
	"package.json",
	"manifest.json",
	"status.json",
}

var JSON_FIELDS_TO_SCRUB = [...]string{
	"translators",
	"contributors",
	"checking_entity",
}

func ScrubJsonFiles(localPath string) error {
	for _, fileName := range JSON_FILES_TO_SCRUB {
		if err := ScrubJsonFile(localPath, fileName); err != nil {
			return err
		}
	}
	return nil
}

func ScrubJsonFile(localPath, fileName string) error {
	jsonPath := path.Join(localPath, fileName)

	var jsonData interface{}
	if _, err := os.Stat(jsonPath); os.IsNotExist(err) {
		return nil // path does not exist, nothing to scrub!
	} else if fileContent, err := ioutil.ReadFile(jsonPath); err != nil {
		log.Error(3, "%v", err)
		return err // error reading file
	} else {
		if err = json.Unmarshal(fileContent, &jsonData); err != nil {
			log.Error(3, "%v", err)
			return err // error unmarhalling file
		}
	}

	m := jsonData.(map[string]interface{})
	ScrubMap(m)

	if fileContent, err := json.MarshalIndent(m, "", "  "); err != nil {
		return err
	} else {
		if err := ScrubFile(localPath, fileName); err != nil {
			return err
		}
		if err := ioutil.WriteFile(jsonPath, []byte(fileContent), 0666); err != nil {
			return err
		}
	}

	return nil
}

func ScrubMap(m map[string]interface{}) {
	for _, field := range JSON_FIELDS_TO_SCRUB {
		if _, ok := m[field]; ok {
			m[field] = []string{}
		}
	}
	for _, v := range m {
		if reflect.ValueOf(v).Kind() == reflect.Map {
			vm := v.(map[string]interface{})
			ScrubMap(vm)
		}
	}
}

// ScrubFile completely removes a file from a repository's history
func ScrubFile(repoPath string, fileName string) error {
	gitPath, _ := exec.LookPath("git")
	cmd := git.NewCommand("filter-branch", "--force", "--prune-empty", "--tag-name-filter", "cat",
		"--index-filter", "\""+gitPath+"\" rm --cached --ignore-unmatch "+fileName,
		"--", "--all")
	_, err := cmd.RunInDir(repoPath)
	if err != nil && err.Error() == "exit status 1" {
		os.RemoveAll(path.Join(repoPath, ".git/refs/original/"))
		cmd = git.NewCommand("reflog", "expire", "--all")
		_, err = cmd.RunInDir(repoPath)
		if err != nil && err.Error() == "exit status 1" {
			cmd = git.NewCommand("gc", "--aggressive", "--prune")
			_, err = cmd.RunInDir(repoPath)
			return err
		}
	}
	return err
}

func ScrubCommitNameAndEmail(localPath, newName, newEmail string) error {
	os.RemoveAll(path.Join(localPath, ".git/refs/original/"))
	if _, err := git.NewCommand("filter-branch", "-f", "--env-filter", `
export GIT_COMMITTER_NAME="`+newName+`"
export GIT_COMMITTER_EMAIL="`+newEmail+`"
export GIT_AUTHOR_NAME="`+newName+`"
export GIT_AUTHOR_EMAIL="`+newEmail+`"
`, "--tag-name-filter", "cat", "--", "--branches", "--tags").RunInDir(localPath); err != nil {
		return err
	}
	if _, err := git.NewCommand("push", "--force", "--tags", "origin", "refs/heads/*").RunInDir(localPath); err != nil {
		return err
	}
	return nil
}
<commit_msg>Don't ignore error (#188)<commit_after>package scrub

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"reflect"

	"code.gitea.io/git"
	"code.gitea.io/gitea/modules/log"
)

var JSON_FILES_TO_SCRUB = [...]string{
	"project.json",
	"package.json",
	"manifest.json",
	"status.json",
}

var JSON_FIELDS_TO_SCRUB = [...]string{
	"translators",
	"contributors",
	"checking_entity",
}

func ScrubJsonFiles(localPath string) error {
	for _, fileName := range JSON_FILES_TO_SCRUB {
		if err := ScrubJsonFile(localPath, fileName); err != nil {
			return err
		}
	}
	return nil
}

func ScrubJsonFile(localPath, fileName string) error {
	jsonPath := path.Join(localPath, fileName)

	var jsonData interface{}
	if _, err := os.Stat(jsonPath); os.IsNotExist(err) {
		return nil // path does not exist, nothing to scrub!
	} else if fileContent, err := ioutil.ReadFile(jsonPath); err != nil {
		log.Error(3, "%v", err)
		return err // error reading file
	} else {
		if err = json.Unmarshal(fileContent, &jsonData); err != nil {
			log.Error(3, "%v", err)
			return err // error unmarhalling file
		}
	}

	m := jsonData.(map[string]interface{})
	ScrubMap(m)

	if fileContent, err := json.MarshalIndent(m, "", "  "); err != nil {
		return err
	} else {
		if err := ScrubFile(localPath, fileName); err != nil {
			return err
		}
		if err := ioutil.WriteFile(jsonPath, []byte(fileContent), 0666); err != nil {
			return err
		}
	}

	return nil
}

func ScrubMap(m map[string]interface{}) {
	for _, field := range JSON_FIELDS_TO_SCRUB {
		if _, ok := m[field]; ok {
			m[field] = []string{}
		}
	}
	for _, v := range m {
		if reflect.ValueOf(v).Kind() == reflect.Map {
			vm := v.(map[string]interface{})
			ScrubMap(vm)
		}
	}
}

// ScrubFile completely removes a file from a repository's history
func ScrubFile(repoPath string, fileName string) error {
	gitPath, err := exec.LookPath("git")
	if err != nil {
		return err
	}
	cmd := git.NewCommand("filter-branch", "--force", "--prune-empty", "--tag-name-filter", "cat",
		"--index-filter", "\""+gitPath+"\" rm --cached --ignore-unmatch "+fileName,
		"--", "--all")
	_, err = cmd.RunInDir(repoPath)
	if err != nil && err.Error() == "exit status 1" {
		os.RemoveAll(path.Join(repoPath, ".git/refs/original/"))
		cmd = git.NewCommand("reflog", "expire", "--all")
		_, err = cmd.RunInDir(repoPath)
		if err != nil && err.Error() == "exit status 1" {
			cmd = git.NewCommand("gc", "--aggressive", "--prune")
			_, err = cmd.RunInDir(repoPath)
			return err
		}
	}
	return err
}

func ScrubCommitNameAndEmail(localPath, newName, newEmail string) error {
	os.RemoveAll(path.Join(localPath, ".git/refs/original/"))
	if _, err := git.NewCommand("filter-branch", "-f", "--env-filter", `
export GIT_COMMITTER_NAME="`+newName+`"
export GIT_COMMITTER_EMAIL="`+newEmail+`"
export GIT_AUTHOR_NAME="`+newName+`"
export GIT_AUTHOR_EMAIL="`+newEmail+`"
`, "--tag-name-filter", "cat", "--", "--branches", "--tags").RunInDir(localPath); err != nil {
		return err
	}
	if _, err := git.NewCommand("push", "--force", "--tags", "origin", "refs/heads/*").RunInDir(localPath); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"path"
	"strconv"
	"sync"
	//	"github.com/davecgh/go-spew/spew"
)

//RawImage contains data we got from API that needs to be modified before further usage
type RawImage struct {
	Imgid          int    `json:"id_number"`
	URL            string `json:"image"`
	Score          int    `json:"score"`
	OriginalFormat string `json:"original_format"`
	Faves          int    `json:"faves"`
}

//Image contains data needed to filter fetch and save image
type Image struct {
	Imgid    int
	URL      string
	Filename string
	Score    int
	Faves    int
}

//Search returns to us array of searched images...
type Search struct {
	Images []RawImage `json:"search"`
}

//ImageCh is a channel of image data. You can put images into channel by parsing
//Derpibooru API by id(s) or  by tags and you can download images that are already
//in channel
type ImageCh chan Image

//Push gets unmarchalled JSON info, massages it and plugs it into channel so it
//would be processed in other places
func trim(dat RawImage) Image {

	tfn := strconv.Itoa(dat.Imgid) + "." + dat.OriginalFormat
	return Image{
		Imgid:    dat.Imgid,
		Filename: tfn,
		URL:      scheme + "/" + path.Dir(dat.URL) + "/" + tfn,
		Score:    dat.Score,
		Faves:    dat.Faves,
	}
}

//ParseImg gets image IDs, fetches information about those images from Derpibooru and pushes them into the channel.
func (imgchan ImageCh) ParseImg(ids []int, key string) {

	for _, imgid := range ids {

		if isParseInterrupted() {
			break
		}

		source := scheme + "//derpibooru.org/images/" + strconv.Itoa(imgid) + ".json"
		if key != "" {
			source = source + "?key=" + key
		}

		lInfo("Getting image info at:", source)

		body, err := getRemoteJSON(source)
		if err != nil {
			lErr(err)
			continue
		}
		var dat RawImage
		if err := json.Unmarshal(body, &dat); //transforming json into native map

		err != nil {
			lErr(err)
			continue
		}

		imgchan <- trim(dat)
	}

	close(imgchan) //closing channel, we are done here

	return
}

//DlImg reads image data from channel and downloads specified images to disc
func (imgchan ImageCh) downloadImages(opts *Config) {

	lInfo("Worker started; reading channel") //nice notification that we are not forgotten
	var n int
	var size int64
	var l sync.Mutex
	var wg sync.WaitGroup
	for k := 0; k < 4; k++ {
		wg.Add(1)
		go func() {
			for imgdata := range imgchan {

				lInfo("Saving as", imgdata.Filename)

				tsize, ok := imgdata.saveImage(opts)
				l.Lock()
				size += tsize
				if ok {
					n++
				}
				l.Unlock()
			}
			wg.Done()
		}()
	}
	wg.Wait()
	lInfof("Downloaded %d images, for a total of %s", n, fmtbytes(float64(size)))
}

//ParseTag gets image tags, fetches information about all images it could from Derpibooru and pushes them into the channel.
func (imgchan ImageCh) ParseTag(opts *TagOpts, key string) {

	//Unlike main, I don't see how I could separate bits out to decrease complexity
	source := scheme + "//derpibooru.org/search.json?sbq=" + opts.Tag //yay hardwiring url strings!

	if key != "" {
		source = source + "&key=" + key
	}

	lInfo("Searching as", source)

	for i := opts.StartPage; opts.StopPage == 0 || i <= opts.StopPage; i++ {

		if isParseInterrupted() {
			break
		}

		lInfo("Searching page", i)

		body, err := getRemoteJSON(source + "&page=" + strconv.Itoa(i))
		if err != nil {
			lErr("Error while getting json from page ", i)
			lErr(err)
			continue
		}

		var dats Search                   //Because we got array incoming instead of single object, we using a slive of maps!
		err = json.Unmarshal(body, &dats) //transforming json into native view

		if err != nil {
			lErr("Error while parsing search page", i)
			lErr(err)
			if serr, ok := err.(*json.SyntaxError); ok { //In case crap was still given, we are looking at it.
				lErr("Occurred at offset: ", serr.Offset)
			}
			continue

		}

		if len(dats.Images) == 0 {
			lInfo("Pages are all over") //Does not mean that process is over.
			break
		} //exit due to finishing all pages

		for _, dat := range dats.Images {
			imgchan <- trim(dat)
		}

	}

	close(imgchan)
}

func isParseInterrupted() bool {
	select {
	case <-interruptParse:
		return true
	default:
		return false
	}
}
<commit_msg>Derpibooru changed API, I missed that. Fixed<commit_after>package main

import (
	"encoding/json"
	"path"
	"strconv"
	"sync"
	//	"github.com/davecgh/go-spew/spew"
)

//RawImage contains data we got from API that needs to be modified before further usage
type RawImage struct {
	Imgid          string `json:"id"`
	URL            string `json:"image"`
	Score          int    `json:"score"`
	OriginalFormat string `json:"original_format"`
	Faves          int    `json:"faves"`
}

//Image contains data needed to filter fetch and save image
type Image struct {
	Imgid    int
	URL      string
	Filename string
	Score    int
	Faves    int
}

//Search returns to us array of searched images...
type Search struct {
	Images []RawImage `json:"search"`
}

//ImageCh is a channel of image data. You can put images into channel by parsing
//Derpibooru API by id(s) or  by tags and you can download images that are already
//in channel
type ImageCh chan Image

//Push gets unmarchalled JSON info, massages it and plugs it into channel so it
//would be processed in other places
func trim(dat RawImage) Image {

	tfn := dat.Imgid + "." + dat.OriginalFormat
	id, ok := strconv.Atoi(dat.Imgid)
	if ok != nil {
		lErr(id, ok)
	}
	return Image{

		Imgid:    id,
		Filename: tfn,
		URL:      scheme + "/" + path.Dir(dat.URL) + "/" + tfn,
		Score:    dat.Score,
		Faves:    dat.Faves,
	}
}

//ParseImg gets image IDs, fetches information about those images from Derpibooru and pushes them into the channel.
func (imgchan ImageCh) ParseImg(ids []int, key string) {

	for _, imgid := range ids {

		if isParseInterrupted() {
			break
		}

		source := scheme + "//derpibooru.org/images/" + strconv.Itoa(imgid) + ".json"
		if key != "" {
			source = source + "?key=" + key
		}

		lInfo("Getting image info at:", source)

		body, err := getRemoteJSON(source)
		if err != nil {
			lErr(err)
			continue
		}
		var dat RawImage
		if err := json.Unmarshal(body, &dat); //transforming json into native map

		err != nil {
			lErr(err)
			continue
		}

		imgchan <- trim(dat)
	}

	close(imgchan) //closing channel, we are done here

	return
}

//DlImg reads image data from channel and downloads specified images to disc
func (imgchan ImageCh) downloadImages(opts *Config) {

	lInfo("Worker started; reading channel") //nice notification that we are not forgotten
	var n int
	var size int64
	var l sync.Mutex
	var wg sync.WaitGroup
	for k := 0; k < 4; k++ {
		wg.Add(1)
		go func() {
			for imgdata := range imgchan {

				lInfo("Saving as", imgdata.Filename)

				tsize, ok := imgdata.saveImage(opts)
				l.Lock()
				size += tsize
				if ok {
					n++
				}
				l.Unlock()
			}
			wg.Done()
		}()
	}
	wg.Wait()
	lInfof("Downloaded %d images, for a total of %s", n, fmtbytes(float64(size)))
}

//ParseTag gets image tags, fetches information about all images it could from Derpibooru and pushes them into the channel.
func (imgchan ImageCh) ParseTag(opts *TagOpts, key string) {

	//Unlike main, I don't see how I could separate bits out to decrease complexity
	source := scheme + "//derpibooru.org/search.json?sbq=" + opts.Tag //yay hardwiring url strings!

	if key != "" {
		source = source + "&key=" + key
	}

	lInfo("Searching as", source)

	for i := opts.StartPage; opts.StopPage == 0 || i <= opts.StopPage; i++ {

		if isParseInterrupted() {
			break
		}

		lInfo("Searching page", i)

		body, err := getRemoteJSON(source + "&page=" + strconv.Itoa(i))
		if err != nil {
			lErr("Error while getting json from page ", i)
			lErr(err)
			continue
		}

		var dats Search                   //Because we got array incoming instead of single object, we using a slive of maps!
		err = json.Unmarshal(body, &dats) //transforming json into native view

		if err != nil {
			lErr("Error while parsing search page", i)
			lErr(err)
			if serr, ok := err.(*json.SyntaxError); ok { //In case crap was still given, we are looking at it.
				lErr("Occurred at offset: ", serr.Offset)
			}
			continue

		}

		if len(dats.Images) == 0 {
			lInfo("Pages are all over") //Does not mean that process is over.
			break
		} //exit due to finishing all pages

		for _, dat := range dats.Images {
			imgchan <- trim(dat)
		}

	}

	close(imgchan)
}

func isParseInterrupted() bool {
	select {
	case <-interruptParse:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package controllers

import "github.com/revel/revel"
import "net/http"
import "io/ioutil"

type Feed struct {
  *revel.Controller
}

func (c Feed) Show(feedId string) revel.Result {
  var requestUrl string = "https://api.vk.com/method/wall.get?owner_id=" + feedId

  resp, err := http.Get(requestUrl)

  if err != nil {
    c.Response.Status = 500
  }

  defer resp.Body.Close()

  body, err := ioutil.ReadAll(resp.Body)

  if err != nil {
    c.Response.Status = 500
  }

  result := string(body)

  return c.Render(result)
}<commit_msg>json marshalling<commit_after>package controllers

import (
  "github.com/revel/revel"
  "net/http"
  "io/ioutil"
  "encoding/json"
  "fmt"
)

type Feed struct {
  *revel.Controller
}

type VKArray struct {
  Id int
  From_id int
  Owner_id int
  Date int
  Post_type string
  Text string
}

type VKResponseBody struct {
  Count int
  Items []VKArray
}

type VKResponse struct {
  Response VKResponseBody
}

func (c Feed) Show(feedId string) revel.Result {
  var requestUrl string = "https://api.vk.com/method/wall.get?count=2&v=5.12&owner_id=" + feedId

  resp, err := http.Get(requestUrl)

  if err != nil {
    c.Response.Status = 500
  }

  defer resp.Body.Close()

  body, err := ioutil.ReadAll(resp.Body)

  if err != nil {
    c.Response.Status = 500
  }

  var encoded VKResponse

  err = json.Unmarshal(body, &encoded)

  if err != nil {
    c.Response.Status = 500
    fmt.Println(err)
  }

  return c.Render(encoded)
}<|endoftext|>


<commit_before>package abortbuild

import (
	"net/http"
	"strconv"
	"time"

	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/atc/builds"
	"github.com/concourse/atc/config"
	"github.com/concourse/atc/db"
	"github.com/concourse/atc/web/routes"
	troutes "github.com/concourse/turbine/routes"
)

type handler struct {
	logger lager.Logger

	jobs       config.Jobs
	db         db.DB
	httpClient *http.Client
}

func NewHandler(logger lager.Logger, jobs config.Jobs, db db.DB) http.Handler {
	return &handler{
		logger: logger,

		jobs: jobs,
		db:   db,

		httpClient: &http.Client{
			Transport: &http.Transport{
				ResponseHeaderTimeout: 5 * time.Minute,
			},
		},
	}
}

func (handler *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	buildID, err := strconv.Atoi(r.FormValue(":build_id"))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	log := handler.logger.Session("abort", lager.Data{
		"build": buildID,
	})

	build, err := handler.db.GetBuild(buildID)
	if err != nil {
		log.Error("failed-to-get-build", err)
		w.WriteHeader(http.StatusNotFound)
		return
	}

	err = handler.db.SaveBuildStatus(buildID, builds.StatusAborted)
	if err != nil {
		log.Error("failed-to-set-aborted", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	generator := rata.NewRequestGenerator(build.Endpoint, troutes.Routes)

	abort, err := generator.CreateRequest(
		troutes.AbortBuild,
		rata.Params{"guid": build.Guid},
		nil,
	)
	if err != nil {
		log.Error("failed-to-construct-abort-request", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp, err := handler.httpClient.Do(abort)
	if err != nil {
		log.Error("failed-to-abort-build", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp.Body.Close()

	redirectPath, err := routes.Routes.CreatePathForRoute(routes.GetBuild, rata.Params{
		"job":   build.JobName,
		"build": build.Name,
	})
	if err != nil {
		log.Fatal("failed-to-create-redirect-uri", err)
	}

	http.Redirect(w, r, redirectPath, 302)
}
<commit_msg>use new turbine packages<commit_after>package abortbuild

import (
	"net/http"
	"strconv"
	"time"

	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/atc/builds"
	"github.com/concourse/atc/config"
	"github.com/concourse/atc/db"
	"github.com/concourse/atc/web/routes"
	"github.com/concourse/turbine"
)

type handler struct {
	logger lager.Logger

	jobs       config.Jobs
	db         db.DB
	httpClient *http.Client
}

func NewHandler(logger lager.Logger, jobs config.Jobs, db db.DB) http.Handler {
	return &handler{
		logger: logger,

		jobs: jobs,
		db:   db,

		httpClient: &http.Client{
			Transport: &http.Transport{
				ResponseHeaderTimeout: 5 * time.Minute,
			},
		},
	}
}

func (handler *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	buildID, err := strconv.Atoi(r.FormValue(":build_id"))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	log := handler.logger.Session("abort", lager.Data{
		"build": buildID,
	})

	build, err := handler.db.GetBuild(buildID)
	if err != nil {
		log.Error("failed-to-get-build", err)
		w.WriteHeader(http.StatusNotFound)
		return
	}

	err = handler.db.SaveBuildStatus(buildID, builds.StatusAborted)
	if err != nil {
		log.Error("failed-to-set-aborted", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	generator := rata.NewRequestGenerator(build.Endpoint, turbine.Routes)

	abort, err := generator.CreateRequest(
		turbine.AbortBuild,
		rata.Params{"guid": build.Guid},
		nil,
	)
	if err != nil {
		log.Error("failed-to-construct-abort-request", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp, err := handler.httpClient.Do(abort)
	if err != nil {
		log.Error("failed-to-abort-build", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	resp.Body.Close()

	redirectPath, err := routes.Routes.CreatePathForRoute(routes.GetBuild, rata.Params{
		"job":   build.JobName,
		"build": build.Name,
	})
	if err != nil {
		log.Fatal("failed-to-create-redirect-uri", err)
	}

	http.Redirect(w, r, redirectPath, 302)
}
<|endoftext|>


<commit_before>package connection

import (
	"fmt"
	"io"
	"sync"

	"github.com/cloudfoundry-incubator/garden/routes"
	"github.com/tedsuo/rata"
)

type ioStream struct {
	conn            *connection
	containerHandle string
	processID       uint32
	streamID        uint32

	wg *sync.WaitGroup
}

func newIOStream(conn *connection, handle string, processID, streamID uint32) *ioStream {
	return &ioStream{
		conn:            conn,
		containerHandle: handle,
		processID:       processID,
		streamID:        streamID,
	}
}

func (a *ioStream) doAttach(streamWriter io.Writer, stdtype string) error {
	if streamWriter == nil {
		return nil
	}

	if err := a.copyStream(streamWriter, stdtype); err != nil {
		return err
	}

	return nil
}

// attaches to the stdout and stderr endpoints for a running process
// and copies output to a local io.writers
func (a *ioStream) attach(stdoutW, stderrW io.Writer) error {
	a.wg = new(sync.WaitGroup)

	if err := a.doAttach(stdoutW, routes.Stdout); err != nil {
		return err
	}

	if err := a.doAttach(stderrW, routes.Stderr); err != nil {
		return err
	}

	return nil
}

func (a *ioStream) copyStream(target io.Writer, route string) error {
	params := rata.Params{
		"handle":   a.containerHandle,
		"pid":      fmt.Sprintf("%d", a.processID),
		"streamid": fmt.Sprintf("%d", a.streamID),
	}
	_, source, err := a.conn.doHijack(
		route,
		nil,
		params,
		nil,
		"application/json",
	)
	if err != nil {
		return fmt.Errorf("Failed to hijack stream %s: %s", route, err)
	}

	a.wg.Add(1)
	go func() {
		io.Copy(target, source)
		a.wg.Done()
	}()

	return nil
}

func (a *ioStream) wait() {
	a.wg.Wait()
}
<commit_msg>Factor connect out of copyStream.<commit_after>package connection

import (
	"fmt"
	"io"
	"sync"

	"github.com/cloudfoundry-incubator/garden/routes"
	"github.com/tedsuo/rata"
)

type ioStream struct {
	conn            *connection
	containerHandle string
	processID       uint32
	streamID        uint32

	wg *sync.WaitGroup
}

func newIOStream(conn *connection, handle string, processID, streamID uint32) *ioStream {
	return &ioStream{
		conn:            conn,
		containerHandle: handle,
		processID:       processID,
		streamID:        streamID,
	}
}

func (a *ioStream) doAttach(streamWriter io.Writer, stdtype string) error {
	if streamWriter == nil {
		return nil
	}

	source, err := a.connect(stdtype)
	if err != nil {
		return err
	}

	if err := a.copyStream(streamWriter, source); err != nil {
		return err
	}

	return nil
}

// attaches to the stdout and stderr endpoints for a running process
// and copies output to a local io.writers
func (a *ioStream) attach(stdoutW, stderrW io.Writer) error {
	a.wg = new(sync.WaitGroup)

	if err := a.doAttach(stdoutW, routes.Stdout); err != nil {
		return err
	}

	if err := a.doAttach(stderrW, routes.Stderr); err != nil {
		return err
	}

	return nil
}

func (a *ioStream) connect(route string) (io.Reader, error) {
	params := rata.Params{
		"handle":   a.containerHandle,
		"pid":      fmt.Sprintf("%d", a.processID),
		"streamid": fmt.Sprintf("%d", a.streamID),
	}
	_, source, err := a.conn.doHijack(
		route,
		nil,
		params,
		nil,
		"application/json",
	)

	if err != nil {
		return nil, fmt.Errorf("Failed to hijack stream %s: %s", route, err)
	}

	return source, nil
}

func (a *ioStream) copyStream(target io.Writer, source io.Reader) error {
	a.wg.Add(1)
	go func() {
		io.Copy(target, source)
		a.wg.Done()
	}()

	return nil
}

func (a *ioStream) wait() {
	a.wg.Wait()
}
<|endoftext|>