<commit_before>// Package httpcache provides a cache enabled http Transport.
package httpcache

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"
)

type ByteCache interface {
	Store(key string, value []byte, timeout time.Duration) error
	Get(key string) ([]byte, error)
}

type Config interface {
	// Generates the cache key for the given http.Request. An empty string will
	// disable caching.
	Key(req *http.Request) string

	// Provides the max cache age for the given request/response pair. A zero
	// value will disable caching for the pair. The request is available via
	// res.Request.
	MaxAge(res *http.Response) time.Duration
}

// Cache enabled http.Transport.
type Transport struct {
	ByteCache ByteCache         // Cache where serialized responses will be stored.
	Config    Config            // Provides cache key & timeout logic.
	Transport http.RoundTripper // The underlying http.RoundTripper for actual requests.
}

type cacheEntry struct {
	Response *http.Response
	Body     []byte
}

// A cache enabled RoundTrip.
func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
	key := t.Config.Key(req)
	var entry cacheEntry

	// from cache
	if key != "" {
		raw, err := t.ByteCache.Get(key)
		if err != nil {
			return nil, err
		}

		if raw != nil {
			if err = json.Unmarshal(raw, &entry); err != nil {
				return nil, err
			}

			// setup fake http.Response
			res = entry.Response
			res.Body = ioutil.NopCloser(bytes.NewReader(entry.Body))
			res.Request = req
			return res, nil
		}
	}

	// real request
	res, err = t.Transport.RoundTrip(req)
	if err != nil {
		return nil, err
	}

	// fully buffer response for caching purposes
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, err
	}

	// remove properties we want to skip in serialization
	res.Body = nil
	res.Request = nil

	// serialize the cache entry
	entry.Response = res
	entry.Body = body
	raw, err := json.Marshal(&entry)
	if err != nil {
		return nil, err
	}

	// put back non serialized properties
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	res.Request = req

	// determine timeout & put it in cache
	timeout := t.Config.MaxAge(res)
	if timeout != 0 {
		if err = t.ByteCache.Store(key, raw, timeout); err != nil {
			return nil, err
		}
	}

	// reset body in case the config.Timeout logic consumed it
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	return res, nil
}

type cacheByPath time.Duration

func (c cacheByPath) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.Host + "/" + req.URL.Path
}

func (c cacheByPath) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the host + path (ignoring scheme, auth, query etc) for
// the specified duration.
func CacheByPath(timeout time.Duration) Config {
	return cacheByPath(timeout)
}

type cacheByURL time.Duration

func (c cacheByURL) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.String()
}

func (c cacheByURL) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the URL with sorted query parameters for the specified
// duration.
func CacheByURL(timeout time.Duration) Config {
	return cacheByURL(timeout)
}
<commit_msg>doc nit<commit_after>// Package httpcache provides a cache enabled http Transport.
package httpcache

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"
)

type ByteCache interface {
	Store(key string, value []byte, timeout time.Duration) error
	Get(key string) ([]byte, error)
}

type Config interface {
	// Generates the cache key for the given http.Request. An empty string will
	// disable caching.
	Key(req *http.Request) string

	// Provides the max cache age for the given request/response pair. A zero
	// value will disable caching for the pair. The request is available via
	// res.Request.
	MaxAge(res *http.Response) time.Duration
}

// Cache enabled http.Transport.
type Transport struct {
	ByteCache ByteCache         // Cache where serialized responses will be stored.
	Config    Config            // Provides cache key & timeout logic.
	Transport http.RoundTripper // The underlying http.RoundTripper for actual requests.
}

type cacheEntry struct {
	Response *http.Response
	Body     []byte
}

// A cache enabled RoundTrip.
func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
	key := t.Config.Key(req)
	var entry cacheEntry

	// from cache
	if key != "" {
		raw, err := t.ByteCache.Get(key)
		if err != nil {
			return nil, err
		}

		if raw != nil {
			if err = json.Unmarshal(raw, &entry); err != nil {
				return nil, err
			}

			// setup fake http.Response
			res = entry.Response
			res.Body = ioutil.NopCloser(bytes.NewReader(entry.Body))
			res.Request = req
			return res, nil
		}
	}

	// real request
	res, err = t.Transport.RoundTrip(req)
	if err != nil {
		return nil, err
	}

	// fully buffer response for caching purposes
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, err
	}

	// remove properties we want to skip in serialization
	res.Body = nil
	res.Request = nil

	// serialize the cache entry
	entry.Response = res
	entry.Body = body
	raw, err := json.Marshal(&entry)
	if err != nil {
		return nil, err
	}

	// put back non serialized properties
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	res.Request = req

	// determine timeout & put it in cache
	timeout := t.Config.MaxAge(res)
	if timeout != 0 {
		if err = t.ByteCache.Store(key, raw, timeout); err != nil {
			return nil, err
		}
	}

	// reset body in case the config.Timeout logic consumed it
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	return res, nil
}

type cacheByPath time.Duration

func (c cacheByPath) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.Host + "/" + req.URL.Path
}

func (c cacheByPath) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the host + path (ignoring scheme, auth, query etc) for
// the specified duration.
func CacheByPath(timeout time.Duration) Config {
	return cacheByPath(timeout)
}

type cacheByURL time.Duration

func (c cacheByURL) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.String()
}

func (c cacheByURL) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the entire URL for the specified duration.
func CacheByURL(timeout time.Duration) Config {
	return cacheByURL(timeout)
}
<|endoftext|>


<commit_before>// Package httpcache provides a cache enabled http Transport.
package httpcache

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"
)

type ByteCache interface {
	Store(key string, value []byte, timeout time.Duration) error
	Get(key string) ([]byte, error)
}

type Config interface {
	// Generates the cache key for the given http.Request. An empty string will
	// disable caching.
	Key(req *http.Request) string

	// Provides the max cache age for the given request/response pair. A zero
	// value will disable caching for the pair. The request is available via
	// res.Request.
	MaxAge(res *http.Response) time.Duration
}

// Cache enabled http.Transport.
type Transport struct {
	ByteCache ByteCache         // Cache where serialized responses will be stored.
	Config    Config            // Provides cache key & timeout logic.
	Transport http.RoundTripper // The underlying http.RoundTripper for actual requests.
}

type cacheEntry struct {
	Response *http.Response
	Body     []byte
}

// A cache enabled RoundTrip.
func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
	key := t.Config.Key(req)
	var entry cacheEntry

	// from cache
	if key != "" {
		raw, err := t.ByteCache.Get(key)
		if err != nil {
			return nil, err
		}

		if raw != nil {
			if err = json.Unmarshal(raw, &entry); err != nil {
				return nil, err
			}

			// setup fake http.Response
			res = entry.Response
			res.Body = ioutil.NopCloser(bytes.NewReader(entry.Body))
			res.Request = req
			return res, nil
		}
	}

	// real request
	res, err = t.Transport.RoundTrip(req)
	if err != nil {
		return nil, err
	}

	// fully buffer response for caching purposes
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, err
	}

	// remove properties we want to skip in serialization
	res.Body = nil
	res.Request = nil

	// serialize the cache entry
	entry.Response = res
	entry.Body = body
	raw, err := json.Marshal(&entry)
	if err != nil {
		return nil, err
	}

	// put back non serialized properties
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	res.Request = req

	// determine timeout & put it in cache
	timeout := t.Config.MaxAge(res)
	if timeout != 0 {
		if err = t.ByteCache.Store(key, raw, timeout); err != nil {
			return nil, err
		}
	}

	// reset body in case the config.Timeout logic consumed it
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	return res, nil
}

type cacheByPath time.Duration

func (c cacheByPath) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.Host + "/" + req.URL.Path
}

func (c cacheByPath) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the host + path (ignoring scheme, auth, query etc) for
// the specified duration.
func CacheByPath(timeout time.Duration) Config {
	return cacheByPath(timeout)
}

type cacheByURL time.Duration

func (c cacheByURL) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.String()
}

func (c cacheByURL) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the entire URL for the specified duration.
func CacheByURL(timeout time.Duration) Config {
	return cacheByURL(timeout)
}
<commit_msg>skip caching logic if key is empty<commit_after>// Package httpcache provides a cache enabled http Transport.
package httpcache

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"
)

type ByteCache interface {
	Store(key string, value []byte, timeout time.Duration) error
	Get(key string) ([]byte, error)
}

type Config interface {
	// Generates the cache key for the given http.Request. An empty string will
	// disable caching.
	Key(req *http.Request) string

	// Provides the max cache age for the given request/response pair. A zero
	// value will disable caching for the pair. The request is available via
	// res.Request.
	MaxAge(res *http.Response) time.Duration
}

// Cache enabled http.Transport.
type Transport struct {
	ByteCache ByteCache         // Cache where serialized responses will be stored.
	Config    Config            // Provides cache key & timeout logic.
	Transport http.RoundTripper // The underlying http.RoundTripper for actual requests.
}

type cacheEntry struct {
	Response *http.Response
	Body     []byte
}

// A cache enabled RoundTrip.
func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
	key := t.Config.Key(req)
	var entry cacheEntry

	// from cache
	if key != "" {
		raw, err := t.ByteCache.Get(key)
		if err != nil {
			return nil, err
		}

		if raw != nil {
			if err = json.Unmarshal(raw, &entry); err != nil {
				return nil, err
			}

			// setup fake http.Response
			res = entry.Response
			res.Body = ioutil.NopCloser(bytes.NewReader(entry.Body))
			res.Request = req
			return res, nil
		}
	}

	// real request
	res, err = t.Transport.RoundTrip(req)
	if err != nil {
		return nil, err
	}

	// no caching required
	if key == "" {
		return res, nil
	}

	// fully buffer response for caching purposes
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, err
	}

	// remove properties we want to skip in serialization
	res.Body = nil
	res.Request = nil

	// serialize the cache entry
	entry.Response = res
	entry.Body = body
	raw, err := json.Marshal(&entry)
	if err != nil {
		return nil, err
	}

	// put back non serialized properties
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	res.Request = req

	// determine timeout & put it in cache
	timeout := t.Config.MaxAge(res)
	if timeout != 0 {
		if err = t.ByteCache.Store(key, raw, timeout); err != nil {
			return nil, err
		}
	}

	// reset body in case the config.Timeout logic consumed it
	res.Body = ioutil.NopCloser(bytes.NewReader(body))
	return res, nil
}

type cacheByPath time.Duration

func (c cacheByPath) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.Host + "/" + req.URL.Path
}

func (c cacheByPath) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the host + path (ignoring scheme, auth, query etc) for
// the specified duration.
func CacheByPath(timeout time.Duration) Config {
	return cacheByPath(timeout)
}

type cacheByURL time.Duration

func (c cacheByURL) Key(req *http.Request) string {
	if req.Method != "GET" && req.Method != "HEAD" {
		return ""
	}
	return req.URL.String()
}

func (c cacheByURL) MaxAge(res *http.Response) time.Duration {
	return time.Duration(c)
}

// This caches against the entire URL for the specified duration.
func CacheByURL(timeout time.Duration) Config {
	return cacheByURL(timeout)
}
<|endoftext|>


<commit_before>package view

import (
	"math/rand"
	"os"
	"testing"

	"fmt"

	"github.com/nictuku/stardew-rocks/parser"
)

func TestTileCoordinate(t *testing.T) {
	x0, y0 := tileCoordinates(117, 16, 16, 400)
	if x0 != 272 {
		t.Errorf("wanted x0 %v, got %v", 272, x0)
	}
	if y0 != 64 {
		t.Errorf("wanted y0 %v, got %v", 64, y0)
	}
}
func TestLoadTile(t *testing.T) {

	// Ensure a known test seed for reproducible test images.
	rand.Seed(0)

	farm := parser.LoadFarmMap()

	for _, name := range []string{
		"Aerlia_1458278945",
		"Dristan_1458278710",
		"MsJake_116822164",
		"Jack_1458408909",
	} {

		sg, err := os.Open("../assets/saves/" + name)
		if err != nil {
			t.Fatal(err)
		}

		gameSave, err := parser.ParseSaveGame(sg)
		if err != nil {
			t.Fatal(err)
		}
		mf := fmt.Sprintf("map-%v.png", name)
		f, err := os.OpenFile(mf, os.O_CREATE|os.O_WRONLY, 0666)
		if err != nil {
			panic(err)
		}
		WriteImage(farm, gameSave, f)
		f.Close()
		t.Log("Wrote map", mf)
	}
}
<commit_msg>Log which image we're testing<commit_after>package view

import (
	"log"
	"math/rand"
	"os"
	"testing"

	"fmt"

	"github.com/nictuku/stardew-rocks/parser"
)

func TestTileCoordinate(t *testing.T) {
	x0, y0 := tileCoordinates(117, 16, 16, 400)
	if x0 != 272 {
		t.Errorf("wanted x0 %v, got %v", 272, x0)
	}
	if y0 != 64 {
		t.Errorf("wanted y0 %v, got %v", 64, y0)
	}
}
func TestLoadTile(t *testing.T) {

	// Ensure a known test seed for reproducible test images.
	rand.Seed(0)

	farm := parser.LoadFarmMap()

	for _, name := range []string{
		"Aerlia_1458278945",
		"Dristan_1458278710",
		"MsJake_116822164",
		"Jack_1458408909",
	} {

		sg, err := os.Open("../assets/saves/" + name)
		if err != nil {
			t.Fatal(err)
		}

		gameSave, err := parser.ParseSaveGame(sg)
		if err != nil {
			t.Fatal(err)
		}
		mf := fmt.Sprintf("map-%v.png", name)
		f, err := os.OpenFile(mf, os.O_CREATE|os.O_WRONLY, 0666)
		if err != nil {
			panic(err)
		}
		log.Printf("Writing screenshot at %v", mf)
		WriteImage(farm, gameSave, f)
		f.Close()
		t.Log("Wrote map", mf)
	}
}
<|endoftext|>


<commit_before>package hamming

import (
	"testing"
)

var testCases = []struct {
	expected         int
	strandA, strandB string
	description      string
}{
	{0, "", "", "no difference between empty strands"},
	{2, "AG", "CT", "complete hamming distance for small strands"},
	{0, "A", "A", "no difference between identical strands"},
	{1, "A", "G", "complete distance for single nucleotide strands"},
	{1, "AT", "CT", "small hamming distance"},
	{1, "GGACG", "GGTCG", "small hamming distance in longer strands"},
	{1, "AATG", "AAA", "ignores extra length on first strand when longer"},
	{2, "ATA", "AGTG", "ignores extra length on second strand when longer"},
	{4, "GATACA", "GCATAA", "large hamming distance"},
	{9, "GGACGGATTCTG", "AGGACGGATTCT", "hamming distance in very long strands"},
}

func TestHamming(t *testing.T) {
	for _, tc := range testCases {

		observed := Distance(tc.strandA, tc.strandB)

		if tc.expected != observed {
			t.Fatalf(`%s:
{%v,%v}
expected: %v
observed: %v`,
				tc.description,
				tc.strandA,
				tc.strandB,
				tc.expected,
				observed,
			)
		}
	}
}
<commit_msg>Added benchmark in go/hamming<commit_after>package hamming

import (
	"testing"
)

var testCases = []struct {
	expected         int
	strandA, strandB string
	description      string
}{
	{0, "", "", "no difference between empty strands"},
	{2, "AG", "CT", "complete hamming distance for small strands"},
	{0, "A", "A", "no difference between identical strands"},
	{1, "A", "G", "complete distance for single nucleotide strands"},
	{1, "AT", "CT", "small hamming distance"},
	{1, "GGACG", "GGTCG", "small hamming distance in longer strands"},
	{1, "AATG", "AAA", "ignores extra length on first strand when longer"},
	{2, "ATA", "AGTG", "ignores extra length on second strand when longer"},
	{4, "GATACA", "GCATAA", "large hamming distance"},
	{9, "GGACGGATTCTG", "AGGACGGATTCT", "hamming distance in very long strands"},
}

func TestHamming(t *testing.T) {
	for _, tc := range testCases {

		observed := Distance(tc.strandA, tc.strandB)

		if tc.expected != observed {
			t.Fatalf(`%s:
{%v,%v}
expected: %v
observed: %v`,
				tc.description,
				tc.strandA,
				tc.strandB,
				tc.expected,
				observed,
			)
		}
	}
}

func BenchmarkHamming(b *testing.B) {
	b.StopTimer()

	for _, tc := range testCases {
		b.StartTimer()

		for i := 0; i < b.N; i++ {
			Distance(tc.strandA, tc.strandB)
		}

		b.StopTimer()
	}
}
<|endoftext|>


<commit_before>package bitbucketserver

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/mitchellh/mapstructure"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

type statusData struct {
	key         string
	buildNumber int64
	status      string
	url         string
	hash        string
	description string
}

func (b *bitbucketClient) SetStatus(ctx context.Context, event sdk.Event) error {
	if b.consumer.disableStatus {
		log.Warning(ctx, "bitbucketClient.SetStatus>  ⚠ Bitbucket statuses are disabled")
		return nil
	}

	var statusData statusData
	var err error
	switch event.EventType {
	case fmt.Sprintf("%T", sdk.EventRunWorkflowNode{}):
		statusData, err = processWorkflowNodeRunEvent(event, b.consumer.uiURL)
	default:
		return nil
	}

	if err != nil {
		return sdk.WrapError(err, "bitbucketClient.SetStatus: Cannot process Event")
	}

	state := getBitbucketStateFromStatus(statusData.status)
	status := Status{
		Key:         statusData.key,
		Name:        fmt.Sprintf("%s%d", statusData.key, statusData.buildNumber),
		State:       state,
		URL:         statusData.url,
		Description: statusData.description,
	}

	values, err := json.Marshal(status)
	if err != nil {
		return sdk.WrapError(err, "Unable to marshall status")
	}

	if err := b.do(ctx, "POST", "build-status", fmt.Sprintf("/commits/%s", statusData.hash), nil, values, nil, nil); err != nil {
		return sdk.WrapError(err, "Unable to post build-status name:%s status:%s", status.Name, state)
	}
	return nil
}

func (b *bitbucketClient) ListStatuses(ctx context.Context, repo string, ref string) ([]sdk.VCSCommitStatus, error) {
	ss := []Status{}

	path := fmt.Sprintf("/commits/%s", ref)
	params := url.Values{}
	nextPage := 0
	for {
		if nextPage != 0 {
			params.Set("start", fmt.Sprintf("%d", nextPage))
		}

		var response ResponseStatus
		if err := b.do(ctx, "GET", "build-status", path, nil, nil, &response, nil); err != nil {
			return nil, sdk.WrapError(err, "Unable to get statuses")
		}

		ss = append(ss, response.Values...)

		if response.IsLastPage {
			break
		} else {
			nextPage = response.NextPageStart
		}
	}

	vcsStatuses := []sdk.VCSCommitStatus{}
	for _, s := range ss {
		if !strings.HasPrefix(s.Description, "CDS/") {
			continue
		}
		vcsStatuses = append(vcsStatuses, sdk.VCSCommitStatus{
			CreatedAt:  time.Unix(s.Timestamp/1000, 0),
			Decription: s.Description,
			Ref:        ref,
			State:      processBitbucketState(s),
		})
	}

	return vcsStatuses, nil
}

func processBitbucketState(s Status) string {
	switch s.State {
	case successful:
		return sdk.StatusSuccess
	case failed:
		return sdk.StatusFail
	default:
		return sdk.StatusDisabled
	}
}

const (
	// "state": "<INPROGRESS|SUCCESSFUL|FAILED>"
	// doc from https://developer.atlassian.com/server/bitbucket/how-tos/updating-build-status-for-commits/
	inProgress = "INPROGRESS"
	successful = "SUCCESSFUL"
	failed     = "FAILED"
)

func processWorkflowNodeRunEvent(event sdk.Event, uiURL string) (statusData, error) {
	data := statusData{}
	var eventNR sdk.EventRunWorkflowNode
	if err := mapstructure.Decode(event.Payload, &eventNR); err != nil {
		return data, sdk.WrapError(err, "Error during consumption")
	}
	data.key = fmt.Sprintf("%s-%s-%s",
		event.ProjectKey,
		event.WorkflowName,
		eventNR.NodeName,
	)
	data.url = fmt.Sprintf("%s/project/%s/workflow/%s/run/%d",
		uiURL,
		event.ProjectKey,
		event.WorkflowName,
		eventNR.Number,
	)
	data.buildNumber = eventNR.Number
	data.status = eventNR.Status
	data.hash = eventNR.Hash
	data.description = sdk.VCSCommitStatusDescription(event.ProjectKey, event.WorkflowName, eventNR)

	return data, nil
}

func getBitbucketStateFromStatus(status string) string {
	switch status {
	case sdk.StatusSuccess, sdk.StatusSkipped:
		return successful
	case sdk.StatusWaiting:
		return inProgress
	case sdk.StatusDisabled:
		return inProgress
	case sdk.StatusFail:
		return failed
	default:
		return failed
	}
}
<commit_msg>fix(vcs): status inprogress for building status<commit_after>package bitbucketserver

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/mitchellh/mapstructure"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

type statusData struct {
	key         string
	buildNumber int64
	status      string
	url         string
	hash        string
	description string
}

func (b *bitbucketClient) SetStatus(ctx context.Context, event sdk.Event) error {
	if b.consumer.disableStatus {
		log.Warning(ctx, "bitbucketClient.SetStatus>  ⚠ Bitbucket statuses are disabled")
		return nil
	}

	var statusData statusData
	var err error
	switch event.EventType {
	case fmt.Sprintf("%T", sdk.EventRunWorkflowNode{}):
		statusData, err = processWorkflowNodeRunEvent(event, b.consumer.uiURL)
	default:
		return nil
	}

	if err != nil {
		return sdk.WrapError(err, "bitbucketClient.SetStatus: Cannot process Event")
	}

	state := getBitbucketStateFromStatus(statusData.status)
	status := Status{
		Key:         statusData.key,
		Name:        fmt.Sprintf("%s%d", statusData.key, statusData.buildNumber),
		State:       state,
		URL:         statusData.url,
		Description: statusData.description,
	}

	values, err := json.Marshal(status)
	if err != nil {
		return sdk.WrapError(err, "Unable to marshall status")
	}

	if err := b.do(ctx, "POST", "build-status", fmt.Sprintf("/commits/%s", statusData.hash), nil, values, nil, nil); err != nil {
		return sdk.WrapError(err, "Unable to post build-status name:%s status:%s", status.Name, state)
	}
	return nil
}

func (b *bitbucketClient) ListStatuses(ctx context.Context, repo string, ref string) ([]sdk.VCSCommitStatus, error) {
	ss := []Status{}

	path := fmt.Sprintf("/commits/%s", ref)
	params := url.Values{}
	nextPage := 0
	for {
		if nextPage != 0 {
			params.Set("start", fmt.Sprintf("%d", nextPage))
		}

		var response ResponseStatus
		if err := b.do(ctx, "GET", "build-status", path, nil, nil, &response, nil); err != nil {
			return nil, sdk.WrapError(err, "Unable to get statuses")
		}

		ss = append(ss, response.Values...)

		if response.IsLastPage {
			break
		} else {
			nextPage = response.NextPageStart
		}
	}

	vcsStatuses := []sdk.VCSCommitStatus{}
	for _, s := range ss {
		if !strings.HasPrefix(s.Description, "CDS/") {
			continue
		}
		vcsStatuses = append(vcsStatuses, sdk.VCSCommitStatus{
			CreatedAt:  time.Unix(s.Timestamp/1000, 0),
			Decription: s.Description,
			Ref:        ref,
			State:      processBitbucketState(s),
		})
	}

	return vcsStatuses, nil
}

func processBitbucketState(s Status) string {
	switch s.State {
	case successful:
		return sdk.StatusSuccess
	case failed:
		return sdk.StatusFail
	default:
		return sdk.StatusDisabled
	}
}

const (
	// "state": "<INPROGRESS|SUCCESSFUL|FAILED>"
	// doc from https://developer.atlassian.com/server/bitbucket/how-tos/updating-build-status-for-commits/
	inProgress = "INPROGRESS"
	successful = "SUCCESSFUL"
	failed     = "FAILED"
)

func processWorkflowNodeRunEvent(event sdk.Event, uiURL string) (statusData, error) {
	data := statusData{}
	var eventNR sdk.EventRunWorkflowNode
	if err := mapstructure.Decode(event.Payload, &eventNR); err != nil {
		return data, sdk.WrapError(err, "Error during consumption")
	}
	data.key = fmt.Sprintf("%s-%s-%s",
		event.ProjectKey,
		event.WorkflowName,
		eventNR.NodeName,
	)
	data.url = fmt.Sprintf("%s/project/%s/workflow/%s/run/%d",
		uiURL,
		event.ProjectKey,
		event.WorkflowName,
		eventNR.Number,
	)
	data.buildNumber = eventNR.Number
	data.status = eventNR.Status
	data.hash = eventNR.Hash
	data.description = sdk.VCSCommitStatusDescription(event.ProjectKey, event.WorkflowName, eventNR)

	return data, nil
}

func getBitbucketStateFromStatus(status string) string {
	switch status {
	case sdk.StatusSuccess, sdk.StatusSkipped, sdk.StatusDisabled:
		return successful
	case sdk.StatusWaiting, sdk.StatusBuilding:
		return inProgress
	case sdk.StatusFail:
		return failed
	default:
		return failed
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/golang/protobuf/ptypes"

	"github.com/ovh/cds/engine/api/grpc"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

// takeWorkflowJob try to take a job.
// If Take is not possible (as Job already booked for example)
// it will return true (-> can work on another job), false, otherwise
func (w *currentWorker) takeWorkflowJob(ctx context.Context, job sdk.WorkflowNodeJobRun) (bool, error) {
	info, err := w.client.QueueTakeJob(job, w.bookedWJobID == job.ID)
	if err != nil {
		return true, sdk.WrapError(err, "takeWorkflowJob> Unable to take workflow node run job. This worker can work on another job.")
	}
	t := ""
	if w.bookedWJobID == job.ID {
		t = ", this was my booked job"
	}
	log.Info("takeWorkflowJob> Job %d taken%s", job.ID, t)

	w.nbActionsDone++
	// Set build variables
	w.currentJob.wJob = &info.NodeJobRun
	// Reset build variables
	w.currentJob.gitsshPath = ""
	w.currentJob.pkey = ""
	w.currentJob.buildVariables = nil

	start := time.Now()

	//This goroutine try to get the pipeline build job every 5 seconds, if it fails, it cancel the build.
	ctx, cancel := context.WithCancel(ctx)
	tick := time.NewTicker(5 * time.Second)
	go func(cancel context.CancelFunc, jobID int64, tick *time.Ticker) {
		for {
			select {
			case <-ctx.Done():
				return
			case _, ok := <-tick.C:
				if !ok {
					return
				}
				b, code, err := sdk.Request("GET", fmt.Sprintf("/queue/workflows/%d/infos", jobID), nil)
				if err != nil {
					if code == http.StatusNotFound {
						log.Info("takeWorkflowJob> Unable to load workflow job - Not Found (Request) %d: %v", jobID, err)
						cancel()
						return
					}
					log.Error("takeWorkflowJob> Unable to load workflow job (Request) %d: %v", jobID, err)
					continue // do not kill the worker here, could be a timeout
				}

				j := &sdk.WorkflowNodeJobRun{}
				if err := json.Unmarshal(b, j); err != nil {
					log.Error("takeWorkflowJob> Unable to load workflow job (Unmarshal) %d: %v", jobID, err)
					continue // do not kill the worker here
				}
				if j.Status != sdk.StatusBuilding.String() {
					log.Info("takeWorkflowJob> The job is not more in Building Status. Current Status: %s - Cancelling context", j.Status, err)
					cancel()
					return
				}

			}
		}
	}(cancel, job.ID, tick)

	// Reset build variables
	w.currentJob.buildVariables = nil
	//Run !
	res := w.processJob(ctx, info)
	tick.Stop()

	now, _ := ptypes.TimestampProto(time.Now())
	res.RemoteTime = now
	res.Duration = sdk.Round(time.Since(start), time.Second).String()

	//Wait until the logchannel is empty
	w.drainLogsAndCloseLogger(ctx)
	res.BuildID = job.ID
	// Try to send result through grpc
	if w.grpc.conn != nil {
		client := grpc.NewWorkflowQueueClient(w.grpc.conn)
		_, err := client.SendResult(ctx, &res)
		if err == nil {
			return false, nil
		}
		log.Error("Unable to send result through grpc: %v", err)
	}

	var lasterr error
	for try := 1; try <= 10; try++ {
		log.Info("takeWorkflowJob> Sending build result...")
		lasterr = w.client.QueueSendResult(job.ID, res)
		if lasterr == nil {
			log.Info("takeWorkflowJob> Send build result OK")
			return false, nil
		}
		log.Warning("takeWorkflowJob> Cannot send build result: HTTP %v - try: %d - new try in 5s", lasterr, try)
		time.Sleep(5 * time.Second)
	}
	log.Error("takeWorkflowJob> Could not send built result 10 times, giving up. job: %d", job.ID)
	return false, lasterr
}
<commit_msg>fix (worker): retry after 15s (#2598)<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/golang/protobuf/ptypes"

	"github.com/ovh/cds/engine/api/grpc"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

// takeWorkflowJob try to take a job.
// If Take is not possible (as Job already booked for example)
// it will return true (-> can work on another job), false, otherwise
func (w *currentWorker) takeWorkflowJob(ctx context.Context, job sdk.WorkflowNodeJobRun) (bool, error) {
	info, err := w.client.QueueTakeJob(job, w.bookedWJobID == job.ID)
	if err != nil {
		return true, sdk.WrapError(err, "takeWorkflowJob> Unable to take workflow node run job. This worker can work on another job.")
	}
	t := ""
	if w.bookedWJobID == job.ID {
		t = ", this was my booked job"
	}
	log.Info("takeWorkflowJob> Job %d taken%s", job.ID, t)

	w.nbActionsDone++
	// Set build variables
	w.currentJob.wJob = &info.NodeJobRun
	// Reset build variables
	w.currentJob.gitsshPath = ""
	w.currentJob.pkey = ""
	w.currentJob.buildVariables = nil

	start := time.Now()

	//This goroutine try to get the pipeline build job every 5 seconds, if it fails, it cancel the build.
	ctx, cancel := context.WithCancel(ctx)
	tick := time.NewTicker(5 * time.Second)
	go func(cancel context.CancelFunc, jobID int64, tick *time.Ticker) {
		for {
			select {
			case <-ctx.Done():
				return
			case _, ok := <-tick.C:
				if !ok {
					return
				}
				b, code, err := sdk.Request("GET", fmt.Sprintf("/queue/workflows/%d/infos", jobID), nil)
				if err != nil {
					if code == http.StatusNotFound {
						log.Info("takeWorkflowJob> Unable to load workflow job - Not Found (Request) %d: %v", jobID, err)
						cancel()
						return
					}
					log.Error("takeWorkflowJob> Unable to load workflow job (Request) %d: %v", jobID, err)
					continue // do not kill the worker here, could be a timeout
				}

				j := &sdk.WorkflowNodeJobRun{}
				if err := json.Unmarshal(b, j); err != nil {
					log.Error("takeWorkflowJob> Unable to load workflow job (Unmarshal) %d: %v", jobID, err)
					continue // do not kill the worker here
				}
				if j.Status != sdk.StatusBuilding.String() {
					log.Info("takeWorkflowJob> The job is not more in Building Status. Current Status: %s - Cancelling context", j.Status, err)
					cancel()
					return
				}

			}
		}
	}(cancel, job.ID, tick)

	// Reset build variables
	w.currentJob.buildVariables = nil
	//Run !
	res := w.processJob(ctx, info)
	tick.Stop()

	now, _ := ptypes.TimestampProto(time.Now())
	res.RemoteTime = now
	res.Duration = sdk.Round(time.Since(start), time.Second).String()

	//Wait until the logchannel is empty
	w.drainLogsAndCloseLogger(ctx)
	res.BuildID = job.ID
	// Try to send result through grpc
	if w.grpc.conn != nil {
		client := grpc.NewWorkflowQueueClient(w.grpc.conn)
		_, err := client.SendResult(ctx, &res)
		if err == nil {
			return false, nil
		}
		log.Error("Unable to send result through grpc: %v", err)
	}

	var lasterr error
	for try := 1; try <= 10; try++ {
		log.Info("takeWorkflowJob> Sending build result...")
		lasterr = w.client.QueueSendResult(job.ID, res)
		if lasterr == nil {
			log.Info("takeWorkflowJob> Send build result OK")
			return false, nil
		}
		log.Warning("takeWorkflowJob> Cannot send build result: HTTP %v - try: %d - new try in 15s", lasterr, try)
		time.Sleep(15 * time.Second)
	}
	log.Error("takeWorkflowJob> Could not send built result 10 times, giving up. job: %d", job.ID)
	return false, lasterr
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"net/http"

	"github.com/Azure/azure-sdk-for-go/arm/dns"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmDnsTxtRecord() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmDnsTxtRecordCreateOrUpdate,
		Read:   resourceArmDnsTxtRecordRead,
		Update: resourceArmDnsTxtRecordCreateOrUpdate,
		Delete: resourceArmDnsTxtRecordDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone_name": {
				Type:     schema.TypeString,
				Required: true,
			},

			"record": {
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"value": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},

			"ttl": {
				Type:     schema.TypeInt,
				Required: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmDnsTxtRecordCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).dnsClient

	name := d.Get("name").(string)
	resGroup := d.Get("resource_group_name").(string)
	zoneName := d.Get("zone_name").(string)
	ttl := int64(d.Get("ttl").(int))
	tags := d.Get("tags").(map[string]interface{})

	records, err := expandAzureRmDnsTxtRecords(d)
	if err != nil {
		return err
	}

	parameters := dns.RecordSet{
		Name: &name,
		RecordSetProperties: &dns.RecordSetProperties{
			Metadata:   expandTags(tags),
			TTL:        &ttl,
			TxtRecords: &records,
		},
	}

	eTag := ""
	ifNoneMatch := "" // set to empty to allow updates to records after creation
	resp, err := client.CreateOrUpdate(resGroup, zoneName, name, dns.TXT, parameters, eTag, ifNoneMatch)
	if err != nil {
		return err
	}

	if resp.ID == nil {
		return fmt.Errorf("Cannot read DNS TXT Record %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*resp.ID)

	return resourceArmDnsTxtRecordRead(d, meta)
}

func resourceArmDnsTxtRecordRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).dnsClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	resGroup := id.ResourceGroup
	name := id.Path["TXT"]
	zoneName := id.Path["dnszones"]

	resp, err := client.Get(resGroup, zoneName, name, dns.TXT)
	if err != nil {
		return fmt.Errorf("Error reading DNS TXT record %s: %+v", name, err)
	}
	if resp.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	}

	d.Set("name", name)
	d.Set("resource_group_name", resGroup)
	d.Set("zone_name", zoneName)
	d.Set("ttl", resp.TTL)

	if err := d.Set("record", flattenAzureRmDnsTxtRecords(resp.TxtRecords)); err != nil {
		return err
	}
	flattenAndSetTags(d, resp.Metadata)

	return nil
}

func resourceArmDnsTxtRecordDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).dnsClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	resGroup := id.ResourceGroup
	name := id.Path["TXT"]
	zoneName := id.Path["dnszones"]

	resp, error := client.Delete(resGroup, zoneName, name, dns.TXT, "")
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Error deleting DNS TXT Record %s: %+v", name, error)
	}

	return nil
}

func flattenAzureRmDnsTxtRecords(records *[]dns.TxtRecord) []map[string]interface{} {
	results := make([]map[string]interface{}, 0, len(*records))

	if records != nil {
		for _, record := range *records {
			txtRecord := make(map[string]interface{})

			if v := record.Value; v != nil {
				value := (*v)[0]
				txtRecord["value"] = value
			}

			results = append(results, txtRecord)
		}
	}

	return results
}

func expandAzureRmDnsTxtRecords(d *schema.ResourceData) ([]dns.TxtRecord, error) {
	recordStrings := d.Get("record").(*schema.Set).List()
	records := make([]dns.TxtRecord, len(recordStrings))

	for i, v := range recordStrings {
		record := v.(map[string]interface{})
		value := []string { record["value"].(string) }

		txtRecord := dns.TxtRecord{
			Value: &value,
		}

		records[i] = txtRecord
	}

	return records, nil
}
<commit_msg>linting<commit_after>package azurerm

import (
	"fmt"
	"net/http"

	"github.com/Azure/azure-sdk-for-go/arm/dns"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmDnsTxtRecord() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmDnsTxtRecordCreateOrUpdate,
		Read:   resourceArmDnsTxtRecordRead,
		Update: resourceArmDnsTxtRecordCreateOrUpdate,
		Delete: resourceArmDnsTxtRecordDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone_name": {
				Type:     schema.TypeString,
				Required: true,
			},

			"record": {
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"value": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},

			"ttl": {
				Type:     schema.TypeInt,
				Required: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmDnsTxtRecordCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).dnsClient

	name := d.Get("name").(string)
	resGroup := d.Get("resource_group_name").(string)
	zoneName := d.Get("zone_name").(string)
	ttl := int64(d.Get("ttl").(int))
	tags := d.Get("tags").(map[string]interface{})

	records, err := expandAzureRmDnsTxtRecords(d)
	if err != nil {
		return err
	}

	parameters := dns.RecordSet{
		Name: &name,
		RecordSetProperties: &dns.RecordSetProperties{
			Metadata:   expandTags(tags),
			TTL:        &ttl,
			TxtRecords: &records,
		},
	}

	eTag := ""
	ifNoneMatch := "" // set to empty to allow updates to records after creation
	resp, err := client.CreateOrUpdate(resGroup, zoneName, name, dns.TXT, parameters, eTag, ifNoneMatch)
	if err != nil {
		return err
	}

	if resp.ID == nil {
		return fmt.Errorf("Cannot read DNS TXT Record %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*resp.ID)

	return resourceArmDnsTxtRecordRead(d, meta)
}

func resourceArmDnsTxtRecordRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).dnsClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	resGroup := id.ResourceGroup
	name := id.Path["TXT"]
	zoneName := id.Path["dnszones"]

	resp, err := client.Get(resGroup, zoneName, name, dns.TXT)
	if err != nil {
		return fmt.Errorf("Error reading DNS TXT record %s: %+v", name, err)
	}
	if resp.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	}

	d.Set("name", name)
	d.Set("resource_group_name", resGroup)
	d.Set("zone_name", zoneName)
	d.Set("ttl", resp.TTL)

	if err := d.Set("record", flattenAzureRmDnsTxtRecords(resp.TxtRecords)); err != nil {
		return err
	}
	flattenAndSetTags(d, resp.Metadata)

	return nil
}

func resourceArmDnsTxtRecordDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).dnsClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	resGroup := id.ResourceGroup
	name := id.Path["TXT"]
	zoneName := id.Path["dnszones"]

	resp, error := client.Delete(resGroup, zoneName, name, dns.TXT, "")
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Error deleting DNS TXT Record %s: %+v", name, error)
	}

	return nil
}

func flattenAzureRmDnsTxtRecords(records *[]dns.TxtRecord) []map[string]interface{} {
	results := make([]map[string]interface{}, 0, len(*records))

	if records != nil {
		for _, record := range *records {
			txtRecord := make(map[string]interface{})

			if v := record.Value; v != nil {
				value := (*v)[0]
				txtRecord["value"] = value
			}

			results = append(results, txtRecord)
		}
	}

	return results
}

func expandAzureRmDnsTxtRecords(d *schema.ResourceData) ([]dns.TxtRecord, error) {
	recordStrings := d.Get("record").(*schema.Set).List()
	records := make([]dns.TxtRecord, len(recordStrings))

	for i, v := range recordStrings {
		record := v.(map[string]interface{})
		value := []string{record["value"].(string)}

		txtRecord := dns.TxtRecord{
			Value: &value,
		}

		records[i] = txtRecord
	}

	return records, nil
}
<|endoftext|>


<commit_before>package jsoniter

import "encoding/json"

type Number string

func CastJsonNumber(val interface{}) (string, bool) {
	switch typedVal := val.(type) {
	case json.Number:
		return string(typedVal), true
	case Number:
		return string(typedVal), true
	}
	return "", false
}
<commit_msg>fix #180, add missing methods to jsoniter.Number<commit_after>package jsoniter

import (
	"encoding/json"
	"strconv"
)

type Number string

// String returns the literal text of the number.
func (n Number) String() string { return string(n) }

// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
	return strconv.ParseFloat(string(n), 64)
}

// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
	return strconv.ParseInt(string(n), 10, 64)
}

func CastJsonNumber(val interface{}) (string, bool) {
	switch typedVal := val.(type) {
	case json.Number:
		return string(typedVal), true
	case Number:
		return string(typedVal), true
	}
	return "", false
}
<|endoftext|>


<commit_before>package engine

import (
	"bytes"
	"errors"
	"regexp"
	"strings"

	h "golang.org/x/net/html"

	"github.com/woobleio/wooblizer/wbzr/engine/doc"
)

// JS Object
type JS struct {
	Name string
	Src  string
}

const docVar string = "this.document"

// GetName returns obj name
func (js *JS) GetName() string { return js.Name }

// GetSource returns obj code source
func (js *JS) GetSource() string { return js.Src }

// IncludeHTMLCSS includes HTML and CSS in the object
func (js *JS) IncludeHTMLCSS(srcHTML string, srcCSS string) error {
	// Fixes net/html new line reading as text node... It breaks the generated script
	doc, err := doc.NewHTML(sanitize(srcHTML))
	if err != nil {
		return errors.New("DOM error : " + err.Error())
	}

	initDocCode := `this.document = document;`
	initDocRegex := regexp.MustCompile(`.*` + initDocCode + `.*`)
	if !initDocRegex.MatchString(js.Src) {
		return errors.New("No document initilization found. this.document = document is required in the object consctructor")
	}

	sRootVar := "_sr_" // Shadow root element
	jsw := newJsWriter(sRootVar)
	if srcHTML != "" {
		constructorRegex := regexp.MustCompile(`.*function Woobly\(`)
		constructorIdx := constructorRegex.FindIndex([]byte(js.Src))
		srcToBytes := []byte(js.Src)
		index := constructorIdx[1]

		coma := ","
		if string(srcToBytes[index:index+1]) == ")" {
			coma = ""
		}
		js.Src = string(append(srcToBytes[:index], append([]byte("_t_"+coma), srcToBytes[index:]...)...))

		jsw.affectVar(sRootVar, "document.querySelector(_t_).attachShadow({mode:'open'})")
		doc.ReadAndExecute(jsw.buildNode, 0)
		jsw.affectAttr("this", "document", sRootVar)

	}

	if srcCSS != "" {
		styleVar := "__s"
		jsw.affectVar(styleVar, "")
		jsw.createElement("style")
		jsw.affectAttr(styleVar, "innerHTML", "'"+sanitize(sanitizeString(srcCSS))+"'")

		if srcHTML != "" {
			jsw.appendChild(docVar, styleVar)
		} else {
			// Rewrite document initialization to override the replaceAll
			jsw.bf.WriteString(initDocCode)
			jsw.appendChild(docVar+".head", styleVar)
		}
	}

	js.Src = string(initDocRegex.ReplaceAll([]byte(js.Src), jsw.bf.Bytes()))

	return nil
}

func sanitize(src string) string {
	rpcer := strings.NewReplacer("\n", "", "\t", "", "\r", "")
	return rpcer.Replace(src)
}

func sanitizeString(src string) string {
	rpcer := strings.NewReplacer("'", "\\'")
	return rpcer.Replace(src)
}

type jsWriter struct {
	bf      bytes.Buffer
	baseVar string
	cVar    string
	vars    []string
}

func newJsWriter(baseVar string) *jsWriter {
	vars := make([]string, 0)
	vars = append(vars, baseVar)
	return &jsWriter{
		bytes.Buffer{},
		baseVar,
		baseVar,
		vars,
	}
}

func (jsw *jsWriter) affectAttr(context string, attrName string, expr string) {
	jsw.bf.WriteString(context)
	jsw.bf.WriteRune('.')
	jsw.bf.WriteString(attrName)
	jsw.bf.WriteString(" = ")
	jsw.bf.WriteString(expr)
	jsw.endExpr()
}

func (jsw *jsWriter) affectVar(varName string, expr string) {
	if len(varName) == 0 {
		varName = jsw.cVar
	}
	jsw.bf.WriteString("var ")
	jsw.bf.WriteString(varName)
	jsw.bf.WriteString(" = ")
	if len(expr) > 0 {
		jsw.bf.WriteString(expr)
		jsw.endExpr()
	}
}

func (jsw *jsWriter) appendChild(to string, toAppend string) {
	if len(toAppend) == 0 {
		toAppend = jsw.cVar
	}
	jsw.bf.WriteString(to)
	jsw.bf.WriteString(".appendChild(")
	jsw.bf.WriteString(toAppend)
	jsw.bf.WriteRune(')')
	jsw.endExpr()
}

func (jsw *jsWriter) buildNode(node *h.Node, pIndex int) int {
	jsw.genUniqueVar()
	jsw.affectVar("", "")
	switch node.Type {
	case h.ElementNode:
		jsw.createElement(node.Data)
	case h.TextNode:
		jsw.createTextNode(node.Data)
	}
	jsw.setAttributes(node.Attr)

	jsVar := jsw.vars[pIndex]
	if jsVar != jsw.baseVar {
		jsVar = "__" + jsVar
	}
	jsw.appendChild(jsVar, "")

	return len(jsw.vars) - 1
}

func (jsw *jsWriter) createElement(el string) {
	jsw.bf.WriteString("document.createElement('")
	jsw.bf.WriteString(el)
	jsw.bf.WriteString("')")
	jsw.endExpr()
}

func (jsw *jsWriter) createTextNode(text string) {
	jsw.bf.WriteString("document.createTextNode('")
	jsw.bf.WriteString(text)
	jsw.bf.WriteString("')")
	jsw.endExpr()
}

func (jsw *jsWriter) endExpr() {
	jsw.bf.WriteRune(';')
}

func (jsw *jsWriter) genUniqueVar() {
	baseNames := [26]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
	tLength := len(jsw.vars)
	bLength := len(baseNames)
	if tLength >= bLength {
		mod := tLength % bLength
		time := tLength / bLength

		jsw.vars = append(jsw.vars, jsw.vars[(time-1)*bLength]+baseNames[mod])
	} else {
		jsw.vars = append(jsw.vars, baseNames[tLength])
	}
	jsw.cVar = "__" + jsw.vars[len(jsw.vars)-1]
}

func (jsw *jsWriter) setAttributes(attrs []h.Attribute) {
	var attrKey string
	for _, attr := range attrs {
		if len(attr.Namespace) > 0 {
			attrKey = attr.Namespace + ":" + attr.Key
		} else {
			attrKey = attr.Key
		}
		jsw.bf.WriteString(jsw.cVar)
		jsw.bf.WriteString(".setAttribute('")
		jsw.bf.WriteString(attrKey)
		jsw.bf.WriteString("', '")
		jsw.bf.WriteString(attr.Val)
		jsw.bf.WriteString("')")
		jsw.endExpr()
	}
}
<commit_msg>Adds comments<commit_after>package engine

import (
	"bytes"
	"errors"
	"regexp"
	"strings"

	h "golang.org/x/net/html"

	"github.com/woobleio/wooblizer/wbzr/engine/doc"
)

// JS Object
type JS struct {
	Name string
	Src  string
}

const docVar string = "this.document"

// GetName returns obj name
func (js *JS) GetName() string { return js.Name }

// GetSource returns obj code source
func (js *JS) GetSource() string { return js.Src }

// IncludeHTMLCSS includes HTML and CSS in the object
func (js *JS) IncludeHTMLCSS(srcHTML string, srcCSS string) error {
	// Fixes net/html new line reading as text node... It breaks the generated script
	doc, err := doc.NewHTML(sanitize(srcHTML))
	if err != nil {
		return errors.New("DOM error : " + err.Error())
	}

	initDocCode := `this.document = document;`
	initDocRegex := regexp.MustCompile(`.*` + initDocCode + `.*`)
	if !initDocRegex.MatchString(js.Src) {
		return errors.New("No document initilization found. this.document = document is required in the object consctructor")
	}

	sRootVar := "_sr_" // Shadow root element
	jsw := newJsWriter(sRootVar)
	if srcHTML != "" {
		constructorRegex := regexp.MustCompile(`.*function Woobly\(`)
		constructorIdx := constructorRegex.FindIndex([]byte(js.Src))
		srcToBytes := []byte(js.Src)
		index := constructorIdx[1]

		coma := ","
		if string(srcToBytes[index:index+1]) == ")" {
			coma = ""
		}
		// Insert target parameter in the object constructor
		js.Src = string(append(srcToBytes[:index], append([]byte("_t_"+coma), srcToBytes[index:]...)...))

		jsw.affectVar(sRootVar, "document.querySelector(_t_).attachShadow({mode:'open'})")
		doc.ReadAndExecute(jsw.buildNode, 0)
		jsw.affectAttr("this", "document", sRootVar)
	}

	if srcCSS != "" {
		styleVar := "__s"
		jsw.affectVar(styleVar, "")
		jsw.createElement("style")
		jsw.affectAttr(styleVar, "innerHTML", "'"+sanitize(sanitizeString(srcCSS))+"'")

		if srcHTML != "" {
			jsw.appendChild(docVar, styleVar)
		} else {
			// Rewrite document initialization to override the replaceAll
			jsw.bf.WriteString(initDocCode)
			jsw.appendChild(docVar+".head", styleVar)
		}
	}

	js.Src = string(initDocRegex.ReplaceAll([]byte(js.Src), jsw.bf.Bytes()))

	return nil
}

func sanitize(src string) string {
	rpcer := strings.NewReplacer("\n", "", "\t", "", "\r", "")
	return rpcer.Replace(src)
}

func sanitizeString(src string) string {
	rpcer := strings.NewReplacer("'", "\\'")
	return rpcer.Replace(src)
}

type jsWriter struct {
	bf      bytes.Buffer
	baseVar string
	cVar    string
	vars    []string
}

func newJsWriter(baseVar string) *jsWriter {
	vars := make([]string, 0)
	vars = append(vars, baseVar)
	return &jsWriter{
		bytes.Buffer{},
		baseVar,
		baseVar,
		vars,
	}
}

func (jsw *jsWriter) affectAttr(context string, attrName string, expr string) {
	jsw.bf.WriteString(context)
	jsw.bf.WriteRune('.')
	jsw.bf.WriteString(attrName)
	jsw.bf.WriteString(" = ")
	jsw.bf.WriteString(expr)
	jsw.endExpr()
}

func (jsw *jsWriter) affectVar(varName string, expr string) {
	if len(varName) == 0 {
		varName = jsw.cVar
	}
	jsw.bf.WriteString("var ")
	jsw.bf.WriteString(varName)
	jsw.bf.WriteString(" = ")
	if len(expr) > 0 {
		jsw.bf.WriteString(expr)
		jsw.endExpr()
	}
}

func (jsw *jsWriter) appendChild(to string, toAppend string) {
	if len(toAppend) == 0 {
		toAppend = jsw.cVar
	}
	jsw.bf.WriteString(to)
	jsw.bf.WriteString(".appendChild(")
	jsw.bf.WriteString(toAppend)
	jsw.bf.WriteRune(')')
	jsw.endExpr()
}

func (jsw *jsWriter) buildNode(node *h.Node, pIndex int) int {
	jsw.genUniqueVar()
	jsw.affectVar("", "")
	switch node.Type {
	case h.ElementNode:
		jsw.createElement(node.Data)
	case h.TextNode:
		jsw.createTextNode(node.Data)
	}
	jsw.setAttributes(node.Attr)

	jsVar := jsw.vars[pIndex]
	if jsVar != jsw.baseVar {
		jsVar = "__" + jsVar
	}
	jsw.appendChild(jsVar, "")

	return len(jsw.vars) - 1
}

func (jsw *jsWriter) createElement(el string) {
	jsw.bf.WriteString("document.createElement('")
	jsw.bf.WriteString(el)
	jsw.bf.WriteString("')")
	jsw.endExpr()
}

func (jsw *jsWriter) createTextNode(text string) {
	jsw.bf.WriteString("document.createTextNode('")
	jsw.bf.WriteString(text)
	jsw.bf.WriteString("')")
	jsw.endExpr()
}

func (jsw *jsWriter) endExpr() {
	jsw.bf.WriteRune(';')
}

func (jsw *jsWriter) genUniqueVar() {
	baseNames := [26]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
	tLength := len(jsw.vars)
	bLength := len(baseNames)
	if tLength >= bLength {
		mod := tLength % bLength
		time := tLength / bLength

		jsw.vars = append(jsw.vars, jsw.vars[(time-1)*bLength]+baseNames[mod])
	} else {
		jsw.vars = append(jsw.vars, baseNames[tLength])
	}
	jsw.cVar = "__" + jsw.vars[len(jsw.vars)-1]
}

func (jsw *jsWriter) setAttributes(attrs []h.Attribute) {
	var attrKey string
	for _, attr := range attrs {
		if len(attr.Namespace) > 0 {
			attrKey = attr.Namespace + ":" + attr.Key
		} else {
			attrKey = attr.Key
		}
		jsw.bf.WriteString(jsw.cVar)
		jsw.bf.WriteString(".setAttribute('")
		jsw.bf.WriteString(attrKey)
		jsw.bf.WriteString("', '")
		jsw.bf.WriteString(attr.Val)
		jsw.bf.WriteString("')")
		jsw.endExpr()
	}
}
<|endoftext|>


<commit_before>package init

import (
	"errors"
	"log"
	"os"
	"strings"
	"time"

	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

/*
CheckRoomInitialization will check if the system is running locally (if it
should be mapped to a room). If yes, pull the room configuration and run the
init code.
*/
func CheckRoomInitialization() error {

	log.Printf("Initializing.")

	//Check if local
	if len(os.Getenv("LOCAL_ENVIRONMENT")) < 1 {
		log.Printf("Not a local instance of the API.")
		log.Printf("Done.")
		return nil
	}

	log.Printf("Getting room information.")

	/*
	  It's not local, parse the hostname for the building room
	  hostname must be in the format {{BuildingShortname}}-{{RoomIdentifier}}
	  or buildling hyphen room. e.g. ITB-1001D
	*/

	//DEBUG
	//hostname := os.Getenv("HOSTNAME")

	hostname := "ITB-1006"
	//END DEBUG

	splitValues := strings.Split(hostname, "-")
	log.Printf("Room %v-%v", splitValues[0], splitValues[1])

	attempts := 0

	room, err := dbo.GetRoomByInfo(splitValues[0], splitValues[1])
	if err != nil {

		//If there was an error we want to attempt to connect multiple times - as the
		//configuration service may not be up.
		for attempts < 40 {
			log.Printf("Attempting to connect to DB...")
			room, err = dbo.GetRoomByInfo(splitValues[0], splitValues[1])
			if err != nil {
				log.Printf("Error: %s", err.Error())
				attempts++
				time.Sleep(2 * time.Second)
			} else {
				break
			}
		}
		if attempts > 30 && err != nil {
			log.Printf("Error Retrieving room information.")
			return err
		}
	}

	//There is no initializer, no need to run code
	if len(room.Configuration.RoomInitKey) < 1 {
		return nil
	}

	//take our room and get the init key
	initMap := getMap()
	if initializor, ok := initMap[room.Configuration.RoomInitKey]; ok {
		initializor.Initialize(room)
		return nil
	}

	return errors.New("No initializer for the key in configuration")
}

//RoomInitializer is the interface programmed against to build a new roomInitializer
type RoomInitializer interface {

	/*
	  Initizlize performs the actions necessary for the room on startup.
	  This is called when the AV-API service is spun up.
	*/
	Initialize(accessors.Room) error
}

//InitializerMap is the map that contains the initializers
var InitializerMap = make(map[string]RoomInitializer)
var roomInitializerBuilt = false

//Init builds or returns the CommandMap
func getMap() map[string]RoomInitializer {
	if !roomInitializerBuilt {
		//Add the new initializers here
		InitializerMap["Default"] = &DefaultInitializer{}
		InitializerMap["DMPS"] = &DMPSInitializer{}
	}

	return InitializerMap
}
<commit_msg>removing debug piece<commit_after>package init

import (
	"errors"
	"log"
	"os"
	"strings"
	"time"

	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

/*
CheckRoomInitialization will check if the system is running locally (if it
should be mapped to a room). If yes, pull the room configuration and run the
init code.
*/
func CheckRoomInitialization() error {

	log.Printf("Initializing.")

	//Check if local
	if len(os.Getenv("LOCAL_ENVIRONMENT")) < 1 {
		log.Printf("Not a local instance of the API.")
		log.Printf("Done.")
		return nil
	}

	log.Printf("Getting room information.")

	/*
	  It's not local, parse the hostname for the building room
	  hostname must be in the format {{BuildingShortname}}-{{RoomIdentifier}}
	  or buildling hyphen room. e.g. ITB-1001D
	*/

	hostname := os.Getenv("PI_HOSTNAME")

	splitValues := strings.Split(hostname, "-")
	log.Printf("Room %v-%v", splitValues[0], splitValues[1])

	attempts := 0

	room, err := dbo.GetRoomByInfo(splitValues[0], splitValues[1])
	if err != nil {

		//If there was an error we want to attempt to connect multiple times - as the
		//configuration service may not be up.
		for attempts < 40 {
			log.Printf("Attempting to connect to DB...")
			room, err = dbo.GetRoomByInfo(splitValues[0], splitValues[1])
			if err != nil {
				log.Printf("Error: %s", err.Error())
				attempts++
				time.Sleep(2 * time.Second)
			} else {
				break
			}
		}
		if attempts > 30 && err != nil {
			log.Printf("Error Retrieving room information.")
			return err
		}
	}

	//There is no initializer, no need to run code
	if len(room.Configuration.RoomInitKey) < 1 {
		return nil
	}

	//take our room and get the init key
	initMap := getMap()
	if initializor, ok := initMap[room.Configuration.RoomInitKey]; ok {
		initializor.Initialize(room)
		return nil
	}

	return errors.New("No initializer for the key in configuration")
}

//RoomInitializer is the interface programmed against to build a new roomInitializer
type RoomInitializer interface {

	/*
	  Initizlize performs the actions necessary for the room on startup.
	  This is called when the AV-API service is spun up.
	*/
	Initialize(accessors.Room) error
}

//InitializerMap is the map that contains the initializers
var InitializerMap = make(map[string]RoomInitializer)
var roomInitializerBuilt = false

//Init builds or returns the CommandMap
func getMap() map[string]RoomInitializer {
	if !roomInitializerBuilt {
		//Add the new initializers here
		InitializerMap["Default"] = &DefaultInitializer{}
		InitializerMap["DMPS"] = &DMPSInitializer{}
	}

	return InitializerMap
}
<|endoftext|>


<commit_before>/*
	Data type labels64 tailors the voxels data type for 64-bit label images.  It simply
	wraps the voxels package, setting Channels (1) and BytesPerValue(4).
*/

package voxels

import (
	"github.com/janelia-flyem/dvid/datastore"
)

func init() {
	values := []DataValue{
		{
			DataType: "uint64",
			Label:    "labels64",
		},
	}
	labels := NewDatatype(1, 8, values)
	labels.DatatypeID = &datastore.DatatypeID{
		Name:    "labels64",
		Url:     "github.com/janelia-flyem/dvid/datatype/voxels/labels64.go",
		Version: "0.6",
	}
	datastore.RegisterDatatype(labels)
}
<commit_msg>Promote labels64 to its own package<commit_after><|endoftext|>


<commit_before>// Copyright 2013 The go-instagram AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package instagram provides a client for using the Instagram API.

Access different parts of the Instagram API using the various services on a Instagram
Client (second parameter is access token that likely you'll need to access most of
Instagram endpoints):

	client := instagram.NewClient(nil)

You can then optionally set ClientID, ClientSecret and AccessToken:

	client.ClientID = "8f2c0ad697ea4094beb2b1753b7cde9c"

With client object set, you can call Instagram endpoints:

	// Gets the most recent media published by a user with id "3"
	media, next, err := client.Users.RecentMedia("3", nil)

Set optional parameters for an API method by passing an Parameters object.

	// Gets user's feed.
	opt := &instagram.Parameters{Count: 3}
	media, next, err := client.Users.RecentMedia("3", opt)

The full Instagram API is documented at http://instagram.com/developer/endpoints/.
*/
package instagram

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	// LibraryVersion represents this library version
	LibraryVersion = "0.1"

	// BaseURL represents Instagram API base URL
	BaseURL = "https://api.instagram.com/v1/"

	// UserAgent represents this client User-Agent
	UserAgent = "go-instagram/" + LibraryVersion
)

// A Client manages communication with the Instagram API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.
	BaseURL *url.URL

	// UserAgent agent used when communicating with Instagram API.
	UserAgent string

	// Application client_id
	ClientID string

	// Application client_secret
	ClientSecret string

	// Authenticated user's access_token
	AccessToken string

	// Services used for talking to different parts of the API.
	Users         *UsersService
	Relationships *RelationshipsService
	Media         *MediaService
	Comments      *CommentsService
	Likes         *LikesService
	Tags          *TagsService
	Locations     *LocationsService
	Geographies   *GeographiesService

	// Temporary Response
	Response *Response
}

// Parameters specifies the optional parameters to various service's methods.
type Parameters struct {
	Count        uint64
	MinID        string
	MaxID        string
	MinTimestamp int64
	MaxTimestamp int64
	Lat          float64
	Lng          float64
	Distance     float64
}

// Response specifies Instagram's response structure.
//
// Instagram's envelope structure spec: http://instagram.com/developer/endpoints/#structure
type Response struct {
	Response   *http.Response      // HTTP response
	Meta       *ResponseMeta       `json:"meta,omitempty"`
	Data       interface{}         `json:"data,omitempty"`
	Pagination *ResponsePagination `json:"pagination,omitempty"`
}

// GetMeta gets extra information about the response. If all goes well,
// only Code key with value 200 is returned. If something goes wrong,
// ErrorType and ErrorMessage keys are present.
func (r *Response) GetMeta() *ResponseMeta {
	return r.Meta
}

// GetData gets the meat of the response.
func (r *Response) GetData() interface{} {
	return &r.Data
}

// GetError gets error from meta's response.
func (r *Response) GetError() error {
	if r.Meta.ErrorType != "" || r.Meta.ErrorMessage != "" {
		return fmt.Errorf(fmt.Sprintf("%s: %s", r.Meta.ErrorType, r.Meta.ErrorMessage))
	}
	return nil
}

// GetPagination gets pagination information.
func (r *Response) GetPagination() *ResponsePagination {
	return r.Pagination
}

// NextURL gets next url which represents URL for next set of data.
func (r *Response) NextURL() string {
	p := r.GetPagination()
	return p.NextURL
}

// NextMaxID gets MaxID parameter that can be passed for next request.
func (r *Response) NextMaxID() string {
	p := r.GetPagination()
	return p.NextMaxID
}

// ResponseMeta represents information about the response. If all goes well,
// only a Code key with value 200 will present. However, sometimes things
// go wrong, and in that case ErrorType and ErrorMessage are present.
type ResponseMeta struct {
	ErrorType    string `json:"error_type,omitempty"`
	Code         int    `json:"code,omitempty"`
	ErrorMessage string `json:"error_message,omitempty"`
}

// ResponsePagination represents information to get access to more data in
// any request for sequential data.
type ResponsePagination struct {
	NextURL   string `json:"next_url,omitempty"`
	NextMaxID string `json:"next_max_id,omitempty"`
}

// NewClient returns a new Instagram API client. if a nil httpClient is
// provided, http.DefaultClient will be used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(BaseURL)

	c := &Client{
		client:    httpClient,
		BaseURL:   baseURL,
		UserAgent: UserAgent,
	}
	c.Users = &UsersService{client: c}
	c.Relationships = &RelationshipsService{client: c}
	c.Media = &MediaService{client: c}
	c.Comments = &CommentsService{client: c}
	c.Likes = &LikesService{client: c}
	c.Tags = &TagsService{client: c}
	c.Locations = &LocationsService{client: c}
	c.Geographies = &GeographiesService{client: c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified
func (c *Client) NewRequest(method, urlStr string, body string) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)
	q := u.Query()
	if c.AccessToken != "" && q.Get("access_token") == "" {
		q.Set("access_token", c.AccessToken)
	}
	if c.ClientID != "" && q.Get("client_id") == "" {
		q.Set("client_id", c.ClientID)
	}
	if c.ClientSecret != "" && q.Get("client_secret") == "" {
		q.Set("client_secret", c.ClientSecret)
	}
	u.RawQuery = q.Encode()

	req, err := http.NewRequest(method, u.String(), bytes.NewBufferString(body))
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// decoded and stored in the value pointed to by v, or returned as an error if
// an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	// TODO: Checks rate limit

	err = CheckResponse(resp)
	if err != nil {
		return resp, err
	}

	r := &Response{Response: resp}
	if v != nil {
		r.Data = v
		err = json.NewDecoder(resp.Body).Decode(r)
		c.Response = r
	}
	return resp, err
}

// ErrorResponse represents a Response which contains an error
type ErrorResponse Response

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d %v %v",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode, r.Meta.ErrorType, r.Meta.ErrorMessage)
}

// CheckResponse checks the API response for error, and returns it
// if present. A response is considered an error if it has non StatusOK
// code.
func CheckResponse(r *http.Response) error {
	if r.StatusCode == http.StatusOK {
		return nil
	}
	resp := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, resp)
	}
	return resp
}
<commit_msg>If method is POST then set Content-Type to application/x-www-form-urlencoded.<commit_after>// Copyright 2013 The go-instagram AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package instagram provides a client for using the Instagram API.

Access different parts of the Instagram API using the various services on a Instagram
Client (second parameter is access token that likely you'll need to access most of
Instagram endpoints):

	client := instagram.NewClient(nil)

You can then optionally set ClientID, ClientSecret and AccessToken:

	client.ClientID = "8f2c0ad697ea4094beb2b1753b7cde9c"

With client object set, you can call Instagram endpoints:

	// Gets the most recent media published by a user with id "3"
	media, next, err := client.Users.RecentMedia("3", nil)

Set optional parameters for an API method by passing an Parameters object.

	// Gets user's feed.
	opt := &instagram.Parameters{Count: 3}
	media, next, err := client.Users.RecentMedia("3", opt)

The full Instagram API is documented at http://instagram.com/developer/endpoints/.
*/
package instagram

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	// LibraryVersion represents this library version
	LibraryVersion = "0.1"

	// BaseURL represents Instagram API base URL
	BaseURL = "https://api.instagram.com/v1/"

	// UserAgent represents this client User-Agent
	UserAgent = "go-instagram/" + LibraryVersion
)

// A Client manages communication with the Instagram API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.
	BaseURL *url.URL

	// UserAgent agent used when communicating with Instagram API.
	UserAgent string

	// Application client_id
	ClientID string

	// Application client_secret
	ClientSecret string

	// Authenticated user's access_token
	AccessToken string

	// Services used for talking to different parts of the API.
	Users         *UsersService
	Relationships *RelationshipsService
	Media         *MediaService
	Comments      *CommentsService
	Likes         *LikesService
	Tags          *TagsService
	Locations     *LocationsService
	Geographies   *GeographiesService

	// Temporary Response
	Response *Response
}

// Parameters specifies the optional parameters to various service's methods.
type Parameters struct {
	Count        uint64
	MinID        string
	MaxID        string
	MinTimestamp int64
	MaxTimestamp int64
	Lat          float64
	Lng          float64
	Distance     float64
}

// Response specifies Instagram's response structure.
//
// Instagram's envelope structure spec: http://instagram.com/developer/endpoints/#structure
type Response struct {
	Response   *http.Response      // HTTP response
	Meta       *ResponseMeta       `json:"meta,omitempty"`
	Data       interface{}         `json:"data,omitempty"`
	Pagination *ResponsePagination `json:"pagination,omitempty"`
}

// GetMeta gets extra information about the response. If all goes well,
// only Code key with value 200 is returned. If something goes wrong,
// ErrorType and ErrorMessage keys are present.
func (r *Response) GetMeta() *ResponseMeta {
	return r.Meta
}

// GetData gets the meat of the response.
func (r *Response) GetData() interface{} {
	return &r.Data
}

// GetError gets error from meta's response.
func (r *Response) GetError() error {
	if r.Meta.ErrorType != "" || r.Meta.ErrorMessage != "" {
		return fmt.Errorf(fmt.Sprintf("%s: %s", r.Meta.ErrorType, r.Meta.ErrorMessage))
	}
	return nil
}

// GetPagination gets pagination information.
func (r *Response) GetPagination() *ResponsePagination {
	return r.Pagination
}

// NextURL gets next url which represents URL for next set of data.
func (r *Response) NextURL() string {
	p := r.GetPagination()
	return p.NextURL
}

// NextMaxID gets MaxID parameter that can be passed for next request.
func (r *Response) NextMaxID() string {
	p := r.GetPagination()
	return p.NextMaxID
}

// ResponseMeta represents information about the response. If all goes well,
// only a Code key with value 200 will present. However, sometimes things
// go wrong, and in that case ErrorType and ErrorMessage are present.
type ResponseMeta struct {
	ErrorType    string `json:"error_type,omitempty"`
	Code         int    `json:"code,omitempty"`
	ErrorMessage string `json:"error_message,omitempty"`
}

// ResponsePagination represents information to get access to more data in
// any request for sequential data.
type ResponsePagination struct {
	NextURL   string `json:"next_url,omitempty"`
	NextMaxID string `json:"next_max_id,omitempty"`
}

// NewClient returns a new Instagram API client. if a nil httpClient is
// provided, http.DefaultClient will be used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(BaseURL)

	c := &Client{
		client:    httpClient,
		BaseURL:   baseURL,
		UserAgent: UserAgent,
	}
	c.Users = &UsersService{client: c}
	c.Relationships = &RelationshipsService{client: c}
	c.Media = &MediaService{client: c}
	c.Comments = &CommentsService{client: c}
	c.Likes = &LikesService{client: c}
	c.Tags = &TagsService{client: c}
	c.Locations = &LocationsService{client: c}
	c.Geographies = &GeographiesService{client: c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified
func (c *Client) NewRequest(method, urlStr string, body string) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)
	q := u.Query()
	if c.AccessToken != "" && q.Get("access_token") == "" {
		q.Set("access_token", c.AccessToken)
	}
	if c.ClientID != "" && q.Get("client_id") == "" {
		q.Set("client_id", c.ClientID)
	}
	if c.ClientSecret != "" && q.Get("client_secret") == "" {
		q.Set("client_secret", c.ClientSecret)
	}
	u.RawQuery = q.Encode()

	req, err := http.NewRequest(method, u.String(), bytes.NewBufferString(body))
	if err != nil {
		return nil, err
	}

	if method == "POST" {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// decoded and stored in the value pointed to by v, or returned as an error if
// an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	// TODO: Checks rate limit

	err = CheckResponse(resp)
	if err != nil {
		return resp, err
	}

	r := &Response{Response: resp}
	if v != nil {
		r.Data = v
		err = json.NewDecoder(resp.Body).Decode(r)
		c.Response = r
	}
	return resp, err
}

// ErrorResponse represents a Response which contains an error
type ErrorResponse Response

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d %v %v",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode, r.Meta.ErrorType, r.Meta.ErrorMessage)
}

// CheckResponse checks the API response for error, and returns it
// if present. A response is considered an error if it has non StatusOK
// code.
func CheckResponse(r *http.Response) error {
	if r.StatusCode == http.StatusOK {
		return nil
	}
	resp := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, resp)
	}
	return resp
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-instagram AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package instagram provides a client for using the Instagram API.

Access different parts of the Instagram API using the various services on a Instagram
Client (second parameter is access token that likely you'll need to access most of
Instagram endpoints):

	client := instagram.NewClient(nil)

You can then optionally set ClientID, ClientSecret and AccessToken:

	client.ClientID = "8f2c0ad697ea4094beb2b1753b7cde9c"

With client object set, you can call Instagram endpoints:

	// Gets the most recent media published by a user with id "3"
	media, next, err := client.Users.RecentMedia("3", nil)

Set optional parameters for an API method by passing an Parameters object.

	// Gets user's feed.
	opt := &instagram.Parameters{Count: 3}
	media, next, err := client.Users.RecentMedia("3", opt)

The full Instagram API is documented at http://instagram.com/developer/endpoints/.
*/
package instagram

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

const (
	// LibraryVersion represents this library version
	LibraryVersion = "0.5"

	// BaseURL represents Instagram API base URL
	BaseURL = "https://api.instagram.com/v1/"

	// UserAgent represents this client User-Agent
	UserAgent = "github.com/carbocation/go-instagram v" + LibraryVersion
)

// A Client manages communication with the Instagram API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.
	BaseURL *url.URL

	// UserAgent agent used when communicating with Instagram API.
	UserAgent string

	// Application client_id
	ClientID string

	// Application client_secret
	ClientSecret string

	// Authenticated user's access_token
	AccessToken string

	// For Authenticated endpoints, using X-Forwarded-For
	// increases events per hour permitted by Instagram
	// This value should, if not nil, be the value of
	// a user's IP address. See
	// http://instagram.com/developer/restrict-api-requests/
	// for additional detail
	XInstaForwardedFor string

	// Services used for talking to different parts of the API.
	Users         *UsersService
	Relationships *RelationshipsService
	Media         *MediaService
	Comments      *CommentsService
	Likes         *LikesService
	Tags          *TagsService
	Locations     *LocationsService
	Geographies   *GeographiesService
	Realtime      *RealtimeService

	// Temporary Response
	Response *Response
}

// Parameters specifies the optional parameters to various service's methods.
type Parameters struct {
	Count        uint64
	MinID        string
	MaxID        string
	MinTimestamp int64
	MaxTimestamp int64
	Lat          float64
	Lng          float64
	Distance     float64
}

// Ratelimit specifies API calls limit found in HTTP headers.
type Ratelimit struct {
	// Total number of possible calls per hour
	Limit int

	// How many calls are left for this particular token or client ID
	Remaining int
}

// Response specifies Instagram's response structure.
//
// Instagram's envelope structure spec: http://instagram.com/developer/endpoints/#structure
type Response struct {
	Response   *http.Response      // HTTP response
	Meta       *ResponseMeta       `json:"meta,omitempty"`
	Data       interface{}         `json:"data,omitempty"`
	Pagination *ResponsePagination `json:"pagination,omitempty"`
}

// GetMeta gets extra information about the response. If all goes well,
// only Code key with value 200 is returned. If something goes wrong,
// ErrorType and ErrorMessage keys are present.
func (r *Response) GetMeta() *ResponseMeta {
	return r.Meta
}

// GetData gets the meat of the response.
func (r *Response) GetData() interface{} {
	return &r.Data
}

// GetError gets error from meta's response.
func (r *Response) GetError() error {
	if r.Meta.ErrorType != "" || r.Meta.ErrorMessage != "" {
		return fmt.Errorf(fmt.Sprintf("%s: %s", r.Meta.ErrorType, r.Meta.ErrorMessage))
	}
	return nil
}

// GetPagination gets pagination information.
func (r *Response) GetPagination() *ResponsePagination {
	return r.Pagination
}

// Parsed rate limit information from response headers.
func (r *Response) GetRatelimit() (Ratelimit, error) {
	var rl Ratelimit
	var err error
	const (
		Limit     = `X-Ratelimit-Limit`
		Remaining = `X-Ratelimit-Remaining`
	)

	rl.Limit, err = strconv.Atoi(r.Response.Header.Get(Limit))
	if err != nil {
		return rl, err
	}

	rl.Remaining, err = strconv.Atoi(r.Response.Header.Get(Remaining))
	return rl, err
}

// NextURL gets next url which represents URL for next set of data.
func (r *Response) NextURL() string {
	p := r.GetPagination()
	return p.NextURL
}

// NextMaxID gets MaxID parameter that can be passed for next request.
func (r *Response) NextMaxID() string {
	p := r.GetPagination()
	return p.NextMaxID
}

// ResponseMeta represents information about the response. If all goes well,
// only a Code key with value 200 will present. However, sometimes things
// go wrong, and in that case ErrorType and ErrorMessage are present.
type ResponseMeta struct {
	ErrorType    string `json:"error_type,omitempty"`
	Code         int    `json:"code,omitempty"`
	ErrorMessage string `json:"error_message,omitempty"`
}

// ResponsePagination represents information to get access to more data in
// any request for sequential data.
type ResponsePagination struct {
	NextURL   string `json:"next_url,omitempty"`
	NextMaxID string `json:"next_max_id,omitempty"`
}

// NewClient returns a new Instagram API client. if a nil httpClient is
// provided, http.DefaultClient will be used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(BaseURL)

	c := &Client{
		client:    httpClient,
		BaseURL:   baseURL,
		UserAgent: UserAgent,
	}
	c.Users = &UsersService{client: c}
	c.Relationships = &RelationshipsService{client: c}
	c.Media = &MediaService{client: c}
	c.Comments = &CommentsService{client: c}
	c.Likes = &LikesService{client: c}
	c.Tags = &TagsService{client: c}
	c.Locations = &LocationsService{client: c}
	c.Geographies = &GeographiesService{client: c}
	c.Realtime = &RealtimeService{client: c}

	return c
}

func (c *Client) ComputeXInstaForwardedFor() string {
	if c.XInstaForwardedFor == "" {
		return ""
	}

	mac := hmac.New(sha256.New, []byte(c.ClientSecret))
	mac.Write([]byte(c.XInstaForwardedFor))

	return fmt.Sprintf("%s|%s", c.XInstaForwardedFor, hex.EncodeToString(mac.Sum(nil)))
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified
func (c *Client) NewRequest(method, urlStr string, body string) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)
	q := u.Query()
	if c.AccessToken != "" && q.Get("access_token") == "" {
		q.Set("access_token", c.AccessToken)
	}
	if c.ClientID != "" && q.Get("client_id") == "" {
		q.Set("client_id", c.ClientID)
	}
	if c.ClientSecret != "" && q.Get("client_secret") == "" {
		q.Set("client_secret", c.ClientSecret)
	}
	u.RawQuery = q.Encode()

	req, err := http.NewRequest(method, u.String(), bytes.NewBufferString(body))
	if err != nil {
		return nil, err
	}

	if method == "POST" {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	}

	if c.XInstaForwardedFor != "" {
		req.Header.Add("X-Insta-Forwarded-For", c.ComputeXInstaForwardedFor())
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// decoded and stored in the value pointed to by v, or returned as an error if
// an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	err = CheckResponse(resp)
	if err != nil {
		return resp, err
	}

	r := &Response{Response: resp}
	if v != nil {
		r.Data = v
		err = json.NewDecoder(resp.Body).Decode(r)
		c.Response = r
	}
	return resp, err
}

// InstagramError represents an error recieved from instagram
type InstagramError ResponseMeta

// Error makes the InstagramError suitable for the error interface
func (err *InstagramError) Error() string {
	return fmt.Sprintf("%s (%d): %s", err.ErrorType, err.Code, err.ErrorMessage)
}

// ErrorResponse represents a Response which contains an error
type ErrorResponse Response

func (r *ErrorResponse) Error() string {
	if r == nil {
		return fmt.Sprintf("A nil error response was returned")
	}

	if r.Response == nil || r.Response.Request == nil {
		return fmt.Sprintf("A nil error response was returned on %v", r)
	}

	if r.Response.Request.URL == nil {
		return fmt.Sprintf("A nil error response was returned on %v", r.Response.Request)
	}

	if r.Meta == nil {
		return fmt.Sprintf("%v %v: %d (no metadata)", r.Response.Request.Method, r.Response.Request.URL,
			r.Response.StatusCode)
	}

	return fmt.Sprintf("%v %v: %d %v %v",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode, r.Meta.ErrorType, r.Meta.ErrorMessage)
}

// CheckResponse checks the API response for error, and returns it
// if present. A response is considered an error if it has non StatusOK
// code.
func CheckResponse(r *http.Response) error {
	if r.StatusCode == http.StatusOK {
		return nil
	}

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

	// Forbidden: see http://instagram.com/developer/restrict-api-requests/
	if r.StatusCode == http.StatusForbidden {
		err := &InstagramError{}
		json.Unmarshal(data, &err)
		return err
	}

	// RateLimit: see http://instagram.com/developer/limits/
	if r.StatusCode == 429 {
		err := &InstagramError{}
		json.Unmarshal(data, &err)
		return err
	}

	// Sometimes Instagram returns 500 with plain message
	// "Oops, an error occurred.".
	if r.StatusCode == http.StatusInternalServerError {
		err := &InstagramError{
			ErrorType:    "Internal Server Error",
			Code:         http.StatusInternalServerError,
			ErrorMessage: "Oops, an error occurred.",
		}
		return err
	}

	if data != nil {
		// Unlike for successful (2XX) requests, unsuccessful
		// requests SOMETIMES have the {Meta: Error{}} format but
		// SOMETIMES they are just Error{}. From what I can tell, there is not
		// an obvious rationale behind what gets constructed in which way, so
		// we need to try both:
		err := &InstagramError{}
		json.Unmarshal(data, err)
		if *err != *new(InstagramError) {
			// Unmarshaling did something
			return err
		} else {
			// Unmarshaling did nothing for us, so the format was not Error{}.
			// We will assume the format was {Meta: Error{}}:
			temp := make(map[string]interface{})
			json.Unmarshal(data, &temp)

			// Convert the meta field to InstagramError
			if igErr, ok := temp["meta"].(*InstagramError); ok {
				return igErr
			} else {
				return &InstagramError{
					ErrorType:    "Unknown Error",
					Code:         0,
					ErrorMessage: fmt.Sprintf("%v", temp),
				}
			}
		}
	}

	return nil
}
<commit_msg>Fixed meta error handling<commit_after>// Copyright 2013 The go-instagram AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package instagram provides a client for using the Instagram API.

Access different parts of the Instagram API using the various services on a Instagram
Client (second parameter is access token that likely you'll need to access most of
Instagram endpoints):

	client := instagram.NewClient(nil)

You can then optionally set ClientID, ClientSecret and AccessToken:

	client.ClientID = "8f2c0ad697ea4094beb2b1753b7cde9c"

With client object set, you can call Instagram endpoints:

	// Gets the most recent media published by a user with id "3"
	media, next, err := client.Users.RecentMedia("3", nil)

Set optional parameters for an API method by passing an Parameters object.

	// Gets user's feed.
	opt := &instagram.Parameters{Count: 3}
	media, next, err := client.Users.RecentMedia("3", opt)

The full Instagram API is documented at http://instagram.com/developer/endpoints/.
*/
package instagram

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

const (
	// LibraryVersion represents this library version
	LibraryVersion = "0.5"

	// BaseURL represents Instagram API base URL
	BaseURL = "https://api.instagram.com/v1/"

	// UserAgent represents this client User-Agent
	UserAgent = "github.com/carbocation/go-instagram v" + LibraryVersion
)

// A Client manages communication with the Instagram API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.
	BaseURL *url.URL

	// UserAgent agent used when communicating with Instagram API.
	UserAgent string

	// Application client_id
	ClientID string

	// Application client_secret
	ClientSecret string

	// Authenticated user's access_token
	AccessToken string

	// For Authenticated endpoints, using X-Forwarded-For
	// increases events per hour permitted by Instagram
	// This value should, if not nil, be the value of
	// a user's IP address. See
	// http://instagram.com/developer/restrict-api-requests/
	// for additional detail
	XInstaForwardedFor string

	// Services used for talking to different parts of the API.
	Users         *UsersService
	Relationships *RelationshipsService
	Media         *MediaService
	Comments      *CommentsService
	Likes         *LikesService
	Tags          *TagsService
	Locations     *LocationsService
	Geographies   *GeographiesService
	Realtime      *RealtimeService

	// Temporary Response
	Response *Response
}

// Parameters specifies the optional parameters to various service's methods.
type Parameters struct {
	Count        uint64
	MinID        string
	MaxID        string
	MinTimestamp int64
	MaxTimestamp int64
	Lat          float64
	Lng          float64
	Distance     float64
}

// Ratelimit specifies API calls limit found in HTTP headers.
type Ratelimit struct {
	// Total number of possible calls per hour
	Limit int

	// How many calls are left for this particular token or client ID
	Remaining int
}

// Response specifies Instagram's response structure.
//
// Instagram's envelope structure spec: http://instagram.com/developer/endpoints/#structure
type Response struct {
	Response   *http.Response      // HTTP response
	Meta       *ResponseMeta       `json:"meta,omitempty"`
	Data       interface{}         `json:"data,omitempty"`
	Pagination *ResponsePagination `json:"pagination,omitempty"`
}

// GetMeta gets extra information about the response. If all goes well,
// only Code key with value 200 is returned. If something goes wrong,
// ErrorType and ErrorMessage keys are present.
func (r *Response) GetMeta() *ResponseMeta {
	return r.Meta
}

// GetData gets the meat of the response.
func (r *Response) GetData() interface{} {
	return &r.Data
}

// GetError gets error from meta's response.
func (r *Response) GetError() error {
	if r.Meta.ErrorType != "" || r.Meta.ErrorMessage != "" {
		return fmt.Errorf(fmt.Sprintf("%s: %s", r.Meta.ErrorType, r.Meta.ErrorMessage))
	}
	return nil
}

// GetPagination gets pagination information.
func (r *Response) GetPagination() *ResponsePagination {
	return r.Pagination
}

// Parsed rate limit information from response headers.
func (r *Response) GetRatelimit() (Ratelimit, error) {
	var rl Ratelimit
	var err error
	const (
		Limit     = `X-Ratelimit-Limit`
		Remaining = `X-Ratelimit-Remaining`
	)

	rl.Limit, err = strconv.Atoi(r.Response.Header.Get(Limit))
	if err != nil {
		return rl, err
	}

	rl.Remaining, err = strconv.Atoi(r.Response.Header.Get(Remaining))
	return rl, err
}

// NextURL gets next url which represents URL for next set of data.
func (r *Response) NextURL() string {
	p := r.GetPagination()
	return p.NextURL
}

// NextMaxID gets MaxID parameter that can be passed for next request.
func (r *Response) NextMaxID() string {
	p := r.GetPagination()
	return p.NextMaxID
}

// ResponseMeta represents information about the response. If all goes well,
// only a Code key with value 200 will present. However, sometimes things
// go wrong, and in that case ErrorType and ErrorMessage are present.
type ResponseMeta struct {
	ErrorType    string `json:"error_type,omitempty"`
	Code         int    `json:"code,omitempty"`
	ErrorMessage string `json:"error_message,omitempty"`
}

// ResponsePagination represents information to get access to more data in
// any request for sequential data.
type ResponsePagination struct {
	NextURL   string `json:"next_url,omitempty"`
	NextMaxID string `json:"next_max_id,omitempty"`
}

// NewClient returns a new Instagram API client. if a nil httpClient is
// provided, http.DefaultClient will be used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(BaseURL)

	c := &Client{
		client:    httpClient,
		BaseURL:   baseURL,
		UserAgent: UserAgent,
	}
	c.Users = &UsersService{client: c}
	c.Relationships = &RelationshipsService{client: c}
	c.Media = &MediaService{client: c}
	c.Comments = &CommentsService{client: c}
	c.Likes = &LikesService{client: c}
	c.Tags = &TagsService{client: c}
	c.Locations = &LocationsService{client: c}
	c.Geographies = &GeographiesService{client: c}
	c.Realtime = &RealtimeService{client: c}

	return c
}

func (c *Client) ComputeXInstaForwardedFor() string {
	if c.XInstaForwardedFor == "" {
		return ""
	}

	mac := hmac.New(sha256.New, []byte(c.ClientSecret))
	mac.Write([]byte(c.XInstaForwardedFor))

	return fmt.Sprintf("%s|%s", c.XInstaForwardedFor, hex.EncodeToString(mac.Sum(nil)))
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified
func (c *Client) NewRequest(method, urlStr string, body string) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)
	q := u.Query()
	if c.AccessToken != "" && q.Get("access_token") == "" {
		q.Set("access_token", c.AccessToken)
	}
	if c.ClientID != "" && q.Get("client_id") == "" {
		q.Set("client_id", c.ClientID)
	}
	if c.ClientSecret != "" && q.Get("client_secret") == "" {
		q.Set("client_secret", c.ClientSecret)
	}
	u.RawQuery = q.Encode()

	req, err := http.NewRequest(method, u.String(), bytes.NewBufferString(body))
	if err != nil {
		return nil, err
	}

	if method == "POST" {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	}

	if c.XInstaForwardedFor != "" {
		req.Header.Add("X-Insta-Forwarded-For", c.ComputeXInstaForwardedFor())
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// decoded and stored in the value pointed to by v, or returned as an error if
// an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	err = CheckResponse(resp)
	if err != nil {
		return resp, err
	}

	r := &Response{Response: resp}
	if v != nil {
		r.Data = v
		err = json.NewDecoder(resp.Body).Decode(r)
		c.Response = r
	}
	return resp, err
}

// InstagramError represents an error recieved from instagram
type InstagramError ResponseMeta

// Error makes the InstagramError suitable for the error interface
func (err *InstagramError) Error() string {
	return fmt.Sprintf("%s (%d): %s", err.ErrorType, err.Code, err.ErrorMessage)
}

// ErrorResponse represents a Response which contains an error
type ErrorResponse Response

func (r *ErrorResponse) Error() string {
	if r == nil {
		return fmt.Sprintf("A nil error response was returned")
	}

	if r.Response == nil || r.Response.Request == nil {
		return fmt.Sprintf("A nil error response was returned on %v", r)
	}

	if r.Response.Request.URL == nil {
		return fmt.Sprintf("A nil error response was returned on %v", r.Response.Request)
	}

	if r.Meta == nil {
		return fmt.Sprintf("%v %v: %d (no metadata)", r.Response.Request.Method, r.Response.Request.URL,
			r.Response.StatusCode)
	}

	return fmt.Sprintf("%v %v: %d %v %v",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode, r.Meta.ErrorType, r.Meta.ErrorMessage)
}

// CheckResponse checks the API response for error, and returns it
// if present. A response is considered an error if it has non StatusOK
// code.
func CheckResponse(r *http.Response) error {
	if r.StatusCode == http.StatusOK {
		return nil
	}

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

	// Forbidden: see http://instagram.com/developer/restrict-api-requests/
	if r.StatusCode == http.StatusForbidden {
		err := &InstagramError{}
		json.Unmarshal(data, &err)
		return err
	}

	// RateLimit: see http://instagram.com/developer/limits/
	if r.StatusCode == 429 {
		err := &InstagramError{}
		json.Unmarshal(data, &err)
		return err
	}

	// Sometimes Instagram returns 500 with plain message
	// "Oops, an error occurred.".
	if r.StatusCode == http.StatusInternalServerError {
		err := &InstagramError{
			ErrorType:    "Internal Server Error",
			Code:         http.StatusInternalServerError,
			ErrorMessage: "Oops, an error occurred.",
		}
		return err
	}

	if data != nil {
		// Unlike for successful (2XX) requests, unsuccessful
		// requests SOMETIMES have the {Meta: Error{}} format but
		// SOMETIMES they are just Error{}. From what I can tell, there is not
		// an obvious rationale behind what gets constructed in which way, so
		// we need to try both:
		err := &InstagramError{}
		json.Unmarshal(data, err)
		if *err != *new(InstagramError) {
			// Unmarshaling did something
			return err
		} else {
			// Unmarshaling did nothing for us, so the format was not Error{}.
			// We will assume the format was {Meta: Error{}}:
			temp := make(map[string]InstagramError)
			json.Unmarshal(data, &temp)

			meta := temp["meta"]

			delete(temp, "meta") // Probably uselesss
			return &meta
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package glisp

func Version() string {
	return "0.2.0"
}
<commit_msg>bump version string to 0.3.0<commit_after>package glisp

func Version() string {
	return "0.3.0"
}
<|endoftext|>