<commit_before>// Copyright 2016 Mender Software AS
//
//    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 view

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/mendersoftware/go-lib-micro/log"
	"github.com/mendersoftware/go-lib-micro/requestid"
)

// Headers
const (
	HttpHeaderLocation = "Location"
)

// Errors
var (
	ErrNotFound = errors.New("Resource not found")
)

type RESTView struct {
}

func (p *RESTView) RenderSuccessPost(w rest.ResponseWriter, r *rest.Request, id string) {
	w.Header().Add(HttpHeaderLocation, fmt.Sprintf("%s/%s", r.URL.String(), id))
	w.WriteHeader(http.StatusCreated)
}

func (p *RESTView) RenderSuccessGet(w rest.ResponseWriter, object interface{}) {
	w.WriteJson(object)
}

func (p *RESTView) RenderError(w rest.ResponseWriter, r *rest.Request, err error, status int, l *log.Logger) {
	l.Error(err.Error())
	renderErrorWithMsg(w, r, status, err.Error())
}

func (p *RESTView) RenderInternalError(w rest.ResponseWriter, r *rest.Request, err error, l *log.Logger) {
	l.F(log.Ctx{}).Error(err.Error())
	renderErrorWithMsg(w, r, http.StatusInternalServerError, "internal error")
}

func renderErrorWithMsg(w rest.ResponseWriter, r *rest.Request, status int, msg string) {
	w.WriteHeader(status)
	writeErr := w.WriteJson(map[string]string{
		"error":      msg,
		"request_id": requestid.GetReqId(r),
	})
	if writeErr != nil {
		panic(writeErr)
	}
}

func (p *RESTView) RenderErrorNotFound(w rest.ResponseWriter, r *rest.Request, l *log.Logger) {
	p.RenderError(w, r, ErrNotFound, http.StatusNotFound, l)
}

func (p *RESTView) RenderSuccessDelete(w rest.ResponseWriter) {
	w.WriteHeader(http.StatusNoContent)
}

func (p *RESTView) RenderSuccessPut(w rest.ResponseWriter) {
	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>view: relative urls in Location headers<commit_after>// Copyright 2016 Mender Software AS
//
//    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 view

import (
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/mendersoftware/go-lib-micro/log"
	"github.com/mendersoftware/go-lib-micro/requestid"
)

// Headers
const (
	HttpHeaderLocation = "Location"
)

// Errors
var (
	ErrNotFound = errors.New("Resource not found")
)

type RESTView struct {
}

func (p *RESTView) RenderSuccessPost(w rest.ResponseWriter, r *rest.Request, id string) {
	w.Header().Add(HttpHeaderLocation, fmt.Sprintf("./%s/%s", strings.TrimLeft(r.URL.Path, "/api/0.0.1/"), id))
	w.WriteHeader(http.StatusCreated)
}

func (p *RESTView) RenderSuccessGet(w rest.ResponseWriter, object interface{}) {
	w.WriteJson(object)
}

func (p *RESTView) RenderError(w rest.ResponseWriter, r *rest.Request, err error, status int, l *log.Logger) {
	l.Error(err.Error())
	renderErrorWithMsg(w, r, status, err.Error())
}

func (p *RESTView) RenderInternalError(w rest.ResponseWriter, r *rest.Request, err error, l *log.Logger) {
	l.F(log.Ctx{}).Error(err.Error())
	renderErrorWithMsg(w, r, http.StatusInternalServerError, "internal error")
}

func renderErrorWithMsg(w rest.ResponseWriter, r *rest.Request, status int, msg string) {
	w.WriteHeader(status)
	writeErr := w.WriteJson(map[string]string{
		"error":      msg,
		"request_id": requestid.GetReqId(r),
	})
	if writeErr != nil {
		panic(writeErr)
	}
}

func (p *RESTView) RenderErrorNotFound(w rest.ResponseWriter, r *rest.Request, l *log.Logger) {
	p.RenderError(w, r, ErrNotFound, http.StatusNotFound, l)
}

func (p *RESTView) RenderSuccessDelete(w rest.ResponseWriter) {
	w.WriteHeader(http.StatusNoContent)
}

func (p *RESTView) RenderSuccessPut(w rest.ResponseWriter) {
	w.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>// Copyright 2017 Giulio Iotti. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

type offset int

type resource struct {
	str string
	cg  group
	n   offset
}

func newResource(tmpl string, cg group, n offset) *resource {
	return &resource{
		cg:  cg,
		n:   n,
		str: fmt.Sprintf(tmpl, url.QueryEscape(string(cg)), n),
	}
}

func (r *resource) String() string {
	return r.str
}

func (r *resource) cache(c *cache, body []byte, err error) {
	c.put(r.cg, newPage(r.n, body), err)
}

type job struct {
	res   *resource
	cache *cache
}

func newJob(r *resource, c *cache) *job {
	return &job{res: r, cache: c}
}

func (j *job) get() ([]byte, error) {
	tr := &http.Transport{
		MaxIdleConns:    10,               // TODO: not hardcoded
		IdleConnTimeout: 30 * time.Second, // TODO: not hardcoded
	}
	client := &http.Client{Transport: tr}
	resp, err := client.Get(j.res.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	buf := &bytes.Buffer{}
	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (j *job) run() {
	debug("fetch request for %s", j.res)
	body, err := j.get()
	if err != nil {
		err = fmt.Errorf("cannot fetch URL %s: %s", j.res, err)
	}
	j.res.cache(j.cache, body, err)
}

type fetcher struct {
	jobs chan *job
}

func newFetcher(workers, queue int) *fetcher {
	f := &fetcher{
		jobs: make(chan *job, queue),
	}
	for i := 0; i < workers; i++ {
		go f.run()
	}
	return f
}

func (f *fetcher) run() {
	for job := range f.jobs {
		job.run()
	}
}

func (f *fetcher) request(j *job) {
	f.jobs <- j
}
<commit_msg>decorate some errors in HTTP fetch<commit_after>// Copyright 2017 Giulio Iotti. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

type offset int

type resource struct {
	str string
	cg  group
	n   offset
}

func newResource(tmpl string, cg group, n offset) *resource {
	return &resource{
		cg:  cg,
		n:   n,
		str: fmt.Sprintf(tmpl, url.QueryEscape(string(cg)), n),
	}
}

func (r *resource) String() string {
	return r.str
}

func (r *resource) cache(c *cache, body []byte, err error) {
	c.put(r.cg, newPage(r.n, body), err)
}

type job struct {
	res   *resource
	cache *cache
}

func newJob(r *resource, c *cache) *job {
	return &job{res: r, cache: c}
}

func (j *job) get() ([]byte, error) {
	tr := &http.Transport{
		MaxIdleConns:    10,               // TODO: not hardcoded
		IdleConnTimeout: 30 * time.Second, // TODO: not hardcoded
	}
	client := &http.Client{Transport: tr}
	resp, err := client.Get(j.res.String())
	if err != nil {
		return nil, fmt.Errorf("cannot GET %s: %s", j.res, err)
	}
	defer resp.Body.Close()
	buf := &bytes.Buffer{}
	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, fmt.Errorf("cannot copy data from %s: %s", j.res, err)
	}
	return buf.Bytes(), nil
}

func (j *job) run() {
	debug("fetch request for %s", j.res)
	body, err := j.get()
	if err != nil {
		err = fmt.Errorf("cannot fetch URL %s: %s", j.res, err)
	}
	j.res.cache(j.cache, body, err)
}

type fetcher struct {
	jobs chan *job
}

func newFetcher(workers, queue int) *fetcher {
	f := &fetcher{
		jobs: make(chan *job, queue),
	}
	for i := 0; i < workers; i++ {
		go f.run()
	}
	return f
}

func (f *fetcher) run() {
	for job := range f.jobs {
		job.run()
	}
}

func (f *fetcher) request(j *job) {
	f.jobs <- j
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	r "github.com/scascketta/capmetro-data/Godeps/_workspace/src/github.com/dancannon/gorethink"
	"time"
)

var (
	lastUpdated map[string]time.Time = map[string]time.Time{}

	firstNewVehicleCheck bool          = true
	nextNewVehicleCheck  time.Time     = time.Now()
	vehicleCheckInterval time.Duration = (4 * 60 * 60) * (1000 * time.Millisecond)

	normalDuration   time.Duration = (30) * (1000 * time.Millisecond)
	extendedDuration time.Duration = (10 * 60) * (1000 * time.Millisecond)

	emptyResponses      map[string]int  = map[string]int{}
	recentEmptyResponse map[string]bool = map[string]bool{}
)

func FilterUpdatedVehicles(vehicles []VehiclePosition) []VehiclePosition {
	updated := []VehiclePosition{}
	for _, v := range vehicles {
		updateTime, _ := lastUpdated[v.VehicleID]
		lastUpdated[v.VehicleID] = v.Time
		if !updateTime.Equal(v.Time) {
			updated = append(updated, v)
		}
	}
	return updated
}

func LogVehiclePositions(session *r.Session, route string) error {
	vehicles, err := FetchVehicles(route)
	if err != nil {
		return err
	}
	if vehicles == nil {
		// increment retry count if fetch just before was also empty
		// only subsequent empty responses matter when determining how long to sleep
		if recentEmptyResponse[route] {
			emptyResponses[route] += 1
		}
		recentEmptyResponse[route] = true
		return fmt.Errorf("No vehicles in response for route: %s.", route)
	} else {
		recentEmptyResponse[route] = false
	}

	updated := FilterUpdatedVehicles(vehicles)

	if len(updated) > 0 {
		_, err = r.Table("vehicle_position").Insert(r.Expr(updated)).Run(session)
		if err != nil {
			return err
		}
		dbglogger.Printf("Log %d vehicles, route %s.\n", len(updated), route)
	} else {
		dbglogger.Printf("No new vehicle positions to record for route %s.\n", route)
	}
	return nil
}

// Check if the the routes are inactive
// There must have been MAX_RETRIES previous attempts to fetch data,
// and all attempts must have failed
func routesAreSleeping() bool {
	dbglogger.Println("emptyResponses:", emptyResponses)
	dbglogger.Println("recentEmptyResponse:", recentEmptyResponse)
	for _, retries := range emptyResponses {
		if retries < MAX_RETRIES {
			return false
		}
	}
	return true
}

func checkNewVehicles(session *r.Session) error {
	new_vehicles := 0
	dbglogger.Println("Check for new vehicles.")
	vehicles := []map[string]string{}
	cur, err := r.Table("vehicle_position").Pluck("vehicle_id", "route", "route_id", "trip_id").Distinct().Run(session)
	if err != nil {
		return err
	}
	cur.All(&vehicles)

	for _, data := range vehicles {
		id := data["vehicle_id"]
		stream := r.Table("vehicles").Pluck("vehicle_id")
		query_expr := r.Expr(map[string]string{"vehicle_id": data["vehicle_id"]})
		cur, err = stream.Contains(query_expr).Run(session)
		if err != nil {
			return err
		}
		var res bool

		cur.Next(&res)
		if !res {
			new_vehicles += 1
			dbglogger.Printf("Adding new vehicle %s to vehicles table.\n", id)
			vehicle := Vehicle{
				VehicleID:    data["vehicle_id"],
				Route:        data["route"],
				RouteID:      data["route_id"],
				TripID:       data["trip_id"],
				LastAnalyzed: time.Now(),
			}
			_, err := r.Table("vehicles").Insert(r.Expr(vehicle)).Run(session)
			if err != nil {
				return err
			}
		}
	}

	dbglogger.Printf("Inserted %d new vehicles.\n", new_vehicles)
	return nil
}
<commit_msg>print vehicle update times<commit_after>package main

import (
	"fmt"
	r "github.com/scascketta/capmetro-data/Godeps/_workspace/src/github.com/dancannon/gorethink"
	"time"
)

var (
	lastUpdated map[string]time.Time = map[string]time.Time{}

	firstNewVehicleCheck bool          = true
	nextNewVehicleCheck  time.Time     = time.Now()
	vehicleCheckInterval time.Duration = (4 * 60 * 60) * (1000 * time.Millisecond)

	normalDuration   time.Duration = (30) * (1000 * time.Millisecond)
	extendedDuration time.Duration = (10 * 60) * (1000 * time.Millisecond)

	emptyResponses      map[string]int  = map[string]int{}
	recentEmptyResponse map[string]bool = map[string]bool{}
)

func FilterUpdatedVehicles(vehicles []VehiclePosition) []VehiclePosition {
	updated := []VehiclePosition{}
	for _, v := range vehicles {
		updateTime, _ := lastUpdated[v.VehicleID]
		lastUpdated[v.VehicleID] = v.Time
		if !updateTime.Equal(v.Time) {
			updated = append(updated, v)
		}
	}
	return updated
}

func LogVehiclePositions(session *r.Session, route string) error {
	vehicles, err := FetchVehicles(route)
	if err != nil {
		return err
	}
	if vehicles == nil {
		// increment retry count if fetch just before was also empty
		// only subsequent empty responses matter when determining how long to sleep
		if recentEmptyResponse[route] {
			emptyResponses[route] += 1
		}
		recentEmptyResponse[route] = true
		return fmt.Errorf("No vehicles in response for route: %s.", route)
	} else {
		recentEmptyResponse[route] = false
	}

	updated := FilterUpdatedVehicles(vehicles)

	for _, v := range updated {
		dbglogger.Printf("Vehicle %s updated at %s\n", v.VehicleID, v.Time.Format("2006-01-02T15:04:05-07:00"))
	}

	if len(updated) > 0 {
		_, err = r.Table("vehicle_position").Insert(r.Expr(updated)).Run(session)
		if err != nil {
			return err
		}
		dbglogger.Printf("Log %d vehicles, route %s.\n", len(updated), route)
	} else {
		dbglogger.Printf("No new vehicle positions to record for route %s.\n", route)
	}
	return nil
}

// Check if the the routes are inactive
// There must have been MAX_RETRIES previous attempts to fetch data,
// and all attempts must have failed
func routesAreSleeping() bool {
	dbglogger.Println("emptyResponses:", emptyResponses)
	dbglogger.Println("recentEmptyResponse:", recentEmptyResponse)
	for _, retries := range emptyResponses {
		if retries < MAX_RETRIES {
			return false
		}
	}
	return true
}

func checkNewVehicles(session *r.Session) error {
	new_vehicles := 0
	dbglogger.Println("Check for new vehicles.")
	vehicles := []map[string]string{}
	cur, err := r.Table("vehicle_position").Pluck("vehicle_id", "route", "route_id", "trip_id").Distinct().Run(session)
	if err != nil {
		return err
	}
	cur.All(&vehicles)

	for _, data := range vehicles {
		id := data["vehicle_id"]
		stream := r.Table("vehicles").Pluck("vehicle_id")
		query_expr := r.Expr(map[string]string{"vehicle_id": data["vehicle_id"]})
		cur, err = stream.Contains(query_expr).Run(session)
		if err != nil {
			return err
		}
		var res bool

		cur.Next(&res)
		if !res {
			new_vehicles += 1
			dbglogger.Printf("Adding new vehicle %s to vehicles table.\n", id)
			vehicle := Vehicle{
				VehicleID:    data["vehicle_id"],
				Route:        data["route"],
				RouteID:      data["route_id"],
				TripID:       data["trip_id"],
				LastAnalyzed: time.Now(),
			}
			_, err := r.Table("vehicles").Insert(r.Expr(vehicle)).Run(session)
			if err != nil {
				return err
			}
		}
	}

	dbglogger.Printf("Inserted %d new vehicles.\n", new_vehicles)
	return nil
}
<|endoftext|>


<commit_before>package bitcoind

// Represents a block
type block struct {
	Hash              string   `json:"hash"`
	Confirmations     uint64   `json:"confirmations"`
	Size              uint64   `json:"size"`
	Height            uint64   `json:"height"`
	Version           uint64   `json:"version"`
	Merkleroot        string   `json:"merkleroot"`
	Tx                []string `json:"tx"`
	Time              int64    `json:"time"`
	Nonce             uint64   `json:"nonce"`
	Bits              string   `json:"bits"`
	Difficulty        float64  `json:"difficulty"`
	Chainwork         string   `json:"chainwork"`
	Previousblockhash string   `json:"previousblockhash"`
}
<commit_msg>add nextblockhash<commit_after>package bitcoind

// Represents a block
type block struct {
	Hash              string   `json:"hash"`
	Confirmations     uint64   `json:"confirmations"`
	Size              uint64   `json:"size"`
	Height            uint64   `json:"height"`
	Version           uint64   `json:"version"`
	Merkleroot        string   `json:"merkleroot"`
	Tx                []string `json:"tx"`
	Time              int64    `json:"time"`
	Nonce             uint64   `json:"nonce"`
	Bits              string   `json:"bits"`
	Difficulty        float64  `json:"difficulty"`
	Chainwork         string   `json:"chainwork"`
	Previousblockhash string   `json:"previousblockhash"`
	Nextblockhash     string   `json:"nextblockhash"`
}
<|endoftext|>


<commit_before>package gin

import (
	"errors"
	"html/template"
	"net/http"
	"net/http/httptest"
	"testing"
)

// TestContextParamsGet tests that a parameter can be parsed from the URL.
func TestContextParamsByName(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test/alexandernyquist", nil)
	w := httptest.NewRecorder()
	name := ""

	r := Default()
	r.GET("/test/:name", func(c *Context) {
		name = c.Params.ByName("name")
	})

	r.ServeHTTP(w, req)

	if name != "alexandernyquist" {
		t.Errorf("Url parameter was not correctly parsed. Should be alexandernyquist, was %s.", name)
	}
}

// TestContextSetGet tests that a parameter is set correctly on the
// current context and can be retrieved using Get.
func TestContextSetGet(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		// Key should be lazily created
		if c.Keys != nil {
			t.Error("Keys should be nil")
		}

		// Set
		c.Set("foo", "bar")

		v, err := c.Get("foo")
		if err != nil {
			t.Errorf("Error on exist key")
		}
		if v != "bar" {
			t.Errorf("Value should be bar, was %s", v)
		}
	})

	r.ServeHTTP(w, req)
}

// TestContextJSON tests that the response is serialized as JSON
// and Content-Type is set to application/json
func TestContextJSON(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		c.JSON(200, H{"foo": "bar"})
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "{\"foo\":\"bar\"}\n" {
		t.Errorf("Response should be {\"foo\":\"bar\"}, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "application/json" {
		t.Errorf("Content-Type should be application/json, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextHTML tests that the response executes the templates
// and responds with Content-Type set to text/html
func TestContextHTML(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	templ, _ := template.New("t").Parse(`Hello {{.Name}}`)
	r.SetHTMLTemplate(templ)

	type TestData struct{ Name string }

	r.GET("/test", func(c *Context) {
		c.HTML(200, "t", TestData{"alexandernyquist"})
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "Hello alexandernyquist" {
		t.Errorf("Response should be Hello alexandernyquist, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "text/html" {
		t.Errorf("Content-Type should be text/html, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextString tests that the response is returned
// with Content-Type set to text/plain
func TestContextString(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		c.String(200, "test")
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "test" {
		t.Errorf("Response should be test, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "text/plain" {
		t.Errorf("Content-Type should be text/plain, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextXML tests that the response is serialized as XML
// and Content-Type is set to application/xml
func TestContextXML(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		c.XML(200, H{"foo": "bar"})
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "<map><foo>bar</foo></map>" {
		t.Errorf("Response should be <map><foo>bar</foo></map>, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "application/xml" {
		t.Errorf("Content-Type should be application/xml, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextData tests that the response can be written from `bytesting`
// with specified MIME type
func TestContextData(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test/csv", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test/csv", func(c *Context) {
		c.Data(200, "text/csv", []byte(`foo,bar`))
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "foo,bar" {
		t.Errorf("Response should be foo&bar, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "text/csv" {
		t.Errorf("Content-Type should be text/csv, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

func TestContextFile(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test/file", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test/file", func(c *Context) {
		c.File("./gin.go")
	})

	r.ServeHTTP(w, req)

	bodyAsString := w.Body.String()

	if len(bodyAsString) == 0 {
		t.Errorf("Got empty body instead of file data")
	}

	if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
		t.Errorf("Content-Type should be text/plain; charset=utf-8, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestHandlerFunc - ensure that custom middleware works properly
func TestHandlerFunc(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1
		context.Next()
		stepsPassed += 1
	})

	r.ServeHTTP(w, req)

	if w.Code != 404 {
		t.Errorf("Response code should be Not found, was: %s", w.Code)
	}

	if stepsPassed != 2 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}
}

// TestBadAbortHandlersChain - ensure that Abort after switch context will not interrupt pending handlers
func TestBadAbortHandlersChain(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1
		context.Next()
		stepsPassed += 1
		// after check and abort
		context.Abort(409)
	},
		func(context *Context) {
			stepsPassed += 1
			context.Next()
			stepsPassed += 1
			context.Abort(403)
		},
	)

	r.ServeHTTP(w, req)

	if w.Code != 403 {
		t.Errorf("Response code should be Forbiden, was: %s", w.Code)
	}

	if stepsPassed != 4 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}
}

// TestAbortHandlersChain - ensure that Abort interrupt used middlewares in fifo order
func TestAbortHandlersChain(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1
		context.Abort(409)
	},
		func(context *Context) {
			stepsPassed += 1
			context.Next()
			stepsPassed += 1
		},
	)

	r.ServeHTTP(w, req)

	if w.Code != 409 {
		t.Errorf("Response code should be Conflict, was: %s", w.Code)
	}

	if stepsPassed != 1 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}
}

// TestFailHandlersChain - ensure that Fail interrupt used middlewares in fifo order as
// as well as Abort
func TestFailHandlersChain(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1

		context.Fail(500, errors.New("foo"))
	},
		func(context *Context) {
			stepsPassed += 1
			context.Next()
			stepsPassed += 1
		},
	)

	r.ServeHTTP(w, req)

	if w.Code != 500 {
		t.Errorf("Response code should be Server error, was: %s", w.Code)
	}

	if stepsPassed != 1 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}

}
<commit_msg>Added tests for JSON binding.<commit_after>package gin

import (
	"bytes"
	"errors"
	"html/template"
	"net/http"
	"net/http/httptest"
	"testing"
)

// TestContextParamsGet tests that a parameter can be parsed from the URL.
func TestContextParamsByName(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test/alexandernyquist", nil)
	w := httptest.NewRecorder()
	name := ""

	r := Default()
	r.GET("/test/:name", func(c *Context) {
		name = c.Params.ByName("name")
	})

	r.ServeHTTP(w, req)

	if name != "alexandernyquist" {
		t.Errorf("Url parameter was not correctly parsed. Should be alexandernyquist, was %s.", name)
	}
}

// TestContextSetGet tests that a parameter is set correctly on the
// current context and can be retrieved using Get.
func TestContextSetGet(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		// Key should be lazily created
		if c.Keys != nil {
			t.Error("Keys should be nil")
		}

		// Set
		c.Set("foo", "bar")

		v, err := c.Get("foo")
		if err != nil {
			t.Errorf("Error on exist key")
		}
		if v != "bar" {
			t.Errorf("Value should be bar, was %s", v)
		}
	})

	r.ServeHTTP(w, req)
}

// TestContextJSON tests that the response is serialized as JSON
// and Content-Type is set to application/json
func TestContextJSON(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		c.JSON(200, H{"foo": "bar"})
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "{\"foo\":\"bar\"}\n" {
		t.Errorf("Response should be {\"foo\":\"bar\"}, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "application/json" {
		t.Errorf("Content-Type should be application/json, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextHTML tests that the response executes the templates
// and responds with Content-Type set to text/html
func TestContextHTML(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	templ, _ := template.New("t").Parse(`Hello {{.Name}}`)
	r.SetHTMLTemplate(templ)

	type TestData struct{ Name string }

	r.GET("/test", func(c *Context) {
		c.HTML(200, "t", TestData{"alexandernyquist"})
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "Hello alexandernyquist" {
		t.Errorf("Response should be Hello alexandernyquist, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "text/html" {
		t.Errorf("Content-Type should be text/html, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextString tests that the response is returned
// with Content-Type set to text/plain
func TestContextString(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		c.String(200, "test")
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "test" {
		t.Errorf("Response should be test, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "text/plain" {
		t.Errorf("Content-Type should be text/plain, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextXML tests that the response is serialized as XML
// and Content-Type is set to application/xml
func TestContextXML(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test", func(c *Context) {
		c.XML(200, H{"foo": "bar"})
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "<map><foo>bar</foo></map>" {
		t.Errorf("Response should be <map><foo>bar</foo></map>, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "application/xml" {
		t.Errorf("Content-Type should be application/xml, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestContextData tests that the response can be written from `bytesting`
// with specified MIME type
func TestContextData(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test/csv", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test/csv", func(c *Context) {
		c.Data(200, "text/csv", []byte(`foo,bar`))
	})

	r.ServeHTTP(w, req)

	if w.Body.String() != "foo,bar" {
		t.Errorf("Response should be foo&bar, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "text/csv" {
		t.Errorf("Content-Type should be text/csv, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

func TestContextFile(t *testing.T) {
	req, _ := http.NewRequest("GET", "/test/file", nil)
	w := httptest.NewRecorder()

	r := Default()
	r.GET("/test/file", func(c *Context) {
		c.File("./gin.go")
	})

	r.ServeHTTP(w, req)

	bodyAsString := w.Body.String()

	if len(bodyAsString) == 0 {
		t.Errorf("Got empty body instead of file data")
	}

	if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
		t.Errorf("Content-Type should be text/plain; charset=utf-8, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

// TestHandlerFunc - ensure that custom middleware works properly
func TestHandlerFunc(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1
		context.Next()
		stepsPassed += 1
	})

	r.ServeHTTP(w, req)

	if w.Code != 404 {
		t.Errorf("Response code should be Not found, was: %s", w.Code)
	}

	if stepsPassed != 2 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}
}

// TestBadAbortHandlersChain - ensure that Abort after switch context will not interrupt pending handlers
func TestBadAbortHandlersChain(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1
		context.Next()
		stepsPassed += 1
		// after check and abort
		context.Abort(409)
	},
		func(context *Context) {
			stepsPassed += 1
			context.Next()
			stepsPassed += 1
			context.Abort(403)
		},
	)

	r.ServeHTTP(w, req)

	if w.Code != 403 {
		t.Errorf("Response code should be Forbiden, was: %s", w.Code)
	}

	if stepsPassed != 4 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}
}

// TestAbortHandlersChain - ensure that Abort interrupt used middlewares in fifo order
func TestAbortHandlersChain(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1
		context.Abort(409)
	},
		func(context *Context) {
			stepsPassed += 1
			context.Next()
			stepsPassed += 1
		},
	)

	r.ServeHTTP(w, req)

	if w.Code != 409 {
		t.Errorf("Response code should be Conflict, was: %s", w.Code)
	}

	if stepsPassed != 1 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}
}

// TestFailHandlersChain - ensure that Fail interrupt used middlewares in fifo order as
// as well as Abort
func TestFailHandlersChain(t *testing.T) {

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	r := Default()
	var stepsPassed int = 0

	r.Use(func(context *Context) {
		stepsPassed += 1

		context.Fail(500, errors.New("foo"))
	},
		func(context *Context) {
			stepsPassed += 1
			context.Next()
			stepsPassed += 1
		},
	)

	r.ServeHTTP(w, req)

	if w.Code != 500 {
		t.Errorf("Response code should be Server error, was: %s", w.Code)
	}

	if stepsPassed != 1 {
		t.Errorf("Falied to switch context in handler function: %s", stepsPassed)
	}

}

func TestBindingJSON(t *testing.T) {

	body := bytes.NewBuffer([]byte("{\"foo\":\"bar\"}"))

	r := Default()
	r.POST("/binding/json", func(c *Context) {
		var body struct {
			Foo string `json:"foo"`
		}
		if c.Bind(&body) {
			c.JSON(200, H{"parsed": body.Foo})
		}
	})

	req, _ := http.NewRequest("POST", "/binding/json", body)
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()

	r.ServeHTTP(w, req)

	if w.Code != 200 {
		t.Errorf("Response code should be Ok, was: %s", w.Code)
	}

	if w.Body.String() != "{\"parsed\":\"bar\"}\n" {
		t.Errorf("Response should be {\"parsed\":\"bar\"}, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "application/json" {
		t.Errorf("Content-Type should be application/json, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

func TestBindingJSONEncoding(t *testing.T) {

	body := bytes.NewBuffer([]byte("{\"foo\":\"嘉\"}"))

	r := Default()
	r.POST("/binding/json", func(c *Context) {
		var body struct {
			Foo string `json:"foo"`
		}
		if c.Bind(&body) {
			c.JSON(200, H{"parsed": body.Foo})
		}
	})

	req, _ := http.NewRequest("POST", "/binding/json", body)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")
	w := httptest.NewRecorder()

	r.ServeHTTP(w, req)

	if w.Code != 200 {
		t.Errorf("Response code should be Ok, was: %s", w.Code)
	}

	if w.Body.String() != "{\"parsed\":\"嘉\"}\n" {
		t.Errorf("Response should be {\"parsed\":\"嘉\"}, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") != "application/json" {
		t.Errorf("Content-Type should be application/json, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

func TestBindingJSONNoContentType(t *testing.T) {

	body := bytes.NewBuffer([]byte("{\"foo\":\"bar\"}"))

	r := Default()
	r.POST("/binding/json", func(c *Context) {
		var body struct {
			Foo string `json:"foo"`
		}
		if c.Bind(&body) {
			c.JSON(200, H{"parsed": body.Foo})
		}

	})

	req, _ := http.NewRequest("POST", "/binding/json", body)
	w := httptest.NewRecorder()

	r.ServeHTTP(w, req)

	if w.Code != 400 {
		t.Errorf("Response code should be Bad request, was: %s", w.Code)
	}

	if w.Body.String() == "{\"parsed\":\"bar\"}\n" {
		t.Errorf("Response should not be {\"parsed\":\"bar\"}, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") == "application/json" {
		t.Errorf("Content-Type should not be application/json, was %s", w.HeaderMap.Get("Content-Type"))
	}
}

func TestBindingJSONMalformed(t *testing.T) {

	body := bytes.NewBuffer([]byte("\"foo\":\"bar\"\n"))

	r := Default()
	r.POST("/binding/json", func(c *Context) {
		var body struct {
			Foo string `json:"foo"`
		}
		if c.Bind(&body) {
			c.JSON(200, H{"parsed": body.Foo})
		}

	})

	req, _ := http.NewRequest("POST", "/binding/json", body)
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()

	r.ServeHTTP(w, req)

	if w.Code != 400 {
		t.Errorf("Response code should be Bad request, was: %s", w.Code)
	}
	if w.Body.String() == "{\"parsed\":\"bar\"}\n" {
		t.Errorf("Response should not be {\"parsed\":\"bar\"}, was: %s", w.Body.String())
	}

	if w.HeaderMap.Get("Content-Type") == "application/json" {
		t.Errorf("Content-Type should not be application/json, was %s", w.HeaderMap.Get("Content-Type"))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	golambda "github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/service/acm"
	"github.com/aws/aws-sdk-go/service/s3"
	flags "github.com/jessevdk/go-flags"
	"github.com/nabeken/aaa/agent"
	"github.com/nabeken/aaa/command"
	"github.com/nabeken/aaa/slack"
	"github.com/nabeken/aws-go-s3/bucket"
	"github.com/pkg/errors"
)

const challengeType = "dns-01"

var options struct {
	S3Bucket   string
	S3KMSKeyID string
	Email      string
}

type dispatcher struct {
}

func (d *dispatcher) handleCertCommand(arg string, slcmd *slack.Command) (string, error) {
	store, err := command.NewStore(options.Email, options.S3Bucket, options.S3KMSKeyID)
	if err != nil {
		return "", errors.Wrap(err, "failed to initialize the store")
	}

	// opts is a subset of command.CertCommand.
	var opts struct {
		CreateKey  bool `long:"create-key"`
		RSAKeySize int  `long:"rsa-key-size" default:"4096"`
	}
	domains, err := flags.ParseArgs(&opts, strings.Split(arg, " "))
	if err != nil {
		return "", err
	}

	log.Println("domains:", domains)

	// How to execute in Slack:
	// /letsencrypt [command] [domains...] [optional_arguments]
	// For example: /letsencrypt cert foo.bar.com --create-key --rsa-key-size 2048
	svc := &command.CertService{
		CommonName: domains[0],
		Domains:    domains[1:],
		CreateKey:  opts.CreateKey,
		RSAKeySize: opts.RSAKeySize,
		Store:      store,
	}

	if err := svc.Run(); err != nil {
		return "", err
	}

	return fmt.Sprintf(
		"%s The certificate for %s is now available!\n```\n"+
			"aws s3 sync s3://%s/aaa-data/v2/%s/domain/%s/ %s```",
		slack.FormatUserName(slcmd.UserName),
		domains,
		options.S3Bucket,
		options.Email,
		svc.CommonName,
		svc.CommonName,
	), nil
}

func (d *dispatcher) handleUploadCommand(arg string, slcmd *slack.Command) (string, error) {
	sess := command.NewAWSSession()
	s3b := bucket.New(s3.New(sess), options.S3Bucket)
	svc := &command.UploadService{
		Domain:  arg,
		Email:   options.Email,
		S3Filer: agent.NewS3Filer(s3b, ""),
		ACMconn: acm.New(sess),
	}

	arn, err := svc.Run()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf(
		"%s The certificate `%s` has been uploaded to ACM! ARN is `%s`",
		slack.FormatUserName(slcmd.UserName),
		arg,
		arn,
	), nil
}

func realmain(event json.RawMessage) (interface{}, error) {
	slcmd, err := slack.ParseCommand(event)
	if err != nil {
		return "", errors.Wrap(err, "failed to parse the command")
	}
	log.Println("slack command:", slcmd)

	handleError := func(err error) error {
		return slack.PostErrorResponse(err, slcmd)
	}

	command := strings.SplitN(slcmd.Text, " ", 2)
	if len(command) != 2 {
		return "", handleError(errors.New("invalid command"))
	}

	dispatcher := &dispatcher{}

	var handler func(string, *slack.Command) (string, error)
	switch command[0] {
	case "cert":
		handler = dispatcher.handleCertCommand
	case "upload":
		handler = dispatcher.handleUploadCommand
	}

	respStr, err := handler(command[1], slcmd)
	if err != nil {
		return nil, handleError(err)
	}
	resp := &slack.CommandResponse{
		ResponseType: "in_channel",
		Text:         respStr,
	}
	return slack.PostResponse(slcmd.ResponseURL, resp), nil
}

func main() {
	// initialize global command option
	options.S3Bucket = os.Getenv("S3_BUCKET")
	options.S3KMSKeyID = os.Getenv("KMS_KEY_ID")
	options.Email = os.Getenv("EMAIL")

	golambda.Start(realmain)
}
<commit_msg>executor: quote a domain so that it works with wildcard<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	golambda "github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/service/acm"
	"github.com/aws/aws-sdk-go/service/s3"
	flags "github.com/jessevdk/go-flags"
	"github.com/nabeken/aaa/agent"
	"github.com/nabeken/aaa/command"
	"github.com/nabeken/aaa/slack"
	"github.com/nabeken/aws-go-s3/bucket"
	"github.com/pkg/errors"
)

const challengeType = "dns-01"

var options struct {
	S3Bucket   string
	S3KMSKeyID string
	Email      string
}

type dispatcher struct {
}

func (d *dispatcher) handleCertCommand(arg string, slcmd *slack.Command) (string, error) {
	store, err := command.NewStore(options.Email, options.S3Bucket, options.S3KMSKeyID)
	if err != nil {
		return "", errors.Wrap(err, "failed to initialize the store")
	}

	// opts is a subset of command.CertCommand.
	var opts struct {
		CreateKey  bool `long:"create-key"`
		RSAKeySize int  `long:"rsa-key-size" default:"4096"`
	}
	domains, err := flags.ParseArgs(&opts, strings.Split(arg, " "))
	if err != nil {
		return "", err
	}

	log.Println("domains:", domains)

	// How to execute in Slack:
	// /letsencrypt [command] [domains...] [optional_arguments]
	// For example: /letsencrypt cert foo.bar.com --create-key --rsa-key-size 2048
	svc := &command.CertService{
		CommonName: domains[0],
		Domains:    domains[1:],
		CreateKey:  opts.CreateKey,
		RSAKeySize: opts.RSAKeySize,
		Store:      store,
	}

	if err := svc.Run(); err != nil {
		return "", err
	}

	return fmt.Sprintf(
		"%s The certificate for %s is now available!\n```\n"+
			"aws s3 sync 's3://%s/aaa-data/v2/%s/domain/%s/' '%s'```",
		slack.FormatUserName(slcmd.UserName),
		domains,
		options.S3Bucket,
		options.Email,
		svc.CommonName,
		svc.CommonName,
	), nil
}

func (d *dispatcher) handleUploadCommand(arg string, slcmd *slack.Command) (string, error) {
	sess := command.NewAWSSession()
	s3b := bucket.New(s3.New(sess), options.S3Bucket)
	svc := &command.UploadService{
		Domain:  arg,
		Email:   options.Email,
		S3Filer: agent.NewS3Filer(s3b, ""),
		ACMconn: acm.New(sess),
	}

	arn, err := svc.Run()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf(
		"%s The certificate `%s` has been uploaded to ACM! ARN is `%s`",
		slack.FormatUserName(slcmd.UserName),
		arg,
		arn,
	), nil
}

func realmain(event json.RawMessage) (interface{}, error) {
	slcmd, err := slack.ParseCommand(event)
	if err != nil {
		return "", errors.Wrap(err, "failed to parse the command")
	}
	log.Println("slack command:", slcmd)

	handleError := func(err error) error {
		return slack.PostErrorResponse(err, slcmd)
	}

	command := strings.SplitN(slcmd.Text, " ", 2)
	if len(command) != 2 {
		return "", handleError(errors.New("invalid command"))
	}

	dispatcher := &dispatcher{}

	var handler func(string, *slack.Command) (string, error)
	switch command[0] {
	case "cert":
		handler = dispatcher.handleCertCommand
	case "upload":
		handler = dispatcher.handleUploadCommand
	}

	respStr, err := handler(command[1], slcmd)
	if err != nil {
		return nil, handleError(err)
	}
	resp := &slack.CommandResponse{
		ResponseType: "in_channel",
		Text:         respStr,
	}
	return slack.PostResponse(slcmd.ResponseURL, resp), nil
}

func main() {
	// initialize global command option
	options.S3Bucket = os.Getenv("S3_BUCKET")
	options.S3KMSKeyID = os.Getenv("KMS_KEY_ID")
	options.Email = os.Getenv("EMAIL")

	golambda.Start(realmain)
}
<|endoftext|>


<commit_before>package atlas

import (
	"image"

	"github.com/PieterD/crap/roguelike/game/atlas/aspect"
	"github.com/PieterD/crap/roguelike/grid"
	"github.com/PieterD/crap/roguelike/vision"
)

type Glyph struct {
	Code int
	Fore grid.Color
	Back grid.Color
}

func Translate(screen image.Rectangle, center image.Point, atlas image.Rectangle) image.Point {
	tl := center.Sub(screen.Max.Div(2))
	if screen.Max.X > atlas.Max.X {
		tl.X = -(screen.Max.X - atlas.Max.X) / 2
	} else {
		if tl.X < 0 {
			tl.X = 0
		}
		if tl.X >= atlas.Max.X-screen.Max.X {
			tl.X = atlas.Max.X - screen.Max.X
		}
	}
	if screen.Max.Y > atlas.Max.Y {
		tl.Y = -(screen.Max.Y - atlas.Max.Y) / 2
	} else {
		if tl.Y < 0 {
			tl.Y = 0
		}
		if tl.Y >= atlas.Max.Y-screen.Max.Y {
			tl.Y = atlas.Max.Y - screen.Max.Y
		}
	}
	return tl
}

type Atlas struct {
	cells      []Cell
	bounds     image.Rectangle
	visibility uint64
}

func (atlas *Atlas) cell(p image.Point) *Cell {
	if !p.In(atlas.bounds) {
		return &Cell{}
	}
	return &atlas.cells[p.X+p.Y*atlas.bounds.Max.X]
}

func New() *Atlas {
	w := 100
	h := 100
	atlas := &Atlas{
		cells: make([]Cell, w*h),
		bounds: image.Rectangle{
			Min: image.Point{X: 0, Y: 0},
			Max: image.Point{X: w, Y: h},
		},
		visibility: 1,
	}
	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			atlas.setFeature(x, y, aspect.Floor)
		}
	}
	for x := 0; x < w; x++ {
		atlas.setFeature(x, 0, aspect.Wall)
		atlas.setFeature(x, h-1, aspect.Wall)
	}
	for y := 0; y < h; y++ {
		atlas.setFeature(0, y, aspect.Wall)
		atlas.setFeature(w-1, y, aspect.Wall)
	}
	for x := 0; x < w; x += 10 {
		for y := 0; y < h; y += 10 {
			atlas.setFeature(x, y, aspect.Wall)
		}
	}

	wallTest(atlas)

	max := 5
	for i := 1; i <= max; i++ {
		atlas.setFeature(max, i, aspect.Wall)
		atlas.setFeature(i, max, aspect.Wall)
	}
	atlas.setFeature(max, 2, aspect.ClosedDoor)

	for x := 0; x <= 10; x++ {
		atlas.setFeature(45+x, 5, aspect.Wall)
		atlas.setFeature(45+x, 15, aspect.Wall)
		atlas.setFeature(45, 5+x, aspect.Wall)
		atlas.setFeature(55, 5+x, aspect.Wall)
	}
	atlas.setFeature(50, 5, aspect.ClosedDoor)
	atlas.setFeature(50, 10, aspect.Floor)

	return atlas
}

func wallTest(atlas *Atlas) {
	// specials
	atlas.setFeature(10, 10, aspect.Wall)

	atlas.setFeature(10, 20, aspect.Wall)
	atlas.setFeature(10, 19, aspect.Wall)
	atlas.setFeature(10, 21, aspect.Wall)

	atlas.setFeature(10, 30, aspect.Wall)
	atlas.setFeature(9, 30, aspect.Wall)
	atlas.setFeature(11, 30, aspect.Wall)

	atlas.setFeature(10, 40, aspect.Wall)
	atlas.setFeature(9, 40, aspect.Wall)
	atlas.setFeature(11, 40, aspect.Wall)
	atlas.setFeature(10, 39, aspect.Wall)
	atlas.setFeature(10, 41, aspect.Wall)

	// doubles
	atlas.setFeature(20, 10, aspect.Wall)
	atlas.setFeature(20, 9, aspect.Wall)

	atlas.setFeature(20, 20, aspect.Wall)
	atlas.setFeature(21, 20, aspect.Wall)

	atlas.setFeature(20, 30, aspect.Wall)
	atlas.setFeature(20, 31, aspect.Wall)

	atlas.setFeature(20, 40, aspect.Wall)
	atlas.setFeature(19, 40, aspect.Wall)

	// triples
	atlas.setFeature(30, 10, aspect.Wall)
	atlas.setFeature(30, 9, aspect.Wall)
	atlas.setFeature(31, 10, aspect.Wall)

	atlas.setFeature(30, 20, aspect.Wall)
	atlas.setFeature(31, 20, aspect.Wall)
	atlas.setFeature(30, 21, aspect.Wall)

	atlas.setFeature(30, 30, aspect.Wall)
	atlas.setFeature(29, 30, aspect.Wall)
	atlas.setFeature(30, 31, aspect.Wall)

	atlas.setFeature(30, 40, aspect.Wall)
	atlas.setFeature(30, 39, aspect.Wall)
	atlas.setFeature(29, 40, aspect.Wall)

	// quads
	atlas.setFeature(40, 10, aspect.Wall)
	atlas.setFeature(40, 9, aspect.Wall)
	atlas.setFeature(40, 11, aspect.Wall)
	atlas.setFeature(41, 10, aspect.Wall)

	atlas.setFeature(40, 20, aspect.Wall)
	atlas.setFeature(41, 20, aspect.Wall)
	atlas.setFeature(40, 21, aspect.Wall)
	atlas.setFeature(39, 20, aspect.Wall)

	atlas.setFeature(40, 30, aspect.Wall)
	atlas.setFeature(40, 29, aspect.Wall)
	atlas.setFeature(39, 30, aspect.Wall)
	atlas.setFeature(40, 31, aspect.Wall)

	atlas.setFeature(40, 40, aspect.Wall)
	atlas.setFeature(40, 39, aspect.Wall)
	atlas.setFeature(41, 40, aspect.Wall)
	atlas.setFeature(39, 40, aspect.Wall)
}

func (atlas *Atlas) Bounds() image.Rectangle {
	return atlas.bounds
}

func (atlas *Atlas) GetFeature(pos image.Point) aspect.Feature {
	return atlas.cell(pos).feature
}

func (atlas *Atlas) SetFeature(pos image.Point, feature aspect.Feature) {
	atlas.cell(pos).feature = feature
}

func (atlas *Atlas) setFeature(x, y int, ft aspect.Feature) {
	atlas.SetFeature(image.Point{X: x, Y: y}, ft)
}

func (atlas *Atlas) Glyph(p image.Point) Glyph {
	cell := atlas.cell(p)
	var glyph Glyph
	switch cell.feature {
	case aspect.Wall:
		glyph = Glyph{
			Code: atlas.wallrune(p, singleWall),
			Fore: grid.Gray,
			Back: grid.Black,
		}
	case aspect.Floor:
		glyph = Glyph{
			Code: atlas.floorrune(p, floorRune),
			Fore: grid.DarkGray,
			Back: grid.Black,
		}
	case aspect.ClosedDoor:
		glyph = Glyph{
			Code: 43,
			Fore: grid.DarkRed,
			Back: grid.Black,
		}
	case aspect.OpenDoor:
		glyph = Glyph{
			Code: 47,
			Fore: grid.DarkRed,
			Back: grid.Black,
		}
	default:
		glyph = Glyph{
			Code: 32,
			Fore: grid.Black,
			Back: grid.Black,
		}
	}
	if !atlas.IsVisible(p) {
		if glyph.Fore != grid.Black {
			glyph.Fore = grid.VeryDarkGray
		}
		glyph.Back = grid.Black
	}
	return glyph
}

func (atlas *Atlas) IsPassable(p image.Point) bool {
	return atlas.cell(p).feature.Passable
}

func (atlas *Atlas) IsTransparent(p image.Point) bool {
	return atlas.cell(p).feature.Transparent
}

func (atlas *Atlas) SetVisible(p image.Point) {
	atlas.cell(p).visibility = atlas.visibility
}

func (atlas *Atlas) IsVisible(p image.Point) bool {
	return atlas.cell(p).visibility == atlas.visibility
}

func (atlas *Atlas) Vision(source image.Point) {
	atlas.visibility++
	vision.ShadowCast(atlas, vision.EndlessRadius(), source)
}

//var singleWall = []int{79, 179, 196, 192, 218, 191, 217, 195, 194, 180, 193, 197}
//var singleWall = []int{9, 179, 196, 192, 218, 191, 217, 195, 194, 180, 193, 197}
var singleWall = []int{233, 179, 196, 192, 218, 191, 217, 195, 194, 180, 193, 197}
var doubleWall = []int{233, 186, 205, 200, 201, 187, 188, 204, 203, 185, 202, 206}
var wallRune = []int{0, 1, 2, 3, 1, 1, 4, 7, 2, 6, 2, 10, 5, 9, 8, 11}

func (atlas *Atlas) wallrune(p image.Point, runes []int) int {
	x := image.Point{X: 1}
	y := image.Point{Y: 1}
	bits := 0
	if atlas.cell(p.Sub(y)).feature.Wallable {
		bits |= 1
	}
	if atlas.cell(p.Add(x)).feature.Wallable {
		bits |= 2
	}
	if atlas.cell(p.Add(y)).feature.Wallable {
		bits |= 4
	}
	if atlas.cell(p.Sub(x)).feature.Wallable {
		bits |= 8
	}
	return runes[wallRune[bits]]
}

//var floorRune = []int{44, 46, 96, 249, 250}
//var floorRune = []int{44, 46, 96, 249, 39}
//var floorRune = []int{44, 46, 96, 249, 39, 250, 250}
var floorRune = []int{250, 44, 250, 46, 250, 96, 250, 249, 250, 39, 250}

//var floorRune = []int{250}

func (atlas *Atlas) floorrune(p image.Point, runes []int) int {
	x := uint64(p.X)
	y := uint64(p.Y)
	ui := ((x<<32)|y)*(x^y) + y - x
	return runes[ui%uint64(len(runes))]
}
<commit_msg>Use parallel by default<commit_after>package atlas

import (
	"image"

	"github.com/PieterD/crap/roguelike/game/atlas/aspect"
	"github.com/PieterD/crap/roguelike/grid"
	"github.com/PieterD/crap/roguelike/vision"
)

type Glyph struct {
	Code int
	Fore grid.Color
	Back grid.Color
}

func Translate(screen image.Rectangle, center image.Point, atlas image.Rectangle) image.Point {
	tl := center.Sub(screen.Max.Div(2))
	if screen.Max.X > atlas.Max.X {
		tl.X = -(screen.Max.X - atlas.Max.X) / 2
	} else {
		if tl.X < 0 {
			tl.X = 0
		}
		if tl.X >= atlas.Max.X-screen.Max.X {
			tl.X = atlas.Max.X - screen.Max.X
		}
	}
	if screen.Max.Y > atlas.Max.Y {
		tl.Y = -(screen.Max.Y - atlas.Max.Y) / 2
	} else {
		if tl.Y < 0 {
			tl.Y = 0
		}
		if tl.Y >= atlas.Max.Y-screen.Max.Y {
			tl.Y = atlas.Max.Y - screen.Max.Y
		}
	}
	return tl
}

type Atlas struct {
	cells      []Cell
	bounds     image.Rectangle
	visibility uint64
}

func (atlas *Atlas) cell(p image.Point) *Cell {
	if !p.In(atlas.bounds) {
		return &Cell{}
	}
	return &atlas.cells[p.X+p.Y*atlas.bounds.Max.X]
}

func New() *Atlas {
	w := 100
	h := 100
	atlas := &Atlas{
		cells: make([]Cell, w*h),
		bounds: image.Rectangle{
			Min: image.Point{X: 0, Y: 0},
			Max: image.Point{X: w, Y: h},
		},
		visibility: 1,
	}
	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			atlas.setFeature(x, y, aspect.Floor)
		}
	}
	for x := 0; x < w; x++ {
		atlas.setFeature(x, 0, aspect.Wall)
		atlas.setFeature(x, h-1, aspect.Wall)
	}
	for y := 0; y < h; y++ {
		atlas.setFeature(0, y, aspect.Wall)
		atlas.setFeature(w-1, y, aspect.Wall)
	}
	for x := 0; x < w; x += 10 {
		for y := 0; y < h; y += 10 {
			atlas.setFeature(x, y, aspect.Wall)
		}
	}

	wallTest(atlas)

	max := 5
	for i := 1; i <= max; i++ {
		atlas.setFeature(max, i, aspect.Wall)
		atlas.setFeature(i, max, aspect.Wall)
	}
	atlas.setFeature(max, 2, aspect.ClosedDoor)

	for x := 0; x <= 10; x++ {
		atlas.setFeature(45+x, 5, aspect.Wall)
		atlas.setFeature(45+x, 15, aspect.Wall)
		atlas.setFeature(45, 5+x, aspect.Wall)
		atlas.setFeature(55, 5+x, aspect.Wall)
	}
	atlas.setFeature(50, 5, aspect.ClosedDoor)
	atlas.setFeature(50, 10, aspect.Floor)

	return atlas
}

func wallTest(atlas *Atlas) {
	// specials
	atlas.setFeature(10, 10, aspect.Wall)

	atlas.setFeature(10, 20, aspect.Wall)
	atlas.setFeature(10, 19, aspect.Wall)
	atlas.setFeature(10, 21, aspect.Wall)

	atlas.setFeature(10, 30, aspect.Wall)
	atlas.setFeature(9, 30, aspect.Wall)
	atlas.setFeature(11, 30, aspect.Wall)

	atlas.setFeature(10, 40, aspect.Wall)
	atlas.setFeature(9, 40, aspect.Wall)
	atlas.setFeature(11, 40, aspect.Wall)
	atlas.setFeature(10, 39, aspect.Wall)
	atlas.setFeature(10, 41, aspect.Wall)

	// doubles
	atlas.setFeature(20, 10, aspect.Wall)
	atlas.setFeature(20, 9, aspect.Wall)

	atlas.setFeature(20, 20, aspect.Wall)
	atlas.setFeature(21, 20, aspect.Wall)

	atlas.setFeature(20, 30, aspect.Wall)
	atlas.setFeature(20, 31, aspect.Wall)

	atlas.setFeature(20, 40, aspect.Wall)
	atlas.setFeature(19, 40, aspect.Wall)

	// triples
	atlas.setFeature(30, 10, aspect.Wall)
	atlas.setFeature(30, 9, aspect.Wall)
	atlas.setFeature(31, 10, aspect.Wall)

	atlas.setFeature(30, 20, aspect.Wall)
	atlas.setFeature(31, 20, aspect.Wall)
	atlas.setFeature(30, 21, aspect.Wall)

	atlas.setFeature(30, 30, aspect.Wall)
	atlas.setFeature(29, 30, aspect.Wall)
	atlas.setFeature(30, 31, aspect.Wall)

	atlas.setFeature(30, 40, aspect.Wall)
	atlas.setFeature(30, 39, aspect.Wall)
	atlas.setFeature(29, 40, aspect.Wall)

	// quads
	atlas.setFeature(40, 10, aspect.Wall)
	atlas.setFeature(40, 9, aspect.Wall)
	atlas.setFeature(40, 11, aspect.Wall)
	atlas.setFeature(41, 10, aspect.Wall)

	atlas.setFeature(40, 20, aspect.Wall)
	atlas.setFeature(41, 20, aspect.Wall)
	atlas.setFeature(40, 21, aspect.Wall)
	atlas.setFeature(39, 20, aspect.Wall)

	atlas.setFeature(40, 30, aspect.Wall)
	atlas.setFeature(40, 29, aspect.Wall)
	atlas.setFeature(39, 30, aspect.Wall)
	atlas.setFeature(40, 31, aspect.Wall)

	atlas.setFeature(40, 40, aspect.Wall)
	atlas.setFeature(40, 39, aspect.Wall)
	atlas.setFeature(41, 40, aspect.Wall)
	atlas.setFeature(39, 40, aspect.Wall)
}

func (atlas *Atlas) Bounds() image.Rectangle {
	return atlas.bounds
}

func (atlas *Atlas) GetFeature(pos image.Point) aspect.Feature {
	return atlas.cell(pos).feature
}

func (atlas *Atlas) SetFeature(pos image.Point, feature aspect.Feature) {
	atlas.cell(pos).feature = feature
}

func (atlas *Atlas) setFeature(x, y int, ft aspect.Feature) {
	atlas.SetFeature(image.Point{X: x, Y: y}, ft)
}

func (atlas *Atlas) Glyph(p image.Point) Glyph {
	cell := atlas.cell(p)
	var glyph Glyph
	switch cell.feature {
	case aspect.Wall:
		glyph = Glyph{
			Code: atlas.wallrune(p, singleWall),
			Fore: grid.Gray,
			Back: grid.Black,
		}
	case aspect.Floor:
		glyph = Glyph{
			Code: atlas.floorrune(p, floorRune),
			Fore: grid.DarkGray,
			Back: grid.Black,
		}
	case aspect.ClosedDoor:
		glyph = Glyph{
			Code: 43,
			Fore: grid.DarkRed,
			Back: grid.Black,
		}
	case aspect.OpenDoor:
		glyph = Glyph{
			Code: 47,
			Fore: grid.DarkRed,
			Back: grid.Black,
		}
	default:
		glyph = Glyph{
			Code: 32,
			Fore: grid.Black,
			Back: grid.Black,
		}
	}
	if !atlas.IsVisible(p) {
		if glyph.Fore != grid.Black {
			glyph.Fore = grid.VeryDarkGray
		}
		glyph.Back = grid.Black
	}
	return glyph
}

func (atlas *Atlas) IsPassable(p image.Point) bool {
	return atlas.cell(p).feature.Passable
}

func (atlas *Atlas) IsTransparent(p image.Point) bool {
	return atlas.cell(p).feature.Transparent
}

func (atlas *Atlas) SetVisible(p image.Point) {
	atlas.cell(p).visibility = atlas.visibility
}

func (atlas *Atlas) IsVisible(p image.Point) bool {
	return atlas.cell(p).visibility == atlas.visibility
}

func (atlas *Atlas) Vision(source image.Point) {
	atlas.visibility++
	vision.ShadowCastPar(atlas, vision.EndlessRadius(), source)
}

//var singleWall = []int{79, 179, 196, 192, 218, 191, 217, 195, 194, 180, 193, 197}
//var singleWall = []int{9, 179, 196, 192, 218, 191, 217, 195, 194, 180, 193, 197}
var singleWall = []int{233, 179, 196, 192, 218, 191, 217, 195, 194, 180, 193, 197}
var doubleWall = []int{233, 186, 205, 200, 201, 187, 188, 204, 203, 185, 202, 206}
var wallRune = []int{0, 1, 2, 3, 1, 1, 4, 7, 2, 6, 2, 10, 5, 9, 8, 11}

func (atlas *Atlas) wallrune(p image.Point, runes []int) int {
	x := image.Point{X: 1}
	y := image.Point{Y: 1}
	bits := 0
	if atlas.cell(p.Sub(y)).feature.Wallable {
		bits |= 1
	}
	if atlas.cell(p.Add(x)).feature.Wallable {
		bits |= 2
	}
	if atlas.cell(p.Add(y)).feature.Wallable {
		bits |= 4
	}
	if atlas.cell(p.Sub(x)).feature.Wallable {
		bits |= 8
	}
	return runes[wallRune[bits]]
}

//var floorRune = []int{44, 46, 96, 249, 250}
//var floorRune = []int{44, 46, 96, 249, 39}
//var floorRune = []int{44, 46, 96, 249, 39, 250, 250}
var floorRune = []int{250, 44, 250, 46, 250, 96, 250, 249, 250, 39, 250}

//var floorRune = []int{250}

func (atlas *Atlas) floorrune(p image.Point, runes []int) int {
	x := uint64(p.X)
	y := uint64(p.Y)
	ui := ((x<<32)|y)*(x^y) + y - x
	return runes[ui%uint64(len(runes))]
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
)

type Message struct {
	Check       string `json:"check"`
	IncidentId  int    `json:"incidentid"`
	Action      string `json:"action"`
	Description string `json:"description"`
}

func (m Message) Title() string {
	switch m.Action {
	case "notify_of_close":
		return "Event Closed"
	case "assign":
		return "Event Assigned"
	}

	return strings.Title(m.Action)
}

func (m Message) Color() string {
	if m.Action == "notify_of_close" {
		return "good"
	} else {
		return "danger"
	}
}

type Payload struct {
	Channel     string       `json:"channel,omitempty"`
	Text        string       `json:"text,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
}

type Attachment struct {
	Fallback string `json:"fallback"`
	Text     string `json:"text"`
	Pretext  string `json:"pretext"`
	Color    string `json:"color"`
}

var (
	slackDomain  string
	slackToken   string
	slackChannel string
	slackHookUrl string
)

func main() {
	if slackDomain = os.Getenv("SLACK_DOMAIN"); slackDomain == "" {
		log.Fatalf("Please specify a SLACK_DOMAIN environment variable")
	}

	if slackToken = os.Getenv("SLACK_TOKEN"); slackToken == "" {
		log.Fatalf("Please specify a SLACK_TOKEN environment variable")
	}

	if slackChannel = os.Getenv("SLACK_CHANNEL"); slackChannel == "" {
		log.Fatalf("Please specify a SLACK_CHANNEL environment variable")
	}

	slackHookUrl = "https://" + slackDomain + "/services/hooks/incoming-webhook?token=" + slackToken

	http.HandleFunc("/notify", func(w http.ResponseWriter, r *http.Request) {

		log.Printf("received from pingdom: %+v", r.URL.RawQuery)

		msg := &Message{}
		if err := json.Unmarshal([]byte(r.URL.Query().Get("message")), msg); err != nil {
			respond(w, 500, map[string]string{"error": err.Error()})
			return
		}

		log.Printf("received from pingdom: %+v", msg)

		// Create a Slack notification payload
		payload := Payload{
			Channel:     slackChannel,
			Attachments: make([]Attachment, 1),
		}

		payload.Attachments[0] = Attachment{
			Fallback: msg.Description,
			Text:     msg.Description,
			Pretext:  msg.Title(),
			Color:    msg.Color(),
		}

		// Encode the payload
		buf, err := json.Marshal(payload)
		if err != nil {
			log.Printf("error: %s", err)
			respond(w, 500, map[string]string{"error": err.Error()})
			return
		}
		data := url.Values{}
		data.Set("payload", string(buf))

		// Send it off
		log.Printf("notifying slack: %+v", payload)
		resp, err := http.PostForm(slackHookUrl, data)
		defer resp.Body.Close()
		if err != nil {
			log.Printf("error: %s", err)
			respond(w, 500, map[string]string{"error": err.Error()})
			return
		}
		if resp.StatusCode != 200 {
			rbuf, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Printf("error: %s", err)
				respond(w, 500, map[string]string{"error": err.Error()})
			} else {
				log.Printf("unexpected response (%d): %s", resp.StatusCode, rbuf)
				respond(w, 500, map[string]string{"error": string(rbuf)})
			}
			return
		}

		log.Printf("success!")

		respond(w, 200, map[string]string{"status": "notified"})
	})

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	log.Printf("Listening on port %s...", port)

	http.ListenAndServe(":"+port, nil)
}

func respond(w http.ResponseWriter, status int, object interface{}) {
	buf, _ := json.Marshal(object)

	w.Header().Add("Content-Type", "application/json")
	w.Header().Add("Content-Length", strconv.Itoa(len(buf)))
	w.WriteHeader(status)
	w.Write(buf)
}
<commit_msg>Use old message format, refactor.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
)

type BadRequest struct {
	reason string
}

func (e BadRequest) Error() string {
	return fmt.Sprintf("bad request: %s", e.reason)
}

type BadResponse struct {
	status int
	body   string
}

func (e BadResponse) Error() string {
	return fmt.Sprintf("bad response (status %d): %s", e.status, e.body)
}

type Payload struct {
	Channel     string       `json:"channel,omitempty"`
	Text        string       `json:"text,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
}

type Attachment struct {
	Fallback string `json:"fallback"`
	Text     string `json:"text"`
	Pretext  string `json:"pretext"`
	Color    string `json:"color"`
}

var (
	slackDomain  string
	slackToken   string
	slackChannel string
	slackHookUrl string
)

func main() {
	if slackDomain = os.Getenv("SLACK_DOMAIN"); slackDomain == "" {
		fmt.Println("please specify a SLACK_DOMAIN environment variable")
		os.Exit(1)
	}

	if slackToken = os.Getenv("SLACK_TOKEN"); slackToken == "" {
		fmt.Println("please specify a SLACK_TOKEN environment variable")
		os.Exit(1)
	}

	if slackChannel = os.Getenv("SLACK_CHANNEL"); slackChannel == "" {
		fmt.Println("please specify a SLACK_CHANNEL environment variable")
		os.Exit(1)
	}

	slackHookUrl = fmt.Sprintf("https://%s/services/hooks/incoming-webhook?token=%s", slackDomain, slackToken)

	http.HandleFunc("/notify", notify)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	fmt.Printf("listening on port %s...\n", port)

	http.ListenAndServe(fmt.Sprintf(":%s", port), nil)
}

func notify(w http.ResponseWriter, r *http.Request) {
	// Extract the message
	message := r.URL.Query().Get("message")
	fmt.Printf("received message: '%s'\n", message)

	// Generate a payload
	data, err := encode(message)
	if err != nil {
		fmt.Printf("error encoding payload: %s\n", err)
		respond(w, 500, err.Error())
		return
	}

	if err = post(data); err != nil {
		fmt.Printf("error sending payload: %s\n", err)
		respond(w, 500, err.Error())
		return
	}

	fmt.Printf("successfully notified slack: '%s'\n", message)

	respond(w, 200, "success")
}

// Encodes a payload as url.Values suitable for a POST
func encode(message string) (data url.Values, err error) {
	if message == "" {
		err = BadRequest{reason: "empty message"}
		return
	}

	p := Payload{
		Channel:     slackChannel,
		Attachments: make([]Attachment, 1),
	}

	p.Attachments[0] = Attachment{
		Fallback: message,
		Text:     message,
	}

	if strings.Contains(message, "UP") {
		p.Attachments[0].Color = "good"
	} else if strings.Contains(message, "DOWN") {
		p.Attachments[0].Color = "danger"
	} else {
		p.Attachments[0].Color = "#cfcfcf"
	}

	buf, err := json.Marshal(p)
	if err != nil {
		return
	}

	data = url.Values{}
	data.Set("payload", string(buf))

	return
}

// Sends a POST request with the given values to Slack
func post(data url.Values) (err error) {
	resp, err := http.PostForm(slackHookUrl, data)
	defer resp.Body.Close()

	if err != nil {
		return
	}

	if resp.StatusCode != 200 {
		rbuf, _ := ioutil.ReadAll(resp.Body)
		err = BadResponse{status: resp.StatusCode, body: string(rbuf)}
	}

	return
}

func respond(w http.ResponseWriter, status int, response string) {
	w.Header().Add("Content-Length", strconv.Itoa(len([]byte(response))))
	w.WriteHeader(status)
	w.Write([]byte(response))
}
<|endoftext|>


<commit_before>package main

import (
	"path/filepath"
	"time"

	"github.com/bitmark-inc/logger"
	"github.com/fsnotify/fsnotify"
)

type FileWatcher interface {
	Start()
}

const (
	FileWatcherLoggerPrefix = "file-watcher"
)

type FileWatcherData struct {
	reader      ConfigReader
	log         *logger.L
	watcherData WatcherData
	watcher     *fsnotify.Watcher
}

type WatcherData struct {
	channels         WatcherChannel
	throttleInterval time.Duration
}

type WatcherChannel struct {
	change chan struct{}
	remove chan struct{}
}

func newFileWatcher(reader ConfigReader, log *logger.L, data WatcherData) FileWatcher {
	watcher, err := fsnotify.NewWatcher()
	if nil != err {
		log.Errorf("new watcher with error: %s", err.Error())
	}
	return &FileWatcherData{
		reader:      reader,
		log:         log,
		watcher:     watcher,
		watcherData: data,
	}
}

func (w *FileWatcherData) Start() {
	_, fileName, _ := w.reader.GetConfig()
	filePath, _ := filepath.Abs(filepath.Clean(fileName))

	w.watcher.Add(filePath)

	go func() {
		for {
			event := <-w.watcher.Events
			w.log.Infof("file event: %v", event)
			remove := w.watcherData.channels.remove
			change := w.watcherData.channels.change

			if event.Name != filepath.Clean(filePath) {
				w.log.Debug("file not match, discard")
				continue
			}

			if event.Op&fsnotify.Remove == fsnotify.Remove {
				w.log.Info("sending file remove event")
				if !w.isChannelFull(remove) {
					remove <- struct{}{}
				} else {
					w.log.Info("remove channel is full, discard event")
				}
			}

			if event.Op&fsnotify.Write == fsnotify.Write {
				w.log.Info("sending config change event...")
				if !w.isChannelFull(change) {
					change <- struct{}{}
				} else {
					w.log.Info("config change event channel full, discard event")
				}
			}
		}
	}()
}

func (w *FileWatcherData) isChannelFull(ch chan struct{}) bool {
	return len(ch) == cap(ch)
}
<commit_msg>[recorderd] compare file name to decide if file change should be considered<commit_after>package main

import (
	"path"
	"path/filepath"
	"time"

	"github.com/bitmark-inc/logger"
	"github.com/fsnotify/fsnotify"
)

type FileWatcher interface {
	Start()
}

const (
	FileWatcherLoggerPrefix = "file-watcher"
)

type FileWatcherData struct {
	reader      ConfigReader
	log         *logger.L
	watcherData WatcherData
	watcher     *fsnotify.Watcher
}

type WatcherData struct {
	channels         WatcherChannel
	throttleInterval time.Duration
}

type WatcherChannel struct {
	change chan struct{}
	remove chan struct{}
}

func newFileWatcher(reader ConfigReader, log *logger.L, data WatcherData) FileWatcher {
	watcher, err := fsnotify.NewWatcher()
	if nil != err {
		log.Errorf("new watcher with error: %s", err.Error())
	}
	return &FileWatcherData{
		reader:      reader,
		log:         log,
		watcher:     watcher,
		watcherData: data,
	}
}

func (w *FileWatcherData) Start() {
	_, fileName, _ := w.reader.GetConfig()
	filePath, _ := filepath.Abs(filepath.Clean(fileName))

	w.watcher.Add(filePath)

	go func() {
		for {
			event := <-w.watcher.Events
			w.log.Infof("file event: %v", event)
			remove := w.watcherData.channels.remove
			change := w.watcherData.channels.change

			if path.Base(event.Name) != path.Base(filepath.Clean(filePath)) {
				w.log.Infof("file %s not match, discard event", filePath)
				continue
			}

			if event.Op&fsnotify.Remove == fsnotify.Remove {
				w.log.Info("sending file remove event")
				if !w.isChannelFull(remove) {
					remove <- struct{}{}
				} else {
					w.log.Info("remove channel is full, discard event")
				}
			}

			if event.Op&fsnotify.Write == fsnotify.Write {
				w.log.Info("sending config change event...")
				if !w.isChannelFull(change) {
					change <- struct{}{}
				} else {
					w.log.Info("config change event channel full, discard event")
				}
			}
		}
	}()
}

func (w *FileWatcherData) isChannelFull(ch chan struct{}) bool {
	return len(ch) == cap(ch)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
)

var cmdField = &Command{
	Run:   runField,
	Usage: "field",
	Short: "Manage sobject fields",
	Long: `
Manage sobject fields

Usage:

  force field list <object>

  force field create <object> <field>:<type> [<option>:<value>]

  force field delete <object> <field>

Examples:

  force field list Todo__c

  force field create Todo__c Due:DateTime required:true

  force field delete Todo__c Due
`,
}

func runField(cmd *Command, args []string) {
	if len(args) == 0 {
		cmd.printUsage()
	} else {
		switch args[0] {
		case "list":
			runFieldList(args[1:])
		case "create", "add":
			runFieldCreate(args[1:])
		case "delete", "remove":
			runFieldDelete(args[1:])
		default:
			ErrorAndExit("no such command: %s", args[0])
		}
	}
}

func runFieldList(args []string) {
	if len(args) != 1 {
		ErrorAndExit("must specify object")
	}
	force, _ := ActiveForce()
	sobject, err := force.GetSobject(args[0])
	if err != nil {
		ErrorAndExit(err.Error())
	}
	DisplayForceSobject(sobject)
}

func runFieldCreate(args []string) {
	if len(args) < 2 {
		ErrorAndExit("must specify object and at least one field")
	}
	force, _ := ActiveForce()
	parts := strings.Split(args[1], ":")
	if len(parts) != 2 {
		ErrorAndExit("must specify name:type for fields")
	}

	var optionMap = make(map[string]string)
	var newOptions = make(map[string]string)
	if len(args) > 2 {
		for _, value := range args[2:] {
			options := strings.Split(value, ":")
			optionMap[options[0]] = options[1]
		}
	}

	// Validate the options for this field type
	xOptions, err := force.Metadata.ValidateFieldOptions(parts[1], optionMap)
	if err != nil {
		ErrorAndExit(err.Error())
	}

	newOptions = xOptions

	if err := force.Metadata.CreateCustomField(args[0], parts[0], parts[1], newOptions); err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Println("Custom field created")
}

func runFieldDelete(args []string) {
	if len(args) < 2 {
		ErrorAndExit("must specify object and at least one field")
	}
	force, _ := ActiveForce()
	if err := force.Metadata.DeleteCustomField(args[0], args[1]); err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Println("Custom field deleted")
}
<commit_msg>Refactor runFieldCreate<commit_after>package main

import (
	"fmt"
	"strings"
)

var cmdField = &Command{
	Run:   runField,
	Usage: "field",
	Short: "Manage sobject fields",
	Long: `
Manage sobject fields

Usage:

  force field list <object>

  force field create <object> <field>:<type> [<option>:<value>]

  force field delete <object> <field>

Examples:

  force field list Todo__c

  force field create Todo__c Due:DateTime required:true

  force field delete Todo__c Due
`,
}

func runField(cmd *Command, args []string) {
	if len(args) == 0 {
		cmd.printUsage()
	} else {
		switch args[0] {
		case "list":
			runFieldList(args[1:])
		case "create", "add":
			runFieldCreate(args[1:])
		case "delete", "remove":
			runFieldDelete(args[1:])
		default:
			ErrorAndExit("no such command: %s", args[0])
		}
	}
}

func runFieldList(args []string) {
	if len(args) != 1 {
		ErrorAndExit("must specify object")
	}
	force, _ := ActiveForce()
	sobject, err := force.GetSobject(args[0])
	if err != nil {
		ErrorAndExit(err.Error())
	}
	DisplayForceSobject(sobject)
}

func runFieldCreate(args []string) {
	if len(args) < 2 {
		ErrorAndExit("must specify object and at least one field")
	}
	force, _ := ActiveForce()
	parts := strings.Split(args[1], ":")
	if len(parts) != 2 {
		ErrorAndExit("must specify name:type for fields")
	}

	var optionMap = make(map[string]string)
	if len(args) > 2 {
		for _, value := range args[2:] {
			options := strings.Split(value, ":")
			optionMap[options[0]] = options[1]
		}
	}

	// Validate the options for this field type
	newOptions, err := force.Metadata.ValidateFieldOptions(parts[1], optionMap)
	if err != nil {
		ErrorAndExit(err.Error())
	}

	if err := force.Metadata.CreateCustomField(args[0], parts[0], parts[1], newOptions); err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Println("Custom field created")
}

func runFieldDelete(args []string) {
	if len(args) < 2 {
		ErrorAndExit("must specify object and at least one field")
	}
	force, _ := ActiveForce()
	if err := force.Metadata.DeleteCustomField(args[0], args[1]); err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Println("Custom field deleted")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"math/rand"
)

type Field struct {
	width  byte
	height byte
	state  [][]int
}

//Field.state == -2 or -1 or 0 ~ 8 or 10 ~ 18
//-2: opened with mine
//-1: not open with mine
//0 ~ 8: not open and the number of mine surrounding
//10 ~ 18: open and the number of mine surrounding

func NewField(width, height, mineNum byte) *Field {
	field := &Field{width, height, [][]int{}}
	field.state = make([][]int, height+2)

	var Combination [][2]byte
	Combination = make([][2]byte, width*height)
	for i := 0; i < int(height)+2; i++ {
		field.state[i] = make([]int, width+2)
	}
	for i := 0; i < int(height); i++ {
		for j := 0; j < int(width); j++ {
			Combination[i*int(height)+j][0] = byte(i + 1)
			Combination[i*int(height)+j][1] = byte(j + 1)
		}
	}

	// set mine
	var pos [][2]byte = make([][2]byte, mineNum)
	for i := 0; i < int(mineNum); i++ {
		idx := rand.Intn(int(width*height) - i)
		pos[i] = Combination[idx]
		Combination = append(Combination[:idx], Combination[idx+1:]...)
		// set surround
		field.state[pos[i][0]-1][pos[i][1]-1] += 1
		field.state[pos[i][0]-1][pos[i][1]] += 1
		field.state[pos[i][0]-1][pos[i][1]+1] += 1
		field.state[pos[i][0]][pos[i][1]-1] += 1
		field.state[pos[i][0]][pos[i][1]+1] += 1
		field.state[pos[i][0]+1][pos[i][1]-1] += 1
		field.state[pos[i][0]+1][pos[i][1]] += 1
		field.state[pos[i][0]+1][pos[i][1]+1] += 1
	}
	for i := 0; i < int(mineNum); i++ {
		// put mine
		field.state[pos[i][0]][pos[i][1]] = -1
	}

	return field
}

func (self *Field) RefreshField() {

}

func (self *Field) Choose(row, column byte) {
	row += 1
	column += 1
	if 0 <= self.state[row][column] && self.state[row][column] <= 8 {
		self.state[row][column] += 10 //open
	} else if self.state[row][column] == -1 {
		self.state[row][column] += -1
	}
}

func (self *Field) FieldString() (out string) {
	header := "  "
	for c := 0; c < int(self.width); c++ {
		header += fmt.Sprintf(" %d  ", c+1)
	}

	field := fmt.Sprintf("%s\n", header)
	for r := 1; r < int(self.height)+1; r++ {
		field += fmt.Sprintf("%d ", r)
		for c := 1; c < int(self.width)+1; c++ {
			if -1 <= self.state[r][c] && self.state[r][c] <= 8 {
				field += "[ ]"
			} else if self.state[r][c] == 10 {
				field += "___"
			} else if 10 < self.state[r][c] {
				field += fmt.Sprintf("_%d_", self.state[r][c]-10)
			} else if self.state[r][c] == -2 {
				field += "_*_"
			}
			field += " "
		}
		if r < int(self.height) {
			field += "\n"
		}
	}
	return fmt.Sprintf("%s", field)
}

func InputLoop(field *Field) {
	var input string
	var pos [][]byte
	var ZERO byte = 48
	for {
		fmt.Scanln(&input)
		in := []byte(input)
		pos = bytes.Split(in, []byte(","))
		if len(pos) != 2 {
			fmt.Println("2 values should be input")
			continue
		}
		field.Choose(pos[0][0]-ZERO-1, pos[1][0]-ZERO-1)
		fmt.Printf("\r%s", field.FieldString())
	}
}

func main() {
	field := NewField(8, 8, 6)
	fmt.Printf("\r%s", field.FieldString())
	InputLoop(field)
}
<commit_msg>implement recursive open<commit_after>package main

import (
	"bytes"
	"fmt"
	"math/rand"
)

type Field struct {
	width  byte
	height byte
	state  [][]int
}

//Field.state == -2 or -1 or 0 ~ 8 or 10 ~ 18
//-2: opened with mine
//-1: not open with mine
//0 ~ 8: not open and the number of mine surrounding
//10 ~ 18: open and the number of mine surrounding

func NewField(width, height, mineNum byte) *Field {
	field := &Field{width, height, [][]int{}}
	field.state = make([][]int, height+2)

	var Combination [][2]byte
	Combination = make([][2]byte, width*height)
	for i := 0; i < int(height)+2; i++ {
		field.state[i] = make([]int, width+2)
	}
	for i := 0; i < int(height); i++ {
		for j := 0; j < int(width); j++ {
			Combination[i*int(height)+j][0] = byte(i + 1)
			Combination[i*int(height)+j][1] = byte(j + 1)
		}
	}

	// set mine
	var pos [][2]byte = make([][2]byte, mineNum)
	for i := 0; i < int(mineNum); i++ {
		idx := rand.Intn(int(width*height) - i)
		pos[i] = Combination[idx]
		Combination = append(Combination[:idx], Combination[idx+1:]...)
		// set surround
		field.state[pos[i][0]-1][pos[i][1]-1] += 1
		field.state[pos[i][0]-1][pos[i][1]] += 1
		field.state[pos[i][0]-1][pos[i][1]+1] += 1
		field.state[pos[i][0]][pos[i][1]-1] += 1
		field.state[pos[i][0]][pos[i][1]+1] += 1
		field.state[pos[i][0]+1][pos[i][1]-1] += 1
		field.state[pos[i][0]+1][pos[i][1]] += 1
		field.state[pos[i][0]+1][pos[i][1]+1] += 1
	}
	for i := 0; i < int(mineNum); i++ {
		// put mine
		field.state[pos[i][0]][pos[i][1]] = -1
	}

	return field
}

func (self *Field) RefreshField() {

}

func (self *Field) RecursiveOpen(row, column byte) {
	self.state[row][column] += 10
	if row == 0 || row == self.height+1 || column == 0 || column == self.width+1 {
		return
	}
	if 0 <= self.state[row-1][column-1] && self.state[row-1][column-1] <= 8 {
		if self.state[row-1][column-1] == 0 {
			self.RecursiveOpen(row-1, column-1)
		} else {
			self.state[row-1][column-1] += 10
		}
	}
	if 0 <= self.state[row-1][column] && self.state[row-1][column] <= 8 {
		if self.state[row-1][column] == 0 {
			self.RecursiveOpen(row-1, column)
		} else {
			self.state[row-1][column] += 10
		}
	}
	if 0 <= self.state[row-1][column+1] && self.state[row-1][column+1] <= 8 {
		if self.state[row-1][column+1] == 0 {
			self.RecursiveOpen(row-1, column+1)
		} else {
			self.state[row-1][column+1] += 10
		}
	}
	if 0 <= self.state[row][column-1] && self.state[row][column-1] <= 8 {
		if self.state[row][column-1] == 0 {
			self.RecursiveOpen(row, column-1)
		} else {
			self.state[row][column-1] += 10
		}
	}
	if 0 <= self.state[row][column+1] && self.state[row][column+1] <= 8 {
		if self.state[row][column+1] == 0 {
			self.RecursiveOpen(row, column+1)
		} else {
			self.state[row][column+1] += 10
		}
	}
	if 0 <= self.state[row+1][column-1] && self.state[row+1][column-1] <= 8 {
		if self.state[row+1][column-1] == 0 {
			self.RecursiveOpen(row+1, column-1)
		} else {
			self.state[row+1][column-1] += 10
		}
	}
	if 0 <= self.state[row+1][column] && self.state[row+1][column] <= 8 {
		if self.state[row+1][column] == 0 {
			self.RecursiveOpen(row+1, column)
		} else {
			self.state[row+1][column] += 10
		}
	}
	if 0 <= self.state[row+1][column+1] && self.state[row+1][column+1] <= 8 {
		if self.state[row+1][column+1] == 0 {
			self.RecursiveOpen(row+1, column+1)
		} else {
			self.state[row+1][column+1] += 10
		}
	}
}

func (self *Field) Choose(row, column byte) {
	row += 1
	column += 1
	if 0 == self.state[row][column] {
		self.RecursiveOpen(row, column)
		//self.state[row][column] += 10 //open
	} else if 0 < self.state[row][column] && self.state[row][column] <= 8 {
		self.state[row][column] += 10
	} else if self.state[row][column] == -1 {
		self.state[row][column] += -1
	}
}

func (self *Field) FieldString() (out string) {
	header := "  "
	for c := 0; c < int(self.width); c++ {
		header += fmt.Sprintf(" %d  ", c+1)
	}

	field := fmt.Sprintf("%s\n", header)
	for r := 1; r < int(self.height)+1; r++ {
		field += fmt.Sprintf("%d ", r)
		for c := 1; c < int(self.width)+1; c++ {
			if -1 <= self.state[r][c] && self.state[r][c] <= 8 {
				field += "[ ]"
			} else if self.state[r][c] == 10 {
				field += "___"
			} else if 10 < self.state[r][c] {
				field += fmt.Sprintf("_%d_", self.state[r][c]-10)
			} else if self.state[r][c] == -2 {
				field += "_*_"
			}
			field += " "
		}
		if r < int(self.height) {
			field += "\n"
		}
	}
	return fmt.Sprintf("%s", field)
}

func InputLoop(field *Field) {
	var input string
	var pos [][]byte
	var ZERO byte = 48
	for {
		fmt.Scanln(&input)
		in := []byte(input)
		pos = bytes.Split(in, []byte(","))
		if len(pos) != 2 {
			fmt.Println("2 values should be input")
			continue
		}
		field.Choose(pos[0][0]-ZERO-1, pos[1][0]-ZERO-1)
		fmt.Printf("\r%s", field.FieldString())
	}
}

func main() {
	field := NewField(8, 8, 6)
	fmt.Printf("\r%s", field.FieldString())
	InputLoop(field)
}
<|endoftext|>


<commit_before>package ari

// LiveRecording describes a recording which is in progress
type LiveRecording struct {
	Cause            string `json:"cause,omitempty"`            // If failed, the cause of the failure
	Duration         int    `json:"duration,omitempty"`         // Length of recording in seconds
	Format           string `json:"format"`                     // Format of recording (wav, gsm, etc)
	Name             string `json:"name"`                       // (base) name for the recording
	Silence_duration int    `json:"silence_duration,omitempty"` // If silence was detected in the recording, the duration in seconds of that silence (requires that maxSilenceSeconds be non-zero)
	State            string `json:"state"`                      // Current state of the recording
	Talking_duration int    `json:"talking_duration,omitempty"` // Duration of talking, in seconds, that has been detected in the recording (requires that maxSilenceSeconds be non-zero)
	Target_uri       string `json:"target_uri"`                 // URI for the channel or bridge which is being recorded (TODO: figure out format for this)
}

// StoredRecording describes a past recording which may be played back (via GetStoredRecording)
type StoredRecording struct {
	Format string `json:"format"`
	Name   string `json:"name"`
}

//List all completed recordings
//Equivalent to GET /recordings/stored
func (c *Client) ListStoredRecordings() ([]StoredRecording, error) {
	var m []StoredRecording
	err := c.AriGet("/recordings/stored", &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

//Get a stored recording's details
//Equivalent to GET /recordings/stored/{recordingName}
func (c *Client) GetStoredRecording(recordingName string) (StoredRecording, error) {
	var m StoredRecording
	err := c.AriGet("/recordings/stored/"+recordingName, &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

//Copy a stored recording
//Equivalent to Post /recordings/stored/{recordingName}/copy
func (c *Client) CopyStoredRecording(recordingName string, destination string) (StoredRecording, error) {
	var m StoredRecording

	//Request structure to copy a stored recording. DestinationRecordingName is required.
	type request struct {
		DestinationRecordingName string `json:"destinationRecordingName"`
	}

	req := request{destination}

	//Make the request
	err := c.AriPost("/recordings/stored/"+recordingName+"/copy", &m, &req)
	//TODO add individual error handling

	if err != nil {
		return m, err
	}
	return m, nil
}

//Get a specific live recording
//Equivalent to GET /recordings/live/{recordingName}
func (c *Client) GetLiveRecording(recordingName string) (LiveRecording, error) {
	var m LiveRecording
	err := c.AriGet("/recordings/live/"+recordingName, &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

//Stop and store a live recording
//Equivalent to Post /recordings/live/{recordingName}/stop
func (c *Client) StopLiveRecording(recordingName string) error {
	err := c.AriPost("/recordings/live/"+recordingName+"/stop", nil, nil)
	if err != nil {
		return err
	}
	return nil
}

//Pause a live recording
//Equivalent to Post /recordings/live/{recordingName}/pause
func (c *Client) PauseLiveRecording(recordingName string) error {

	//Since no request body is required nor return object
	//we just pass two nils.

	err := c.AriPost("/recordings/live/"+recordingName+"/pause", nil, nil)
	if err != nil {
		return err
	}
	return nil
}

//Mute a live recording
//Equivalent to Post /recordings/live/{recordingName}/mute
func (c *Client) MuteLiveRecording(recordingName string) error {
	err := c.AriPost("/recordings/live/"+recordingName+"/mute", nil, nil)
	if err != nil {
		return err
	}
	return nil
}

//Delete a stored recording
//Equivalent to DELETE /recordings/stored/{recordingName}
func (c *Client) DeleteStoredRecording(recordingName string) error {
	err := c.AriDelete("/recordings/stored/"+recordingName, nil, nil)
	return err
}

//TODO reproduce this error in isolation: does not delete. Cannot delete any recording produced by this.
//Stop a live recording and discard it
//Equivalent to DELETE /recordings/live/{recordingName}
func (c *Client) ScrapLiveRecording(recordingName string) error {
	err := c.AriDelete("/recordings/live/"+recordingName, nil, nil)
	return err
}

//Unpause a live recording
//Equivalent to DELETE /recordings/live/{recordingName}/pause
func (c *Client) ResumeLiveRecording(recordingName string) error {
	err := c.AriDelete("/recordings/live/"+recordingName+"/pause", nil, nil)
	return err
}

//Unmute a live recording
//Equivalent to DELETE /recordings/live/{recordingName}/mute
func (c *Client) UnmuteLiveRecording(recordingName string) error {
	err := c.AriDelete("/recordings/live/"+recordingName+"/mute", nil, nil)
	return err
}
<commit_msg>Wrapped the Live/Stored Recordings with a client and simpler methods<commit_after>package ari

import "fmt"

// LiveRecording describes a recording which is in progress
type LiveRecording struct {
	Cause            string  `json:"cause,omitempty"`            // If failed, the cause of the failure
	Duration         int     `json:"duration,omitempty"`         // Length of recording in seconds
	Format           string  `json:"format"`                     // Format of recording (wav, gsm, etc)
	Name             string  `json:"name"`                       // (base) name for the recording
	Silence_duration int     `json:"silence_duration,omitempty"` // If silence was detected in the recording, the duration in seconds of that silence (requires that maxSilenceSeconds be non-zero)
	State            string  `json:"state"`                      // Current state of the recording
	Talking_duration int     `json:"talking_duration,omitempty"` // Duration of talking, in seconds, that has been detected in the recording (requires that maxSilenceSeconds be non-zero)
	Target_uri       string  `json:"target_uri"`                 // URI for the channel or bridge which is being recorded (TODO: figure out format for this)
	client           *Client // Reference to the client which created or returned this LiveRecording
}

// StoredRecording describes a past recording which may be played back (via GetStoredRecording)
type StoredRecording struct {
	Format string `json:"format"`
	Name   string `json:"name"`

	client *Client // Reference to the client which created or returned this StoredRecording
}

//List all completed recordings
//Equivalent to GET /recordings/stored
func (c *Client) ListStoredRecordings() ([]StoredRecording, error) {
	var m []StoredRecording
	err := c.AriGet("/recordings/stored", &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

//Get a stored recording's details
//Equivalent to GET /recordings/stored/{recordingName}
func (c *Client) GetStoredRecording(recordingName string) (StoredRecording, error) {
	var m StoredRecording
	err := c.AriGet("/recordings/stored/"+recordingName, &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

//Get a specific live recording
//Equivalent to GET /recordings/live/{recordingName}
func (c *Client) GetLiveRecording(recordingName string) (LiveRecording, error) {
	var m LiveRecording
	err := c.AriGet("/recordings/live/"+recordingName, &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

//Copy current StoredRecording
func (s *StoredRecording) Copy(recordingName string, destination string) (StoredRecording, error) {
	if s.client == nil {
		return fmt.Errorf("No client found in StoredRecording")
	}
	return s.client.StopLiveRecording(recordingName, destination)
}

// No method for getting the current LiveRecording--you have it.

//Stop and store current LiveRecording
func (l *LiveRecording) Stop(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.StopLiveRecording(recordingName)
}

//Pause current LiveRecording
func (l *LiveRecording) Pause(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.PauseLiveRecording(recordingName)
}

//Mute current LiveRecording
func (l *LiveRecording) Mute(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.MuteLiveRecording(recordingName)
}

//Delete current LiveRecording
func (l *LiveRecording) Delete(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.DeleteStoredRecording(recordingName)
}

//TODO reproduce this error in isolation: does not delete. Cannot delete any recording produced by this.
//Stop and delete current LiveRecording
func (l *LiveRecording) Scrap(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.ScrapLiveRecording(recordingName)
}

//Unpause current LiveRecording
func (l *LiveRecording) Resume(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.ResumeLiveRecording(recordingName)
}

//Unmute current LiveRecording
func (l *LiveRecording) Unmute(recordingName string) error {
	if l.client == nil {
		return fmt.Errorf("No client found in LiveRecording")
	}
	return l.client.UnmuteLiveRecording(recordingName)
}

//Copy a stored recording
//Equivalent to Post /recordings/stored/{recordingName}/copy
func (c *Client) CopyStoredRecording(recordingName string, destination string) (StoredRecording, error) {
	var m StoredRecording

	//Request structure to copy a stored recording. DestinationRecordingName is required.
	type request struct {
		DestinationRecordingName string `json:"destinationRecordingName"`
	}

	req := request{destination}

	//Make the request
	err := c.AriPost("/recordings/stored/"+recordingName+"/copy", &m, &req)
	//TODO add individual error handling

	if err != nil {
		return m, err
	}
	return m, nil
}

//Stop and store a live recording
//Equivalent to Post /recordings/live/{recordingName}/stop
func (c *Client) StopLiveRecording(recordingName string) error {
	err := c.AriPost("/recordings/live/"+recordingName+"/stop", nil, nil)
	if err != nil {
		return err
	}
	return nil
}

//Pause a live recording
//Equivalent to Post /recordings/live/{recordingName}/pause
func (c *Client) PauseLiveRecording(recordingName string) error {

	//Since no request body is required nor return object
	//we just pass two nils.

	err := c.AriPost("/recordings/live/"+recordingName+"/pause", nil, nil)
	if err != nil {
		return err
	}
	return nil
}

//Mute a live recording
//Equivalent to Post /recordings/live/{recordingName}/mute
func (c *Client) MuteLiveRecording(recordingName string) error {
	err := c.AriPost("/recordings/live/"+recordingName+"/mute", nil, nil)
	if err != nil {
		return err
	}
	return nil
}

//Delete a stored recording
//Equivalent to DELETE /recordings/stored/{recordingName}
func (c *Client) DeleteStoredRecording(recordingName string) error {
	err := c.AriDelete("/recordings/stored/"+recordingName, nil, nil)
	return err
}

//TODO reproduce this error in isolation: does not delete. Cannot delete any recording produced by this.
//Stop a live recording and discard it
//Equivalent to DELETE /recordings/live/{recordingName}
func (c *Client) ScrapLiveRecording(recordingName string) error {
	err := c.AriDelete("/recordings/live/"+recordingName, nil, nil)
	return err
}

//Unpause a live recording
//Equivalent to DELETE /recordings/live/{recordingName}/pause
func (c *Client) ResumeLiveRecording(recordingName string) error {
	err := c.AriDelete("/recordings/live/"+recordingName+"/pause", nil, nil)
	return err
}

//Unmute a live recording
//Equivalent to DELETE /recordings/live/{recordingName}/mute
func (c *Client) UnmuteLiveRecording(recordingName string) error {
	err := c.AriDelete("/recordings/live/"+recordingName+"/mute", nil, nil)
	return err
}
<|endoftext|>