<commit_before>package main_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/influxdb/influxdb/client"
	"github.com/influxdb/influxdb/influxql"

	main "github.com/influxdb/influxdb/cmd/influxd"
)

// createCombinedNodeCluster creates a cluster of nServers nodes, each of which
// runs as both a Broker and Data node. If any part cluster creation fails,
// the testing is marked as failed.
func createCombinedNodeCluster(t *testing.T, testName string, nServers, basePort int) {
	t.Logf("Creating cluster of %d nodes for test %s", nServers, testName)

	if nServers == 0 {
		return
	}

	tmpDir := os.TempDir()
	tmpBrokerDir := filepath.Join(tmpDir, "broker-integration-test")
	tmpDataDir := filepath.Join(tmpDir, "data-integration-test")
	t.Logf("Test %s: using tmp directory %q for brokers\n", testName, tmpBrokerDir)
	t.Logf("Test %s: using tmp directory %q for data nodes\n", testName, tmpDataDir)
	// Sometimes if a test fails, it's because of a log.Fatal() in the program.
	// This prevents the defer from cleaning up directories.
	// To be safe, nuke them always before starting
	_ = os.RemoveAll(tmpBrokerDir)
	_ = os.RemoveAll(tmpDataDir)

	// Create the first node, special case.
	c := main.NewConfig()
	c.Broker.Dir = filepath.Join(tmpBrokerDir, strconv.Itoa(basePort))
	c.Data.Dir = filepath.Join(tmpDataDir, strconv.Itoa(basePort))
	c.Broker.Port = basePort
	c.Data.Port = basePort
	s := main.Run(c, "", "x.x", os.Stderr)
	if s == nil {
		t.Fatalf("Test %s: failed to create node on port %d", testName, basePort)
	}
}

// simpleWriteAndQuery creates a simple database, retention policy, and replicates
// the data across all nodes. It then ensures a series of writes and queries are OK.
func simpleWriteAndQuery(t *testing.T, testname string, serverURL *url.URL, nServers int) {
	now := time.Now().UTC()

	// Create a database
	t.Log("Creating database")
	u := urlFor(serverURL, "query", url.Values{"q": []string{"CREATE DATABASE foo"}})
	resp, err := http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't create database: %s", err)
	}
	defer resp.Body.Close()

	var results client.Results
	err = json.NewDecoder(resp.Body).Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Create database failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	if len(results.Results) != 1 {
		t.Fatalf("Create database failed.  Unexpected results length.  expected: %d, actual %d", 1, len(results.Results))
	}

	// Query the database exists
	u = urlFor(serverURL, "query", url.Values{"q": []string{"SHOW DATABASES"}})
	resp, err = http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't query databases: %s", err)
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("show databases failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	expectedResults := client.Results{
		Results: []client.Result{
			{Rows: []influxql.Row{
				influxql.Row{
					Columns: []string{"name"},
					Values:  [][]interface{}{{"foo"}},
				},
			}},
		},
	}
	if !reflect.DeepEqual(results, expectedResults) {
		t.Fatalf("show databases failed.  Unexpected results.  expected: %+v, actual %+v", expectedResults, results)
	}

	// Create a retention policy
	t.Log("Creating retention policy")
	replication := fmt.Sprintf("CREATE RETENTION POLICY bar ON foo DURATION 1h REPLICATION %d DEFAULT", nServers)
	u = urlFor(serverURL, "query", url.Values{"q": []string{replication}})
	resp, err = http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't create retention policy: %s", err)
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Create retention policy failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	if len(results.Results) != 1 {
		t.Fatalf("Create retention policy failed.  Unexpected results length.  expected: %d, actual %d", 1, len(results.Results))
	}

	// TODO corylanou: Query the retention policy exists

	// Write Data
	t.Log("Write data")
	u = urlFor(serverURL, "write", url.Values{})

	buf := []byte(fmt.Sprintf(`{"database" : "foo", "retentionPolicy" : "bar", "points": [{"name": "cpu", "tags": {"host": "server01"},"timestamp": %d, "precision":"n","values": {"value": 100}}]}`, now.UnixNano()))
	t.Logf("Writing raw data: %s", string(buf))
	resp, err = http.Post(u.String(), "application/json", bytes.NewReader(buf))
	if err != nil {
		t.Fatalf("Couldn't write data: %s", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Write to database failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	// Need some time for server to get consensus and write data
	// TODO corylanou query the status endpoint for the server and wait for the index to update to know the write was applied
	time.Sleep(100 * time.Millisecond)

	// Query the data exists
	t.Log("Query data")
	u = urlFor(serverURL, "query", url.Values{"q": []string{`select value from "foo"."bar".cpu`}, "db": []string{"foo"}})
	resp, err = http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't query databases: %s", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Couldn't read body of response: %s", err)
	}
	t.Logf("resp.Body: %s\n", string(body))

	dec := json.NewDecoder(bytes.NewReader(body))
	dec.UseNumber()
	err = dec.Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("query databases failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	expectedResults = client.Results{
		Results: []client.Result{
			{Rows: []influxql.Row{
				{
					Name:    "cpu",
					Columns: []string{"time", "value"},
					Values: [][]interface{}{
						[]interface{}{now.Format(time.RFC3339Nano), json.Number("100")},
					},
				}}},
		},
	}

	if !reflect.DeepEqual(results, expectedResults) {
		t.Logf("Expected:\n")
		t.Logf("%#v\n", expectedResults)
		t.Logf("Actual:\n")
		t.Logf("%#v\n", results)
		t.Fatalf("query databases failed.  Unexpected results.")
	}
}

func Test_ServerSingleIntegration(t *testing.T) {
	createCombinedNodeCluster(t, "single node", 1, 8090)
	serverURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:8090",
	}
	simpleWriteAndQuery(t, "single node", serverURL, 1)
}

func urlFor(u *url.URL, path string, params url.Values) *url.URL {
	u.Path = path
	u.RawQuery = params.Encode()
	return u
}
<commit_msg>Add 3-node cluster test<commit_after>package main_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/influxdb/influxdb/client"
	"github.com/influxdb/influxdb/influxql"

	main "github.com/influxdb/influxdb/cmd/influxd"
)

// createCombinedNodeCluster creates a cluster of nServers nodes, each of which
// runs as both a Broker and Data node. If any part cluster creation fails,
// the testing is marked as failed.
func createCombinedNodeCluster(t *testing.T, testName string, nServers, basePort int) {
	t.Logf("Creating cluster of %d nodes for test %s", nServers, testName)

	if nServers == 0 {
		return
	}

	tmpDir := os.TempDir()
	tmpBrokerDir := filepath.Join(tmpDir, "broker-integration-test")
	tmpDataDir := filepath.Join(tmpDir, "data-integration-test")
	t.Logf("Test %s: using tmp directory %q for brokers\n", testName, tmpBrokerDir)
	t.Logf("Test %s: using tmp directory %q for data nodes\n", testName, tmpDataDir)
	// Sometimes if a test fails, it's because of a log.Fatal() in the program.
	// This prevents the defer from cleaning up directories.
	// To be safe, nuke them always before starting
	_ = os.RemoveAll(tmpBrokerDir)
	_ = os.RemoveAll(tmpDataDir)

	// Create the first node, special case.
	c := main.NewConfig()
	c.Broker.Dir = filepath.Join(tmpBrokerDir, strconv.Itoa(basePort))
	c.Data.Dir = filepath.Join(tmpDataDir, strconv.Itoa(basePort))
	c.Broker.Port = basePort
	c.Data.Port = basePort

	s := main.Run(c, "", "x.x", os.Stderr)
	if s == nil {
		t.Fatalf("Test %s: failed to create leader node on port %d", testName, basePort)
	}

	// Create subsequent nodes, which join to first node.
	for i := 1; i < nServers; i++ {
		nextPort := basePort + i
		c.Broker.Dir = filepath.Join(tmpBrokerDir, strconv.Itoa(nextPort))
		c.Data.Dir = filepath.Join(tmpDataDir, strconv.Itoa(nextPort))
		c.Broker.Port = nextPort
		c.Data.Port = nextPort

		s := main.Run(c, "http://localhost:"+strconv.Itoa(basePort), "x.x", os.Stderr)
		if s == nil {
			t.Fatalf("Test %s: failed to create following node on port %d", testName, basePort)
		}

		time.Sleep(3 * time.Second)
	}
}

// simpleWriteAndQuery creates a simple database, retention policy, and replicates
// the data across all nodes. It then ensures a series of writes and queries are OK.
func simpleWriteAndQuery(t *testing.T, testname string, serverURL *url.URL, nServers int) {
	now := time.Now().UTC()

	// Create a database
	t.Log("Creating database")
	u := urlFor(serverURL, "query", url.Values{"q": []string{"CREATE DATABASE foo"}})
	resp, err := http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't create database: %s", err)
	}
	defer resp.Body.Close()

	var results client.Results
	err = json.NewDecoder(resp.Body).Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Create database failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	if len(results.Results) != 1 {
		t.Fatalf("Create database failed.  Unexpected results length.  expected: %d, actual %d", 1, len(results.Results))
	}

	// Query the database exists
	u = urlFor(serverURL, "query", url.Values{"q": []string{"SHOW DATABASES"}})
	resp, err = http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't query databases: %s", err)
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("show databases failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	expectedResults := client.Results{
		Results: []client.Result{
			{Rows: []influxql.Row{
				influxql.Row{
					Columns: []string{"name"},
					Values:  [][]interface{}{{"foo"}},
				},
			}},
		},
	}
	if !reflect.DeepEqual(results, expectedResults) {
		t.Fatalf("show databases failed.  Unexpected results.  expected: %+v, actual %+v", expectedResults, results)
	}

	// Create a retention policy
	t.Log("Creating retention policy")
	replication := fmt.Sprintf("CREATE RETENTION POLICY bar ON foo DURATION 1h REPLICATION %d DEFAULT", nServers)
	u = urlFor(serverURL, "query", url.Values{"q": []string{replication}})
	resp, err = http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't create retention policy: %s", err)
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Create retention policy failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	if len(results.Results) != 1 {
		t.Fatalf("Create retention policy failed.  Unexpected results length.  expected: %d, actual %d", 1, len(results.Results))
	}

	// TODO corylanou: Query the retention policy exists

	// Write Data
	t.Log("Write data")
	u = urlFor(serverURL, "write", url.Values{})

	buf := []byte(fmt.Sprintf(`{"database" : "foo", "retentionPolicy" : "bar", "points": [{"name": "cpu", "tags": {"host": "server01"},"timestamp": %d, "precision":"n","values": {"value": 100}}]}`, now.UnixNano()))
	t.Logf("Writing raw data: %s", string(buf))
	resp, err = http.Post(u.String(), "application/json", bytes.NewReader(buf))
	if err != nil {
		t.Fatalf("Couldn't write data: %s", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Write to database failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	// Need some time for server to get consensus and write data
	// TODO corylanou query the status endpoint for the server and wait for the index to update to know the write was applied
	time.Sleep(100 * time.Millisecond)

	// Query the data exists
	t.Log("Query data")
	u = urlFor(serverURL, "query", url.Values{"q": []string{`select value from "foo"."bar".cpu`}, "db": []string{"foo"}})
	resp, err = http.Get(u.String())
	if err != nil {
		t.Fatalf("Couldn't query databases: %s", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Couldn't read body of response: %s", err)
	}
	t.Logf("resp.Body: %s\n", string(body))

	dec := json.NewDecoder(bytes.NewReader(body))
	dec.UseNumber()
	err = dec.Decode(&results)
	if err != nil {
		t.Fatalf("Couldn't decode results: %v", err)
	}

	if results.Error() != nil {
		t.Logf("results.Error(): %q", results.Error().Error())
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("query databases failed.  Unexpected status code.  expected: %d, actual %d", http.StatusOK, resp.StatusCode)
	}

	expectedResults = client.Results{
		Results: []client.Result{
			{Rows: []influxql.Row{
				{
					Name:    "cpu",
					Columns: []string{"time", "value"},
					Values: [][]interface{}{
						[]interface{}{now.Format(time.RFC3339Nano), json.Number("100")},
					},
				}}},
		},
	}

	if !reflect.DeepEqual(results, expectedResults) {
		t.Logf("Expected:\n")
		t.Logf("%#v\n", expectedResults)
		t.Logf("Actual:\n")
		t.Logf("%#v\n", results)
		t.Fatalf("query databases failed.  Unexpected results.")
	}
}

func Test_ServerSingleIntegration(t *testing.T) {
	createCombinedNodeCluster(t, "single node", 1, 8090)

	serverURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:8090",
	}
	simpleWriteAndQuery(t, "single node", serverURL, 1)
}

func Test_Server3NodeIntegration(t *testing.T) {
	createCombinedNodeCluster(t, "3 node", 3, 8090)

	serverURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:8090",
	}
	simpleWriteAndQuery(t, "3 node", serverURL, 3)
}

func urlFor(u *url.URL, path string, params url.Values) *url.URL {
	u.Path = path
	u.RawQuery = params.Encode()
	return u
}
<|endoftext|>


<commit_before>package router

import (
	"fmt"
	"os"
	"sort"
	"testing"

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

type SortableRules []Rule

func (r SortableRules) Len() int {
	return len(r)
}
func (r SortableRules) Less(i, j int) bool {
	return r[i].Name < r[j].Name
}
func (r SortableRules) Swap(i, j int) {
	tmp := r[j]
	r[j] = r[i]
	r[i] = tmp
}

func TestParsesWellFormatedConfig(t *testing.T) {
	conf := []byte(`
routes:
  rule-one:
    matchers:
      title: ["authorize-app"]
    output:
      type: "notifications"
      channel: "#team"
      icon: ":rocket:"
      message: "authorized %{foo.bar} in ${SCHOOL}"
      user: "@fishman"
  rule-two:
    matchers:
      foo.bar: ["multiple", "matches"]
      baz: ["whatever"]
    output:
      type: "alerts"
      series: "other-series"
      dimensions: ["baz"]
      stat_type: "gauge"
`)
	expected := SortableRules{
		Rule{
			Name:     "rule-one",
			Matchers: RuleMatchers{"title": []string{"authorize-app"}},
			Output: RuleOutput{
				"type":    "notifications",
				"channel": "#team",
				"icon":    ":rocket:",
				"message": `authorized %{foo.bar} in Hogwarts`,
				"user":    "@fishman",
			},
		},
		Rule{
			Name: "rule-two",
			Matchers: RuleMatchers{
				"foo.bar": []string{"multiple", "matches"},
				"baz":     []string{"whatever"},
			},
			Output: RuleOutput{
				"type":       "alerts",
				"series":     "other-series",
				"dimensions": []interface{}{"baz"},
				"stat_type":  "gauge",
			},
		},
	}

	err := os.Setenv("SCHOOL", "Hogwarts")
	assert.Nil(t, err)

	router, err := newFromConfigBytes(conf)
	assert.Nil(t, err)

	r, ok := router.(*RuleRouter)
	assert.True(t, ok)
	actual := SortableRules(r.rules)
	sort.Sort(expected)
	sort.Sort(actual)
	assert.Equal(t, expected, actual)
}

func TestOnlyNonemptyStringMatcherValues(t *testing.T) {
	confTmpl := `
routes:
  non-string-values:
    matchers:
      no-numbers: [%s]
    output:
      type: "analytics"
      series: "fun"
`

	// Make sure the template works
	conf := []byte(fmt.Sprintf(confTmpl, "\"valid\""))
	_, err := newFromConfigBytes(conf)
	assert.Nil(t, err)

	for _, invalidVal := range []string{"5", "true", "[]", "{}", `""`} {
		conf := []byte(fmt.Sprintf(confTmpl, invalidVal))
		_, err := newFromConfigBytes(conf)
		assert.Error(t, err)
	}
}

func TestNoSpecialsInMatcher(t *testing.T) {
	confFieldTmpl := `
routes:
  complicated-fields:
    matchers:
      "%s": ["hallo?"]
    output:
      type: "analytics"
      series: "fun"
`
	confValTmpl := `
routes:
  complicated-values:
    matchers:
      title: ["%s"]
    output:
      type: "analytics"
      series: "fun"
`

	// Make sure templates work
	for _, tmpl := range []string{confFieldTmpl, confValTmpl} {
		conf := []byte(fmt.Sprintf(tmpl, "valid"))
		_, err := newFromConfigBytes(conf)
		assert.Nil(t, err)
	}

	invalids := []string{"${wut}", "%{wut}", "$huh", "}ok?", "nope{", `100% fail`}
	for _, invalid := range invalids {
		for _, tmpl := range []string{confFieldTmpl, confValTmpl} {
			conf := []byte(fmt.Sprintf(tmpl, invalid))
			_, err := newFromConfigBytes(conf)
			assert.Error(t, err)
		}
	}
}

func TestNoDupMatchers(t *testing.T) {
	confTmpl := `
routes:
  sloppy:
    matchers:
      title: [%s]
    output:
      type: "analytics"
      series: "fun"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `"non-repeated", "name"`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf := []byte(fmt.Sprintf(confTmpl, `"repeated", "repeated", "name"`))
	_, err = newFromConfigBytes(invalidConf)
	assert.Error(t, err)
}

func TestErrorsThrownWithTypeOCaught(t *testing.T) {
	assert := assert.New(t)

	config := `
route: # Shouldn't routes (plural)
  non-string-values:
    matchers:
      errors: [ "type-o" ]
    output:
      type: "analytics"
      series: "fun"
`
	_, err := newFromConfigBytes([]byte(config))
	assert.Error(err)

	config = `
routes:
  non-string-values:
    matcher: # Shouldn't matches (plural)
      errors: [ "type-o" ]
    output:
      type: "analytics"
      series: "fun"
`
	_, err = newFromConfigBytes([]byte(config))
	assert.Error(err)

	config = `
routes:
  $non-string-values: # Invalid rule name
    matchers:
      errors: [ "type-o" ]
    output:
      type: "analytics"
      series: "fun"
`
	_, err = newFromConfigBytes([]byte(config))
	assert.Error(err)

	config = `
routes:
  $non-string-values: # Invalid rule name
    matchers:
      errors: [ "type-o" ]
    outputs: # Should be output (signular)
      type: "analytics"
      series: "fun"
`
	_, err = newFromConfigBytes([]byte(config))
	assert.Error(err)
}

func TestOutputRequiresCorrectTypes(t *testing.T) {
	confTmpl := `
routes:
  wrong:
    matchers:
      title: ["test"]
    output:
      type: "alerts"
      series: %s
      dimensions: %s
      stat_type: "gauge"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `"my-series"`, `["dim1", "dim2"]`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf0 := []byte(fmt.Sprintf(confTmpl, `["my-series"]`, `["dim1", "dim2"]`))
	_, err = newFromConfigBytes(invalidConf0)
	assert.Error(t, err)

	invalidConf1 := []byte(fmt.Sprintf(confTmpl, `"my-series"`, `"dim1"`))
	_, err = newFromConfigBytes(invalidConf1)
	assert.Error(t, err)
}

func TestOutputRequiresAllKeys(t *testing.T) {
	confTmpl := `
routes:
  wrong:
    matchers:
      title: ["test"]
    output:
      type: "alerts"%s
      dimensions: ["dim1", "dim2"]
      stat_type: "gauge"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `
      series: "whatever"`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf := []byte(fmt.Sprintf(confTmpl, ``))
	_, err = newFromConfigBytes(invalidConf)
	assert.Error(t, err)
}

func TestOutputNoExtraKeysAllowed(t *testing.T) {
	confTmpl := `
routes:
  wrong:
    matchers:
      title: ["test"]
    output:
      type: "alerts"%s
      dimensions: ["dim1", "dim2"]
      stat_type: "gauge"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `
      series: "whatever"`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf := []byte(fmt.Sprintf(confTmpl, `
      series: "whatever"
      something-else: "hi there"`))
	_, err = newFromConfigBytes(invalidConf)
	assert.Error(t, err)
}
<commit_msg>Fixed type-os in unit tests<commit_after>package router

import (
	"fmt"
	"os"
	"sort"
	"testing"

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

type SortableRules []Rule

func (r SortableRules) Len() int {
	return len(r)
}
func (r SortableRules) Less(i, j int) bool {
	return r[i].Name < r[j].Name
}
func (r SortableRules) Swap(i, j int) {
	tmp := r[j]
	r[j] = r[i]
	r[i] = tmp
}

func TestParsesWellFormatedConfig(t *testing.T) {
	conf := []byte(`
routes:
  rule-one:
    matchers:
      title: ["authorize-app"]
    output:
      type: "notifications"
      channel: "#team"
      icon: ":rocket:"
      message: "authorized %{foo.bar} in ${SCHOOL}"
      user: "@fishman"
  rule-two:
    matchers:
      foo.bar: ["multiple", "matches"]
      baz: ["whatever"]
    output:
      type: "alerts"
      series: "other-series"
      dimensions: ["baz"]
      stat_type: "gauge"
`)
	expected := SortableRules{
		Rule{
			Name:     "rule-one",
			Matchers: RuleMatchers{"title": []string{"authorize-app"}},
			Output: RuleOutput{
				"type":    "notifications",
				"channel": "#team",
				"icon":    ":rocket:",
				"message": `authorized %{foo.bar} in Hogwarts`,
				"user":    "@fishman",
			},
		},
		Rule{
			Name: "rule-two",
			Matchers: RuleMatchers{
				"foo.bar": []string{"multiple", "matches"},
				"baz":     []string{"whatever"},
			},
			Output: RuleOutput{
				"type":       "alerts",
				"series":     "other-series",
				"dimensions": []interface{}{"baz"},
				"stat_type":  "gauge",
			},
		},
	}

	err := os.Setenv("SCHOOL", "Hogwarts")
	assert.Nil(t, err)

	router, err := newFromConfigBytes(conf)
	assert.Nil(t, err)

	r, ok := router.(*RuleRouter)
	assert.True(t, ok)
	actual := SortableRules(r.rules)
	sort.Sort(expected)
	sort.Sort(actual)
	assert.Equal(t, expected, actual)
}

func TestOnlyNonemptyStringMatcherValues(t *testing.T) {
	confTmpl := `
routes:
  non-string-values:
    matchers:
      no-numbers: [%s]
    output:
      type: "analytics"
      series: "fun"
`

	// Make sure the template works
	conf := []byte(fmt.Sprintf(confTmpl, "\"valid\""))
	_, err := newFromConfigBytes(conf)
	assert.Nil(t, err)

	for _, invalidVal := range []string{"5", "true", "[]", "{}", `""`} {
		conf := []byte(fmt.Sprintf(confTmpl, invalidVal))
		_, err := newFromConfigBytes(conf)
		assert.Error(t, err)
	}
}

func TestNoSpecialsInMatcher(t *testing.T) {
	confFieldTmpl := `
routes:
  complicated-fields:
    matchers:
      "%s": ["hallo?"]
    output:
      type: "analytics"
      series: "fun"
`
	confValTmpl := `
routes:
  complicated-values:
    matchers:
      title: ["%s"]
    output:
      type: "analytics"
      series: "fun"
`

	// Make sure templates work
	for _, tmpl := range []string{confFieldTmpl, confValTmpl} {
		conf := []byte(fmt.Sprintf(tmpl, "valid"))
		_, err := newFromConfigBytes(conf)
		assert.Nil(t, err)
	}

	invalids := []string{"${wut}", "%{wut}", "$huh", "}ok?", "nope{", `100% fail`}
	for _, invalid := range invalids {
		for _, tmpl := range []string{confFieldTmpl, confValTmpl} {
			conf := []byte(fmt.Sprintf(tmpl, invalid))
			_, err := newFromConfigBytes(conf)
			assert.Error(t, err)
		}
	}
}

func TestNoDupMatchers(t *testing.T) {
	confTmpl := `
routes:
  sloppy:
    matchers:
      title: [%s]
    output:
      type: "analytics"
      series: "fun"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `"non-repeated", "name"`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf := []byte(fmt.Sprintf(confTmpl, `"repeated", "repeated", "name"`))
	_, err = newFromConfigBytes(invalidConf)
	assert.Error(t, err)
}

func TestErrorsThrownWithTypeOCaught(t *testing.T) {
	assert := assert.New(t)

	config := `
route: # Shouldn't routes (plural)
  string-values:
    matchers:
      errors: [ "type-o" ]
    output:
      type: "analytics"
      series: "fun"
`
	_, err := newFromConfigBytes([]byte(config))
	assert.Error(err)

	config = `
routes:
  string-values:
    matcher: # Shouldn't matches (plural)
      errors: [ "type-o" ]
    output:
      type: "analytics"
      series: "fun"
`
	_, err = newFromConfigBytes([]byte(config))
	assert.Error(err)

	config = `
routes:
  string-values:
    matchers:
      errors: [ "type-o" ]
    outputs: # Should be output (singular)
      type: "analytics"
      series: "fun"
`
	_, err = newFromConfigBytes([]byte(config))
	assert.Error(err)

	config = `
routes:
  $invalid-string-values: # Invalid rule name
    matchers:
      errors: [ "type-o" ]
    output:
      type: "analytics"
      series: "fun"
`
	_, err = newFromConfigBytes([]byte(config))
	assert.Error(err)
}

func TestOutputRequiresCorrectTypes(t *testing.T) {
	confTmpl := `
routes:
  wrong:
    matchers:
      title: ["test"]
    output:
      type: "alerts"
      series: %s
      dimensions: %s
      stat_type: "gauge"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `"my-series"`, `["dim1", "dim2"]`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf0 := []byte(fmt.Sprintf(confTmpl, `["my-series"]`, `["dim1", "dim2"]`))
	_, err = newFromConfigBytes(invalidConf0)
	assert.Error(t, err)

	invalidConf1 := []byte(fmt.Sprintf(confTmpl, `"my-series"`, `"dim1"`))
	_, err = newFromConfigBytes(invalidConf1)
	assert.Error(t, err)
}

func TestOutputRequiresAllKeys(t *testing.T) {
	confTmpl := `
routes:
  wrong:
    matchers:
      title: ["test"]
    output:
      type: "alerts"%s
      dimensions: ["dim1", "dim2"]
      stat_type: "gauge"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `
      series: "whatever"`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf := []byte(fmt.Sprintf(confTmpl, ``))
	_, err = newFromConfigBytes(invalidConf)
	assert.Error(t, err)
}

func TestOutputNoExtraKeysAllowed(t *testing.T) {
	confTmpl := `
routes:
  wrong:
    matchers:
      title: ["test"]
    output:
      type: "alerts"%s
      dimensions: ["dim1", "dim2"]
      stat_type: "gauge"
`

	validConf := []byte(fmt.Sprintf(confTmpl, `
      series: "whatever"`))
	_, err := newFromConfigBytes(validConf)
	assert.Nil(t, err)

	invalidConf := []byte(fmt.Sprintf(confTmpl, `
      series: "whatever"
      something-else: "hi there"`))
	_, err = newFromConfigBytes(invalidConf)
	assert.Error(t, err)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package user

import (
	"fmt"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
	"github.com/gogits/gogs/modules/setting"
)

const (
	DASHBOARD base.TplName = "user/dashboard/dashboard"
	PULLS     base.TplName = "user/dashboard/pulls"
	ISSUES    base.TplName = "user/issues"
	STARS     base.TplName = "user/stars"
	PROFILE   base.TplName = "user/profile"
)

func Dashboard(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("dashboard")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsNews"] = true

	var ctxUser *models.User
	// Check context type.
	orgName := ctx.Params(":org")
	if len(orgName) > 0 {
		// Organization.
		org, err := models.GetUserByName(orgName)
		if err != nil {
			if err == models.ErrUserNotExist {
				ctx.Handle(404, "GetUserByName", err)
			} else {
				ctx.Handle(500, "GetUserByName", err)
			}
			return
		}
		ctxUser = org
	} else {
		// Normal user.
		ctxUser = ctx.User
		collaborates, err := models.GetCollaborativeRepos(ctxUser.Name)
		if err != nil {
			ctx.Handle(500, "GetCollaborativeRepos", err)
			return
		}
		ctx.Data["CollaborateCount"] = len(collaborates)
		ctx.Data["CollaborativeRepos"] = collaborates
	}
	ctx.Data["ContextUser"] = ctxUser

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	repos, err := models.GetRepositories(ctxUser.Id, true)
	if err != nil {
		ctx.Handle(500, "GetRepositories", err)
		return
	}
	ctx.Data["Repos"] = repos

	// Get mirror repositories.
	mirrors := make([]*models.Repository, 0, len(repos)/2)
	for _, repo := range repos {
		if repo.IsMirror {
			if err = repo.GetMirror(); err != nil {
				ctx.Handle(500, "GetMirror: "+repo.Name, err)
				return
			}
			mirrors = append(mirrors, repo)
		}
	}
	ctx.Data["MirrorCount"] = len(mirrors)
	ctx.Data["Mirrors"] = mirrors

	// Get feeds.
	actions, err := models.GetFeeds(ctxUser.Id, 0, false)
	if err != nil {
		ctx.Handle(500, "GetFeeds", err)
		return
	}

	// Check access of private repositories.
	feeds := make([]*models.Action, 0, len(actions))
	for _, act := range actions {
		if act.IsPrivate {
			if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
				models.READABLE); !has {
				continue
			}
		}
		// FIXME: cache results?
		u, err := models.GetUserByName(act.ActUserName)
		if err != nil {
			ctx.Handle(500, "GetUserByName", err)
			return
		}
		act.ActAvatar = u.AvatarLink()
		feeds = append(feeds, act)
	}
	ctx.Data["Feeds"] = feeds
	ctx.HTML(200, DASHBOARD)
}

func Pulls(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("pull_requests")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsPulls"] = true

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, PULLS)
}

func Profile(ctx *middleware.Context) {
	ctx.Data["Title"] = "Profile"
	ctx.Data["PageIsUserProfile"] = true

	uname := ctx.Params(":username")
	// Special handle for FireFox requests favicon.ico.
	if uname == "favicon.ico" {
		ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
		return
	}

	u, err := models.GetUserByName(uname)
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "GetUserByName", err)
		} else {
			ctx.Handle(500, "GetUserByName", err)
		}
		return
	}

	if u.IsOrganization() {
		ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
		return
	}

	// For security reason, hide e-mail address for anonymous visitors.
	if !ctx.IsSigned {
		u.Email = ""
	}
	ctx.Data["Owner"] = u

	tab := ctx.Query("tab")
	ctx.Data["TabName"] = tab
	switch tab {
	case "activity":
		ctx.Data["Feeds"], err = models.GetFeeds(u.Id, 0, true)
		if err != nil {
			ctx.Handle(500, "GetFeeds", err)
			return
		}
	default:
		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
		if err != nil {
			ctx.Handle(500, "GetRepositories", err)
			return
		}
	}

	ctx.HTML(200, PROFILE)
}

func Email2User(ctx *middleware.Context) {
	u, err := models.GetUserByEmail(ctx.Query("email"))
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "user.Email2User(GetUserByEmail)", err)
		} else {
			ctx.Handle(500, "user.Email2User(GetUserByEmail)", err)
		}
		return
	}
	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
}

const (
	TPL_FEED = `

%s
%s
`
)

// func Feeds(ctx *middleware.Context, form auth.FeedsForm) {
// 	actions, err := models.GetFeeds(form.UserId, form.Page*20, false)
// 	if err != nil {
// 		ctx.JSON(500, err)
// 		return
// 	}

// 	feeds := make([]string, 0, len(actions))
// 	for _, act := range actions {
// 		if act.IsPrivate {
// 			if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
// 				models.READABLE); !has {
// 				continue
// 			}
// 		}
// 		feeds = append(feeds, fmt.Sprintf(TPL_FEED, base.ActionIcon(act.OpType),
// 			base.TimeSince(act.Created), base.ActionDesc(act)))
// 	}
// 	ctx.JSON(200, &feeds)
// }

func Issues(ctx *middleware.Context) {
	ctx.Data["Title"] = "Your Issues"

	viewType := ctx.Query("type")
	types := []string{"assigned", "created_by"}
	if !com.IsSliceContainsStr(types, viewType) {
		viewType = "all"
	}

	isShowClosed := ctx.Query("state") == "closed"

	var filterMode int
	switch viewType {
	case "assigned":
		filterMode = models.FM_ASSIGN
	case "created_by":
		filterMode = models.FM_CREATE
	}

	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)

	// Get all repositories.
	repos, err := models.GetRepositories(ctx.User.Id, true)
	if err != nil {
		ctx.Handle(500, "user.Issues(GetRepositories)", err)
		return
	}

	repoIds := make([]int64, 0, len(repos))
	showRepos := make([]*models.Repository, 0, len(repos))
	for _, repo := range repos {
		if repo.NumIssues == 0 {
			continue
		}

		repoIds = append(repoIds, repo.Id)
		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
		issueStats.AllCount += int64(repo.NumOpenIssues)

		if isShowClosed {
			if repo.NumClosedIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		} else {
			if repo.NumOpenIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		}
	}

	if repoId > 0 {
		repoIds = []int64{repoId}
	}

	page, _ := com.StrTo(ctx.Query("page")).Int()

	// Get all issues.
	var ius []*models.IssueUser
	switch viewType {
	case "assigned":
		fallthrough
	case "created_by":
		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
	default:
		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
	}
	if err != nil {
		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
		return
	}

	issues := make([]*models.Issue, len(ius))
	for i := range ius {
		issues[i], err = models.GetIssueById(ius[i].IssueId)
		if err != nil {
			if err == models.ErrIssueNotExist {
				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueId), err)
				return
			}
		}

		issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId)
		if err != nil {
			if err == models.ErrRepoNotExist {
				log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err)
				return
			}
		}

		if err = issues[i].Repo.GetOwner(); err != nil {
			ctx.Handle(500, "user.Issues(GetOwner)", err)
			return
		}

		if err = issues[i].GetPoster(); err != nil {
			ctx.Handle(500, "user.Issues(GetUserById)", err)
			return
		}
	}

	ctx.Data["RepoId"] = repoId
	ctx.Data["Repos"] = showRepos
	ctx.Data["Issues"] = issues
	ctx.Data["ViewType"] = viewType
	ctx.Data["IssueStats"] = issueStats
	ctx.Data["IsShowClosed"] = isShowClosed
	if isShowClosed {
		ctx.Data["State"] = "closed"
		ctx.Data["ShowCount"] = issueStats.ClosedCount
	} else {
		ctx.Data["ShowCount"] = issueStats.OpenCount
	}
	ctx.HTML(200, ISSUES)
}
<commit_msg>fix #139<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package user

import (
	"fmt"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
	"github.com/gogits/gogs/modules/setting"
)

const (
	DASHBOARD base.TplName = "user/dashboard/dashboard"
	PULLS     base.TplName = "user/dashboard/pulls"
	ISSUES    base.TplName = "user/issues"
	STARS     base.TplName = "user/stars"
	PROFILE   base.TplName = "user/profile"
)

func Dashboard(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("dashboard")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsNews"] = true

	var ctxUser *models.User
	// Check context type.
	orgName := ctx.Params(":org")
	if len(orgName) > 0 {
		// Organization.
		org, err := models.GetUserByName(orgName)
		if err != nil {
			if err == models.ErrUserNotExist {
				ctx.Handle(404, "GetUserByName", err)
			} else {
				ctx.Handle(500, "GetUserByName", err)
			}
			return
		}
		ctxUser = org
	} else {
		// Normal user.
		ctxUser = ctx.User
		collaborates, err := models.GetCollaborativeRepos(ctxUser.Name)
		if err != nil {
			ctx.Handle(500, "GetCollaborativeRepos", err)
			return
		}
		ctx.Data["CollaborateCount"] = len(collaborates)
		ctx.Data["CollaborativeRepos"] = collaborates
	}
	ctx.Data["ContextUser"] = ctxUser

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	repos, err := models.GetRepositories(ctxUser.Id, true)
	if err != nil {
		ctx.Handle(500, "GetRepositories", err)
		return
	}
	ctx.Data["Repos"] = repos

	// Get mirror repositories.
	mirrors := make([]*models.Repository, 0, len(repos)/2)
	for _, repo := range repos {
		if repo.IsMirror {
			if err = repo.GetMirror(); err != nil {
				ctx.Handle(500, "GetMirror: "+repo.Name, err)
				return
			}
			mirrors = append(mirrors, repo)
		}
	}
	ctx.Data["MirrorCount"] = len(mirrors)
	ctx.Data["Mirrors"] = mirrors

	// Get feeds.
	actions, err := models.GetFeeds(ctxUser.Id, 0, false)
	if err != nil {
		ctx.Handle(500, "GetFeeds", err)
		return
	}

	// Check access of private repositories.
	feeds := make([]*models.Action, 0, len(actions))
	for _, act := range actions {
		if act.IsPrivate {
			if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
				models.READABLE); !has {
				continue
			}
		}
		// FIXME: cache results?
		u, err := models.GetUserByName(act.ActUserName)
		if err != nil {
			ctx.Handle(500, "GetUserByName", err)
			return
		}
		act.ActAvatar = u.AvatarLink()
		feeds = append(feeds, act)
	}
	ctx.Data["Feeds"] = feeds
	ctx.HTML(200, DASHBOARD)
}

func Pulls(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("pull_requests")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsPulls"] = true

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, PULLS)
}

func Profile(ctx *middleware.Context) {
	ctx.Data["Title"] = "Profile"
	ctx.Data["PageIsUserProfile"] = true

	uname := ctx.Params(":username")
	// Special handle for FireFox requests favicon.ico.
	if uname == "favicon.ico" {
		ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
		return
	}

	u, err := models.GetUserByName(uname)
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "GetUserByName", err)
		} else {
			ctx.Handle(500, "GetUserByName", err)
		}
		return
	}

	if u.IsOrganization() {
		ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
		return
	}

	// For security reason, hide e-mail address for anonymous visitors.
	if !ctx.IsSigned {
		u.Email = ""
	}
	ctx.Data["Owner"] = u

	tab := ctx.Query("tab")
	ctx.Data["TabName"] = tab
	switch tab {
	case "activity":
		actions, err := models.GetFeeds(u.Id, 0, false)
		if err != nil {
			ctx.Handle(500, "GetFeeds", err)
			return
		}
		feeds := make([]*models.Action, 0, len(actions))
		for _, act := range actions {
			// FIXME: cache results?
			u, err := models.GetUserByName(act.ActUserName)
			if err != nil {
				ctx.Handle(500, "GetUserByName", err)
				return
			}
			act.ActAvatar = u.AvatarLink()
			feeds = append(feeds, act)
		}
		ctx.Data["Feeds"] = feeds
	default:
		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
		if err != nil {
			ctx.Handle(500, "GetRepositories", err)
			return
		}
	}

	ctx.HTML(200, PROFILE)
}

func Email2User(ctx *middleware.Context) {
	u, err := models.GetUserByEmail(ctx.Query("email"))
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "user.Email2User(GetUserByEmail)", err)
		} else {
			ctx.Handle(500, "user.Email2User(GetUserByEmail)", err)
		}
		return
	}
	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
}

const (
	TPL_FEED = `

%s
%s
`
)

// func Feeds(ctx *middleware.Context, form auth.FeedsForm) {
// 	actions, err := models.GetFeeds(form.UserId, form.Page*20, false)
// 	if err != nil {
// 		ctx.JSON(500, err)
// 		return
// 	}

// 	feeds := make([]string, 0, len(actions))
// 	for _, act := range actions {
// 		if act.IsPrivate {
// 			if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
// 				models.READABLE); !has {
// 				continue
// 			}
// 		}
// 		feeds = append(feeds, fmt.Sprintf(TPL_FEED, base.ActionIcon(act.OpType),
// 			base.TimeSince(act.Created), base.ActionDesc(act)))
// 	}
// 	ctx.JSON(200, &feeds)
// }

func Issues(ctx *middleware.Context) {
	ctx.Data["Title"] = "Your Issues"

	viewType := ctx.Query("type")
	types := []string{"assigned", "created_by"}
	if !com.IsSliceContainsStr(types, viewType) {
		viewType = "all"
	}

	isShowClosed := ctx.Query("state") == "closed"

	var filterMode int
	switch viewType {
	case "assigned":
		filterMode = models.FM_ASSIGN
	case "created_by":
		filterMode = models.FM_CREATE
	}

	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)

	// Get all repositories.
	repos, err := models.GetRepositories(ctx.User.Id, true)
	if err != nil {
		ctx.Handle(500, "user.Issues(GetRepositories)", err)
		return
	}

	repoIds := make([]int64, 0, len(repos))
	showRepos := make([]*models.Repository, 0, len(repos))
	for _, repo := range repos {
		if repo.NumIssues == 0 {
			continue
		}

		repoIds = append(repoIds, repo.Id)
		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
		issueStats.AllCount += int64(repo.NumOpenIssues)

		if isShowClosed {
			if repo.NumClosedIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		} else {
			if repo.NumOpenIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		}
	}

	if repoId > 0 {
		repoIds = []int64{repoId}
	}

	page, _ := com.StrTo(ctx.Query("page")).Int()

	// Get all issues.
	var ius []*models.IssueUser
	switch viewType {
	case "assigned":
		fallthrough
	case "created_by":
		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
	default:
		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
	}
	if err != nil {
		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
		return
	}

	issues := make([]*models.Issue, len(ius))
	for i := range ius {
		issues[i], err = models.GetIssueById(ius[i].IssueId)
		if err != nil {
			if err == models.ErrIssueNotExist {
				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueId), err)
				return
			}
		}

		issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId)
		if err != nil {
			if err == models.ErrRepoNotExist {
				log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err)
				return
			}
		}

		if err = issues[i].Repo.GetOwner(); err != nil {
			ctx.Handle(500, "user.Issues(GetOwner)", err)
			return
		}

		if err = issues[i].GetPoster(); err != nil {
			ctx.Handle(500, "user.Issues(GetUserById)", err)
			return
		}
	}

	ctx.Data["RepoId"] = repoId
	ctx.Data["Repos"] = showRepos
	ctx.Data["Issues"] = issues
	ctx.Data["ViewType"] = viewType
	ctx.Data["IssueStats"] = issueStats
	ctx.Data["IsShowClosed"] = isShowClosed
	if isShowClosed {
		ctx.Data["State"] = "closed"
		ctx.Data["ShowCount"] = issueStats.ClosedCount
	} else {
		ctx.Data["ShowCount"] = issueStats.OpenCount
	}
	ctx.HTML(200, ISSUES)
}
<|endoftext|>


<commit_before>package rproxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"

	"github.com/Codility/redis-proxy/fakeredis"
	"github.com/stvp/assert"
)

func TestProxyAdminNonTLS(t *testing.T) {
	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin:  AddrSpec{Addr: "127.0.0.1:0"},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	res, err := http.Get(fmt.Sprintf("http://%s/", proxy.AdminAddr().String()))
	assert.Nil(t, err)
	assert.Equal(t, res.StatusCode, 200)
}

func TestProxyAdminTLS(t *testing.T) {
	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin: AddrSpec{
				Addr:     "127.0.0.1:0",
				TLS:      true,
				CertFile: "../test_data/tls/server/cert.pem",
				KeyFile:  "../test_data/tls/server/key.pem",
			},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	certPEM, err := ioutil.ReadFile("../test_data/tls/testca/cacert.pem")
	assert.Nil(t, err)

	roots := x509.NewCertPool()
	assert.True(t, roots.AppendCertsFromPEM(certPEM))

	addr := strings.Replace(proxy.AdminAddr().String(), "127.0.0.1", "localhost", -1)
	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs: roots,
			},
		},
	}
	res, err := client.Get(fmt.Sprintf("https://%s/", addr))
	assert.Nil(t, err)
	assert.Equal(t, res.StatusCode, 200)
}

func TestProxyAdminStatusJSON(t *testing.T) {
	// Old-style status.json page

	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin:  AddrSpec{Addr: "127.0.0.1:0"},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	res, err := http.Get(fmt.Sprintf("http://%s/status.json", proxy.AdminAddr().String()))

	var status map[string]interface{}
	if err = json.NewDecoder(res.Body).Decode(&status); err != nil {
		t.Fatal(err)
	}

	expect := func(path string, expVal interface{}) {
		parts := strings.Split(path, "/")
		val := interface{}(status)
		for _, k := range parts {
			if val == nil {
				t.Fatalf("Path: %s, hit <nil> when trying to access %s", path, k)
			}
			val = val.(map[string]interface{})[k]
		}
		if val != expVal {
			t.Fatalf("Path: %s expected %v, got %v", path, expVal, val)
		}
	}

	expect("ActiveRequests", 0.0)
	expect("WaitingRequests", 0.0)
	expect("State", 2.0)
	expect("RawConnections", 0.0)

	expect("Config/uplink/addr", srv.Addr().String())
	expect("Config/uplink/pass", "[removed]")
	expect("Config/uplink/tls", false)
	expect("Config/uplink/network", "")
	expect("Config/uplink/certfile", "")
	expect("Config/uplink/keyfile", "")
	expect("Config/uplink/cacertfile", "")

	expect("Config/listen/addr", "127.0.0.1:0")
	expect("Config/listen/pass", "[removed]")
	expect("Config/listen/tls", false)
	expect("Config/listen/network", "")
	expect("Config/listen/certfile", "")
	expect("Config/listen/keyfile", "")
	expect("Config/listen/cacertfile", "")

	expect("Config/listen_raw/addr", "")
	expect("Config/listen_raw/pass", "[removed]")
	expect("Config/listen_raw/tls", false)
	expect("Config/listen_raw/network", "")
	expect("Config/listen_raw/certfile", "")
	expect("Config/listen_raw/keyfile", "")
	expect("Config/listen_raw/cacertfile", "")

	expect("Config/admin/addr", "127.0.0.1:0")
	expect("Config/admin/pass", "[removed]")
	expect("Config/admin/tls", false)
	expect("Config/admin/network", "")
	expect("Config/admin/certfile", "")
	expect("Config/admin/keyfile", "")
	expect("Config/admin/cacertfile", "")
}

func TestProxyAdminInfoJSON(t *testing.T) {
	// Old-style status.json page

	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin:  AddrSpec{Addr: "127.0.0.1:0"},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	res, err := http.Get(fmt.Sprintf("http://%s/info.json", proxy.AdminAddr().String()))
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != 200 {
		t.Fatal(res.StatusCode)
	}

	var status map[string]interface{}
	if err = json.NewDecoder(res.Body).Decode(&status); err != nil {
		t.Fatal(err)
	}

	expect := func(path string, expVal interface{}) {
		parts := strings.Split(path, "/")
		val := interface{}(status)
		for _, k := range parts {
			if val == nil {
				t.Fatalf("Path: %s, hit <nil> when trying to access %s", path, k)
			}
			val = val.(map[string]interface{})[k]
		}
		if val != expVal {
			t.Fatalf("Path: %s expected %v, got %v", path, expVal, val)
		}
	}

	expect("active_requests", 0.0)
	expect("waiting_requests", 0.0)
	expect("state", 2.0)
	expect("state_str", "running")
	expect("raw_connections", 0.0)

	expect("config/uplink/addr", srv.Addr().String())
	expect("config/uplink/pass", "[removed]")
	expect("config/uplink/tls", false)
	expect("config/uplink/network", "")
	expect("config/uplink/certfile", "")
	expect("config/uplink/keyfile", "")
	expect("config/uplink/cacertfile", "")

	expect("config/listen/addr", "127.0.0.1:0")
	expect("config/listen/pass", "[removed]")
	expect("config/listen/tls", false)
	expect("config/listen/network", "")
	expect("config/listen/certfile", "")
	expect("config/listen/keyfile", "")
	expect("config/listen/cacertfile", "")

	expect("config/listen_raw/addr", "")
	expect("config/listen_raw/pass", "[removed]")
	expect("config/listen_raw/tls", false)
	expect("config/listen_raw/network", "")
	expect("config/listen_raw/certfile", "")
	expect("config/listen_raw/keyfile", "")
	expect("config/listen_raw/cacertfile", "")

	expect("config/admin/addr", "127.0.0.1:0")
	expect("config/admin/pass", "[removed]")
	expect("config/admin/tls", false)
	expect("config/admin/network", "")
	expect("config/admin/certfile", "")
	expect("config/admin/keyfile", "")
	expect("config/admin/cacertfile", "")
}
<commit_msg>Remove junk comment.<commit_after>package rproxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"

	"github.com/Codility/redis-proxy/fakeredis"
	"github.com/stvp/assert"
)

func TestProxyAdminNonTLS(t *testing.T) {
	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin:  AddrSpec{Addr: "127.0.0.1:0"},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	res, err := http.Get(fmt.Sprintf("http://%s/", proxy.AdminAddr().String()))
	assert.Nil(t, err)
	assert.Equal(t, res.StatusCode, 200)
}

func TestProxyAdminTLS(t *testing.T) {
	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin: AddrSpec{
				Addr:     "127.0.0.1:0",
				TLS:      true,
				CertFile: "../test_data/tls/server/cert.pem",
				KeyFile:  "../test_data/tls/server/key.pem",
			},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	certPEM, err := ioutil.ReadFile("../test_data/tls/testca/cacert.pem")
	assert.Nil(t, err)

	roots := x509.NewCertPool()
	assert.True(t, roots.AppendCertsFromPEM(certPEM))

	addr := strings.Replace(proxy.AdminAddr().String(), "127.0.0.1", "localhost", -1)
	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs: roots,
			},
		},
	}
	res, err := client.Get(fmt.Sprintf("https://%s/", addr))
	assert.Nil(t, err)
	assert.Equal(t, res.StatusCode, 200)
}

func TestProxyAdminStatusJSON(t *testing.T) {
	// Old-style status.json page

	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin:  AddrSpec{Addr: "127.0.0.1:0"},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	res, err := http.Get(fmt.Sprintf("http://%s/status.json", proxy.AdminAddr().String()))

	var status map[string]interface{}
	if err = json.NewDecoder(res.Body).Decode(&status); err != nil {
		t.Fatal(err)
	}

	expect := func(path string, expVal interface{}) {
		parts := strings.Split(path, "/")
		val := interface{}(status)
		for _, k := range parts {
			if val == nil {
				t.Fatalf("Path: %s, hit <nil> when trying to access %s", path, k)
			}
			val = val.(map[string]interface{})[k]
		}
		if val != expVal {
			t.Fatalf("Path: %s expected %v, got %v", path, expVal, val)
		}
	}

	expect("ActiveRequests", 0.0)
	expect("WaitingRequests", 0.0)
	expect("State", 2.0)
	expect("RawConnections", 0.0)

	expect("Config/uplink/addr", srv.Addr().String())
	expect("Config/uplink/pass", "[removed]")
	expect("Config/uplink/tls", false)
	expect("Config/uplink/network", "")
	expect("Config/uplink/certfile", "")
	expect("Config/uplink/keyfile", "")
	expect("Config/uplink/cacertfile", "")

	expect("Config/listen/addr", "127.0.0.1:0")
	expect("Config/listen/pass", "[removed]")
	expect("Config/listen/tls", false)
	expect("Config/listen/network", "")
	expect("Config/listen/certfile", "")
	expect("Config/listen/keyfile", "")
	expect("Config/listen/cacertfile", "")

	expect("Config/listen_raw/addr", "")
	expect("Config/listen_raw/pass", "[removed]")
	expect("Config/listen_raw/tls", false)
	expect("Config/listen_raw/network", "")
	expect("Config/listen_raw/certfile", "")
	expect("Config/listen_raw/keyfile", "")
	expect("Config/listen_raw/cacertfile", "")

	expect("Config/admin/addr", "127.0.0.1:0")
	expect("Config/admin/pass", "[removed]")
	expect("Config/admin/tls", false)
	expect("Config/admin/network", "")
	expect("Config/admin/certfile", "")
	expect("Config/admin/keyfile", "")
	expect("Config/admin/cacertfile", "")
}

func TestProxyAdminInfoJSON(t *testing.T) {
	srv := fakeredis.Start("srv", "tcp")
	defer srv.Stop()

	conf := &TestConfigLoader{
		conf: &Config{
			Uplink: AddrSpec{Addr: srv.Addr().String()},
			Listen: AddrSpec{Addr: "127.0.0.1:0"},
			Admin:  AddrSpec{Addr: "127.0.0.1:0"},
		},
	}
	proxy := mustStartTestProxy(t, conf)
	defer proxy.Stop()

	res, err := http.Get(fmt.Sprintf("http://%s/info.json", proxy.AdminAddr().String()))
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != 200 {
		t.Fatal(res.StatusCode)
	}

	var status map[string]interface{}
	if err = json.NewDecoder(res.Body).Decode(&status); err != nil {
		t.Fatal(err)
	}

	expect := func(path string, expVal interface{}) {
		parts := strings.Split(path, "/")
		val := interface{}(status)
		for _, k := range parts {
			if val == nil {
				t.Fatalf("Path: %s, hit <nil> when trying to access %s", path, k)
			}
			val = val.(map[string]interface{})[k]
		}
		if val != expVal {
			t.Fatalf("Path: %s expected %v, got %v", path, expVal, val)
		}
	}

	expect("active_requests", 0.0)
	expect("waiting_requests", 0.0)
	expect("state", 2.0)
	expect("state_str", "running")
	expect("raw_connections", 0.0)

	expect("config/uplink/addr", srv.Addr().String())
	expect("config/uplink/pass", "[removed]")
	expect("config/uplink/tls", false)
	expect("config/uplink/network", "")
	expect("config/uplink/certfile", "")
	expect("config/uplink/keyfile", "")
	expect("config/uplink/cacertfile", "")

	expect("config/listen/addr", "127.0.0.1:0")
	expect("config/listen/pass", "[removed]")
	expect("config/listen/tls", false)
	expect("config/listen/network", "")
	expect("config/listen/certfile", "")
	expect("config/listen/keyfile", "")
	expect("config/listen/cacertfile", "")

	expect("config/listen_raw/addr", "")
	expect("config/listen_raw/pass", "[removed]")
	expect("config/listen_raw/tls", false)
	expect("config/listen_raw/network", "")
	expect("config/listen_raw/certfile", "")
	expect("config/listen_raw/keyfile", "")
	expect("config/listen_raw/cacertfile", "")

	expect("config/admin/addr", "127.0.0.1:0")
	expect("config/admin/pass", "[removed]")
	expect("config/admin/tls", false)
	expect("config/admin/network", "")
	expect("config/admin/certfile", "")
	expect("config/admin/keyfile", "")
	expect("config/admin/cacertfile", "")
}
<|endoftext|>


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

package x509

import (
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"strings"

	"github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types"
)

type Certificate struct {
	Issuer         string
	SerialNumber   string
	Subject        string
	SubjectAsText  string
	SubjectKeyID   string
	AuthorityKeyID string
	Certificate    *x509.Certificate
}

func DecodeX509Certificate(pemCertificate string) (*Certificate, error) {
	block, _ := pem.Decode([]byte(pemCertificate))
	if block == nil {
		return nil, types.NewErrInvalidCertificate("Could not decode pem certificate")
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, types.NewErrInvalidCertificate(fmt.Sprintf("Could not parse certificate: %v", err.Error()))
	}

	certificate := Certificate{
		Issuer:         cert.Issuer.String(),
		SerialNumber:   cert.SerialNumber.String(),
		Subject:        cert.Subject.String(),
		SubjectAsText:  cert.Subject.String(),
		SubjectKeyID:   BytesToHex(cert.SubjectKeyId),
		AuthorityKeyID: BytesToHex(cert.AuthorityKeyId),
		Certificate:    cert,
	}

	certificate = PatchCertificate(certificate)

	return &certificate, nil
}

// This function is needed to patch the Issuer/Subject(vid/pid) field of certificate to hex format.
// https://github.com/zigbee-alliance/distributed-compliance-ledger/issues/270
func PatchCertificate(certificate Certificate) Certificate {
	oldVIDKey := "1.3.6.1.4.1.37244.2.1"
	oldPIDKey := "1.3.6.1.4.1.37244.2.2"

	newVIDKey := "vid"
	newPIDKey := "pid"

	issuer := certificate.Issuer
	issuer = FormatOID(issuer, oldVIDKey, newVIDKey)
	issuer = FormatOID(issuer, oldPIDKey, newPIDKey)

	subject := certificate.Subject
	subject = FormatOID(subject, oldVIDKey, newVIDKey)
	subject = FormatOID(subject, oldPIDKey, newPIDKey)

	certificate.Issuer = issuer
	certificate.SubjectAsText = subject

	certificate.Subject = StringtoBase64(certificate.Subject)

	return certificate
}

func FormatOID(header, oldKey, newKey string) string {
	subjectValues := strings.Split(header, ",")

	// When translating a string number into a hexadecimal number,
	// we must take 8 numbers of this string number from the end so that it needs to fit into an integer number.
	hexStringIntegerLength := 8
	for index, value := range subjectValues {
		if strings.HasPrefix(value, oldKey) {
			// get value from header
			value = value[len(value)-hexStringIntegerLength:]

			decoded, _ := hex.DecodeString(value)

			subjectValues[index] = fmt.Sprintf("%s=0x%s", newKey, decoded)
		}
	}

	return strings.Join(subjectValues, ",")
}

func StringtoBase64(subject string) string {
	return base64.StdEncoding.EncodeToString([]byte(subject))
}

func BytesToHex(bytes []byte) string {
	if bytes == nil {
		return ""
	}

	bytesHex := make([]string, len(bytes))
	for i, b := range bytes {
		bytesHex[i] = fmt.Sprintf("%02X", b)
	}

	return strings.Join(bytesHex, ":")
}

func (c Certificate) Verify(parent *Certificate) error {
	roots := x509.NewCertPool()
	roots.AddCert(parent.Certificate)

	opts := x509.VerifyOptions{Roots: roots}

	if _, err := c.Certificate.Verify(opts); err != nil {
		return types.NewErrInvalidCertificate(fmt.Sprintf("Certificate verification failed. Error: %v", err))
	}

	return nil
}

func (c Certificate) IsSelfSigned() bool {
	if len(c.AuthorityKeyID) > 0 {
		return c.Issuer == c.Subject && c.AuthorityKeyID == c.SubjectKeyID
	}

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

package x509

import (
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"strings"

	"github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types"
)

type Certificate struct {
	Issuer         string
	SerialNumber   string
	Subject        string
	SubjectAsText  string
	SubjectKeyID   string
	AuthorityKeyID string
	Certificate    *x509.Certificate
}

func DecodeX509Certificate(pemCertificate string) (*Certificate, error) {
	block, _ := pem.Decode([]byte(pemCertificate))
	if block == nil {
		return nil, types.NewErrInvalidCertificate("Could not decode pem certificate")
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, types.NewErrInvalidCertificate(fmt.Sprintf("Could not parse certificate: %v", err.Error()))
	}

	certificate := Certificate{
		Issuer:         cert.Issuer.String(),
		SerialNumber:   cert.SerialNumber.String(),
		Subject:        cert.Subject.String(),
		SubjectAsText:  cert.Subject.String(),
		SubjectKeyID:   BytesToHex(cert.SubjectKeyId),
		AuthorityKeyID: BytesToHex(cert.AuthorityKeyId),
		Certificate:    cert,
	}

	certificate = PatchCertificate(certificate)

	return &certificate, nil
}

// This function is needed to patch the Issuer/Subject(vid/pid) field of certificate to hex format.
// https://github.com/zigbee-alliance/distributed-compliance-ledger/issues/270
func PatchCertificate(certificate Certificate) Certificate {
	oldVIDKey := "1.3.6.1.4.1.37244.2.1"
	oldPIDKey := "1.3.6.1.4.1.37244.2.2"

	newVIDKey := "vid"
	newPIDKey := "pid"

	issuer := certificate.Issuer
	issuer = FormatOID(issuer, oldVIDKey, newVIDKey)
	issuer = FormatOID(issuer, oldPIDKey, newPIDKey)

	subject := certificate.Subject
	subject = FormatOID(subject, oldVIDKey, newVIDKey)
	subject = FormatOID(subject, oldPIDKey, newPIDKey)

	certificate.Issuer = issuer
	certificate.SubjectAsText = subject

	certificate.Subject = StringtoBase64(certificate.Subject)

	return certificate
}

func FormatOID(header, oldKey, newKey string) string {
	subjectValues := strings.Split(header, ",")

	// When translating a string number into a hexadecimal number,
	// we must take 8 numbers of this string number from the end so that it needs to fit into an integer number.
	hexStringIntegerLength := 8
	for index, value := range subjectValues {
		if strings.HasPrefix(value, oldKey) {
			// get value from header
			value = value[len(value)-hexStringIntegerLength:]

			decoded, _ := hex.DecodeString(value)

			subjectValues[index] = fmt.Sprintf("%s=0x%s", newKey, decoded)
		}
	}

	return strings.Join(subjectValues, ",")
}

func StringtoBase64(subject string) string {
	return base64.StdEncoding.EncodeToString([]byte(subject))
}

func BytesToHex(bytes []byte) string {
	if bytes == nil {
		return ""
	}

	bytesHex := make([]string, len(bytes))
	for i, b := range bytes {
		bytesHex[i] = fmt.Sprintf("%02X", b)
	}

	return strings.Join(bytesHex, ":")
}

func (c Certificate) Verify(parent *Certificate) error {
	roots := x509.NewCertPool()
	roots.AddCert(parent.Certificate)

	opts := x509.VerifyOptions{Roots: roots}

	if _, err := c.Certificate.Verify(opts); err != nil {
		return types.NewErrInvalidCertificate(fmt.Sprintf("Certificate verification failed. Error: %v", err))
	}

	return nil
}

func (c Certificate) IsSelfSigned() bool {
	if len(c.AuthorityKeyID) > 0 {
		return c.Issuer == c.SubjectAsText && c.AuthorityKeyID == c.SubjectKeyID
	}

	return c.Issuer == c.Subject
}
<|endoftext|>


<commit_before>/*
https://godoc.org/code.google.com/p/go.net/websocket
http://blog.golang.org/spotlight-on-external-go-libraries
https://gist.github.com/jweir/4528042
https://github.com/golang-samples/websocket/blob/master/simple/main.go
http://blog.jupo.org/2013/02/23/a-tale-of-two-queues/
http://stackoverflow.com/questions/19708330/serving-a-websocket-in-go
*/

package main

import (
	"code.google.com/p/go.net/websocket"
	"gopkg.in/redis.v1"
	"net/http"
	"log"
	_ "reflect"
)

var (
    redis_host = "127.0.0.1"
    redis_port = "6379"
    redis_channel = "pubsocketd"
    /* see below - I have no idea what I am doing */
    /* pubsub *redis.PubSub */
)

/*
func init() {

	addr := redis_host + ":" + redis_port

	client := redis.NewTCPClient(&redis.Options{
	    Addr: addr,
	})

	defer client.Close()

	pubsub := client.PubSub()
	defer pubsub.Close()

	err := pubsub.Subscribe(redis_channel)
}
*/

func pubSubHandler(ws *websocket.Conn) {

	log.Printf("connecting!")

	addr := redis_host + ":" + redis_port

	client := redis.NewTCPClient(&redis.Options{
	    Addr: addr,
	})

	defer client.Close()

	pubsub := client.PubSub()
	defer pubsub.Close()

	err := pubsub.Subscribe(redis_channel)

	if err != nil {
	   websocket.JSON.Send(ws, "FUBAR")
	   ws.Close()
	}

	for {
		i, _ := pubsub.Receive()
		msg, _ := i.(*redis.Message)

		if msg != nil {
		   websocket.JSON.Send(ws, msg.Payload)
		}
	}
}

func main() {

	http.HandleFunc("/", func (w http.ResponseWriter, req *http.Request){
        	s := websocket.Server{Handler: websocket.Handler(pubSubHandler)}
        	s.ServeHTTP(w, req)
    	});

	log.Printf("Listening on " + redis_host + ":" + redis_port + " and relaying messages from '" + redis_channel + "'")

	http_err := http.ListenAndServe("127.0.0.1:8080", nil)

	if http_err != nil {
		panic("ListenAndServe: " + http_err.Error())
	}
}
<commit_msg>command line flags<commit_after>/*
https://godoc.org/code.google.com/p/go.net/websocket
http://blog.golang.org/spotlight-on-external-go-libraries
https://gist.github.com/jweir/4528042
https://github.com/golang-samples/websocket/blob/master/simple/main.go
http://blog.jupo.org/2013/02/23/a-tale-of-two-queues/
http://stackoverflow.com/questions/19708330/serving-a-websocket-in-go
*/

package main

import (
	"code.google.com/p/go.net/websocket"
	"gopkg.in/redis.v1"
	"net/http"
	"fmt"
	"log"
	"flag"
	_ "os"
	_ "reflect"
)

var (
    redis_host string
    redis_port int
    redis_channel string
    redis_endpoint string
    websocket_host string
    websocket_port int
    websocket_endpoint string
    pubsub *redis.PubSub
)

func init() {

     flag.StringVar(&websocket_host, "ws-host", "127.0.0.1", "Websocket host")
     flag.IntVar(&websocket_port, "ws-port", 8080, "Websocket port")
     flag.StringVar(&redis_host, "rs-host", "127.0.0.1", "Redis host")
     flag.IntVar(&redis_port, "rs-port", 6379, "Redis port")
     flag.StringVar(&redis_channel, "rs-channel", "pubsocketd", "Redis channel")

     /*

     client := redis.NewTCPClient(&redis.Options{
	    Addr: redis_endpoint,
	})

	defer client.Close()

	pubsub := client.PubSub()
	defer pubsub.Close()

	err := pubsub.Subscribe(redis_channel)
	*/
}

func pubSubHandler(ws *websocket.Conn) {

	log.Printf("connecting!")

	client := redis.NewTCPClient(&redis.Options{
	    Addr: redis_endpoint,
	})

	defer client.Close()

	pubsub := client.PubSub()
	defer pubsub.Close()

	err := pubsub.Subscribe(redis_channel)

	if err != nil {
	   websocket.JSON.Send(ws, "FUBAR")
	   ws.Close()
	}

	for {
		i, _ := pubsub.Receive()
		msg, _ := i.(*redis.Message)

		if msg != nil {
		   websocket.JSON.Send(ws, msg.Payload)
		}
	}
}

func main() {

     // not that init() is invoked before we get here

     flag.Parse()

     websocket_endpoint = fmt.Sprintf("%s:%d", websocket_host, websocket_port)
     redis_endpoint = fmt.Sprintf("%s:%d", redis_host, redis_port)

	http.HandleFunc("/", func (w http.ResponseWriter, req *http.Request){
        	s := websocket.Server{Handler: websocket.Handler(pubSubHandler)}
        	s.ServeHTTP(w, req)
    	});

	log.Printf("Listening for websocket requests on " + websocket_endpoint)
	log.Printf("Listening for pubsub messages from " + redis_endpoint + "#" + redis_channel)

	http_err := http.ListenAndServe(websocket_endpoint, nil)

	if http_err != nil {
		panic("ListenAndServe: " + http_err.Error())
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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

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

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

package solver

import (
	"context"
	"fmt"
	"net/http"
	"path"
	"strings"

	logf "github.com/jetstack/cert-manager/pkg/logs"
)

type HTTP01Solver struct {
	ListenPort int

	Domain string
	Token  string
	Key    string
}

func (h *HTTP01Solver) Listen(ctx context.Context) error {
	log := logf.FromContext(ctx)
	log.Info("starting listener",
		"expected_domain", h.Domain,
		"expected_token", h.Token,
		"expected_key", h.Key,
		"listen_port", h.ListenPort,
	)

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// extract vars from the request
		host := strings.Split(r.Host, ":")[0]
		basePath := path.Dir(r.URL.EscapedPath())
		token := path.Base(r.URL.EscapedPath())

		log := log.WithValues(
			"host", host,
			"path", r.URL.EscapedPath(),
			"base_path", basePath,
			"token", token,
		)
		if r.URL.EscapedPath() == "/" || r.URL.EscapedPath() == "/healthz" {
			log.Info("responding OK to health check")
			w.WriteHeader(http.StatusOK)
			return
		}
		log.Info("validating request")
		// verify the base path is correct
		if basePath != HTTPChallengePath {
			log.Info("invalid base_path", "expected_base_path", HTTPChallengePath)
			http.NotFound(w, r)
			return
		}

		log.Info("comparing host", "expected_host", h.Domain)
		if h.Domain != host {
			log.Info("invalid host", "expected_host", h.Domain)
			http.NotFound(w, r)
			return
		}

		log.Info("comparing token", "expected_token", h.Token)
		if h.Token != token {
			// if nothing else, we return a 404 here
			log.Info("invalid token", "expected_token", h.Token)
			http.NotFound(w, r)
			return
		}

		log.Info("got successful challenge request, writing key")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, h.Key)
	})
	return http.ListenAndServe(fmt.Sprintf(":%d", h.ListenPort), handler)
}
<commit_msg>add cache-control header<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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

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

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

package solver

import (
	"context"
	"fmt"
	"net/http"
	"path"
	"strings"

	logf "github.com/jetstack/cert-manager/pkg/logs"
)

type HTTP01Solver struct {
	ListenPort int

	Domain string
	Token  string
	Key    string
}

func (h *HTTP01Solver) Listen(ctx context.Context) error {
	log := logf.FromContext(ctx)
	log.Info("starting listener",
		"expected_domain", h.Domain,
		"expected_token", h.Token,
		"expected_key", h.Key,
		"listen_port", h.ListenPort,
	)

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// extract vars from the request
		host := strings.Split(r.Host, ":")[0]
		basePath := path.Dir(r.URL.EscapedPath())
		token := path.Base(r.URL.EscapedPath())

		log := log.WithValues(
			"host", host,
			"path", r.URL.EscapedPath(),
			"base_path", basePath,
			"token", token,
		)
		if r.URL.EscapedPath() == "/" || r.URL.EscapedPath() == "/healthz" {
			log.Info("responding OK to health check")
			w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
			w.WriteHeader(http.StatusOK)
			return
		}
		log.Info("validating request")
		// verify the base path is correct
		if basePath != HTTPChallengePath {
			log.Info("invalid base_path", "expected_base_path", HTTPChallengePath)
			http.NotFound(w, r)
			return
		}

		log.Info("comparing host", "expected_host", h.Domain)
		if h.Domain != host {
			log.Info("invalid host", "expected_host", h.Domain)
			http.NotFound(w, r)
			return
		}

		log.Info("comparing token", "expected_token", h.Token)
		if h.Token != token {
			// if nothing else, we return a 404 here
			log.Info("invalid token", "expected_token", h.Token)
			http.NotFound(w, r)
			return
		}

		log.Info("got successful challenge request, writing key")
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, h.Key)
	})
	return http.ListenAndServe(fmt.Sprintf(":%d", h.ListenPort), handler)
}
<|endoftext|>


<commit_before>package txbuilder

import (
	"bytes"
	"context"

	cfg "github.com/bytom/config"
	"github.com/bytom/consensus"
	"github.com/bytom/errors"
	"github.com/bytom/protocol"
	"github.com/bytom/protocol/bc/types"
	"github.com/bytom/protocol/vm"
)

var (
	// ErrRejected means the network rejected a tx (as a double-spend)
	ErrRejected = errors.New("transaction rejected")
	// ErrMissingRawTx means missing transaction
	ErrMissingRawTx = errors.New("missing raw tx")
	// ErrBadInstructionCount means too many signing instructions compare with inputs
	ErrBadInstructionCount = errors.New("too many signing instructions in template")
	// ErrOrphanTx means submit transaction is orphan
	ErrOrphanTx = errors.New("finalize can't find transaction input utxo")
	// ErrExtTxFee means transaction fee exceed max limit
	ErrExtTxFee = errors.New("transaction fee exceed max limit")
	// ErrNoGasInput means transaction has no gas input
	ErrNoGasInput = errors.New("transaction has no gas input")
)

// FinalizeTx validates a transaction signature template,
// assembles a fully signed tx, and stores the effects of
// its changes on the UTXO set.
func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error {
	if fee := CalculateTxFee(tx); fee > cfg.CommonConfig.Wallet.MaxTxFee {
		return ErrExtTxFee
	}

	if err := checkTxSighashCommitment(tx); err != nil {
		return err
	}

	if len(tx.GasInputIDs) == 0 {
		return ErrNoGasInput
	}

	// This part is use for prevent tx size  is 0
	data, err := tx.TxData.MarshalText()
	if err != nil {
		return err
	}
	tx.TxData.SerializedSize = uint64(len(data))
	tx.Tx.SerializedSize = uint64(len(data))

	isOrphan, err := c.ValidateTx(tx)
	if errors.Root(err) == protocol.ErrBadTx {
		return errors.Sub(ErrRejected, err)
	}
	if err != nil {
		return errors.WithDetail(err, "tx rejected: "+err.Error())
	}
	if isOrphan {
		return ErrOrphanTx
	}
	return nil
}

var (
	// ErrNoTxSighashCommitment is returned when no input commits to the
	// complete transaction.
	// To permit idempotence of transaction submission, we require at
	// least one input to commit to the complete transaction (what you get
	// when you build a transaction with allow_additional_actions=false).
	ErrNoTxSighashCommitment = errors.New("no commitment to tx sighash")

	// ErrNoTxSighashAttempt is returned when there was no attempt made to sign
	// this transaction.
	ErrNoTxSighashAttempt = errors.New("no tx sighash attempted")

	// ErrTxSignatureFailure is returned when there was an attempt to sign this
	// transaction, but it failed.
	ErrTxSignatureFailure = errors.New("tx signature was attempted but failed")
)

func checkTxSighashCommitment(tx *types.Tx) error {
	// TODO: this is the local sender check rules, we might don't need it due to the rule is difference
	return nil
	var lastError error

	for i, inp := range tx.Inputs {
		var args [][]byte
		switch t := inp.TypedInput.(type) {
		case *types.SpendInput:
			args = t.Arguments
		case *types.IssuanceInput:
			args = t.Arguments
		}
		// Note: These numbers will need to change if more args are added such that the minimum length changes
		switch {
		// A conforming arguments list contains
		// [... arg1 arg2 ... argN N sig1 sig2 ... sigM prog]
		// The args are the opaque arguments to prog. In the case where
		// N is 0 (prog takes no args), and assuming there must be at
		// least one signature, args has a minimum length of 3.
		case len(args) == 0:
			lastError = ErrNoTxSighashAttempt
			continue
		case len(args) < 3:
			lastError = ErrTxSignatureFailure
			continue
		}
		lastError = ErrNoTxSighashCommitment
		prog := args[len(args)-1]
		if len(prog) != 35 {
			continue
		}
		if prog[0] != byte(vm.OP_DATA_32) {
			continue
		}
		if !bytes.Equal(prog[33:], []byte{byte(vm.OP_TXSIGHASH), byte(vm.OP_EQUAL)}) {
			continue
		}
		h := tx.SigHash(uint32(i))
		if !bytes.Equal(h.Bytes(), prog[1:33]) {
			continue
		}
		// At least one input passes commitment checks
		return nil
	}

	return lastError
}

// CalculateTxFee calculate transaction fee
func CalculateTxFee(tx *types.Tx) (fee uint64) {
	totalInputBTM := uint64(0)
	totalOutputBTM := uint64(0)

	for _, input := range tx.Inputs {
		if input.InputType() != types.CoinbaseInputType && input.AssetID() == *consensus.BTMAssetID {
			totalInputBTM += input.Amount()
		}
	}

	for _, output := range tx.Outputs {
		if *output.AssetId == *consensus.BTMAssetID {
			totalOutputBTM += output.Amount
		}
	}

	fee = totalInputBTM - totalOutputBTM
	return
}
<commit_msg>fix coinbase fee bug (#1642)<commit_after>package txbuilder

import (
	"bytes"
	"context"

	cfg "github.com/bytom/config"
	"github.com/bytom/consensus"
	"github.com/bytom/errors"
	"github.com/bytom/protocol"
	"github.com/bytom/protocol/bc/types"
	"github.com/bytom/protocol/vm"
)

var (
	// ErrRejected means the network rejected a tx (as a double-spend)
	ErrRejected = errors.New("transaction rejected")
	// ErrMissingRawTx means missing transaction
	ErrMissingRawTx = errors.New("missing raw tx")
	// ErrBadInstructionCount means too many signing instructions compare with inputs
	ErrBadInstructionCount = errors.New("too many signing instructions in template")
	// ErrOrphanTx means submit transaction is orphan
	ErrOrphanTx = errors.New("finalize can't find transaction input utxo")
	// ErrExtTxFee means transaction fee exceed max limit
	ErrExtTxFee = errors.New("transaction fee exceed max limit")
	// ErrNoGasInput means transaction has no gas input
	ErrNoGasInput = errors.New("transaction has no gas input")
)

// FinalizeTx validates a transaction signature template,
// assembles a fully signed tx, and stores the effects of
// its changes on the UTXO set.
func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error {
	if fee := CalculateTxFee(tx); fee > cfg.CommonConfig.Wallet.MaxTxFee {
		return ErrExtTxFee
	}

	if err := checkTxSighashCommitment(tx); err != nil {
		return err
	}

	if len(tx.GasInputIDs) == 0 {
		return ErrNoGasInput
	}

	// This part is use for prevent tx size  is 0
	data, err := tx.TxData.MarshalText()
	if err != nil {
		return err
	}
	tx.TxData.SerializedSize = uint64(len(data))
	tx.Tx.SerializedSize = uint64(len(data))

	isOrphan, err := c.ValidateTx(tx)
	if errors.Root(err) == protocol.ErrBadTx {
		return errors.Sub(ErrRejected, err)
	}
	if err != nil {
		return errors.WithDetail(err, "tx rejected: "+err.Error())
	}
	if isOrphan {
		return ErrOrphanTx
	}
	return nil
}

var (
	// ErrNoTxSighashCommitment is returned when no input commits to the
	// complete transaction.
	// To permit idempotence of transaction submission, we require at
	// least one input to commit to the complete transaction (what you get
	// when you build a transaction with allow_additional_actions=false).
	ErrNoTxSighashCommitment = errors.New("no commitment to tx sighash")

	// ErrNoTxSighashAttempt is returned when there was no attempt made to sign
	// this transaction.
	ErrNoTxSighashAttempt = errors.New("no tx sighash attempted")

	// ErrTxSignatureFailure is returned when there was an attempt to sign this
	// transaction, but it failed.
	ErrTxSignatureFailure = errors.New("tx signature was attempted but failed")
)

func checkTxSighashCommitment(tx *types.Tx) error {
	// TODO: this is the local sender check rules, we might don't need it due to the rule is difference
	return nil
	var lastError error

	for i, inp := range tx.Inputs {
		var args [][]byte
		switch t := inp.TypedInput.(type) {
		case *types.SpendInput:
			args = t.Arguments
		case *types.IssuanceInput:
			args = t.Arguments
		}
		// Note: These numbers will need to change if more args are added such that the minimum length changes
		switch {
		// A conforming arguments list contains
		// [... arg1 arg2 ... argN N sig1 sig2 ... sigM prog]
		// The args are the opaque arguments to prog. In the case where
		// N is 0 (prog takes no args), and assuming there must be at
		// least one signature, args has a minimum length of 3.
		case len(args) == 0:
			lastError = ErrNoTxSighashAttempt
			continue
		case len(args) < 3:
			lastError = ErrTxSignatureFailure
			continue
		}
		lastError = ErrNoTxSighashCommitment
		prog := args[len(args)-1]
		if len(prog) != 35 {
			continue
		}
		if prog[0] != byte(vm.OP_DATA_32) {
			continue
		}
		if !bytes.Equal(prog[33:], []byte{byte(vm.OP_TXSIGHASH), byte(vm.OP_EQUAL)}) {
			continue
		}
		h := tx.SigHash(uint32(i))
		if !bytes.Equal(h.Bytes(), prog[1:33]) {
			continue
		}
		// At least one input passes commitment checks
		return nil
	}

	return lastError
}

// CalculateTxFee calculate transaction fee
func CalculateTxFee(tx *types.Tx) (fee uint64) {
	totalInputBTM := uint64(0)
	totalOutputBTM := uint64(0)

	for _, input := range tx.Inputs {
		if input.InputType() == types.CoinbaseInputType {
			return 0
		}
		if input.AssetID() == *consensus.BTMAssetID {
			totalInputBTM += input.Amount()
		}
	}

	for _, output := range tx.Outputs {
		if *output.AssetId == *consensus.BTMAssetID {
			totalOutputBTM += output.Amount
		}
	}

	fee = totalInputBTM - totalOutputBTM
	return
}
<|endoftext|>


<commit_before>/*
Package pushbullet provides simple access to the API of http://pushbullet.com.

Example client:
	pb := pushbullet.New("YOUR_API_KEY")
	devices, err := pb.Devices()
	...
	err = pb.PushNote(devices[0].Id, "Hello!", "Hi from go-pushbullet!")
*/
package pushbullet

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const HOST = "https://www.pushbullet.com/api"

// A Client connects to PushBullet with an API Key.
type Client struct {
	Key    string
	Client *http.Client
}

// New creates a new client with your personal API key.
func New(apikey string) *Client {
	return &Client{apikey, http.DefaultClient}
}

// New creates a new client with your personal API key and the given http Client
func NewWithClient(apikey string, client *http.Client) *Client {
	return &Client{apikey, client}
}

// A Device represents an Android Device as reported by PushBullet.
type Device struct {
	Id        int
	OwnerName string `json:"owner_name"`
	Extras    struct {
		Manufacturer   string
		Model          string
		AndroidVersion string `json:"android_version"`
		SdkVersion     string `json:"sdk_version"`
		AppVersion     string `json:"app_version"`
		Nickname       string
	}
}

type deviceResponse struct {
	Devices       []*Device
	SharedDevices []*Device `json:"shared_devices"`
}

func (c *Client) buildRequest(endpoint string, data url.Values) *http.Request {
	r, err := http.NewRequest("GET", HOST+endpoint, nil)
	if err != nil {
		panic(err)
	}

	// appengine sdk requires us to set the auth header by hand
	u := url.UserPassword(c.Key, "")
	r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(u.String())))

	if data != nil {
		r.Method = "POST"
		r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		r.Body = ioutil.NopCloser(strings.NewReader(data.Encode()))
	}

	return r
}

// Devices fetches a list of devices from PushBullet.
func (c *Client) Devices() ([]*Device, error) {
	req := c.buildRequest("/devices", nil)
	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() {
		resp.Body.Close()
	}()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(resp.Status)
	}

	var devResp deviceResponse
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&devResp)
	if err != nil {
		return nil, err
	}

	devices := append(devResp.Devices, devResp.SharedDevices...)
	return devices, nil
}

// Push pushes the data to a specific device registered with PushBullet.
func (c *Client) Push(deviceId int, data url.Values) error {
	data.Set("device_id", strconv.Itoa(deviceId))
	req := c.buildRequest("/pushes", data)
	resp, err := c.Client.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusOK {
		return errors.New(resp.Status)
	}

	return nil
}

// PushNote pushes a note with title and body to a specific PushBullet device.
func (c *Client) PushNote(deviceId int, title, body string) error {
	data := url.Values{
		"type":  {"note"},
		"title": {title},
		"body":  {body},
	}
	return c.Push(deviceId, data)
}

// PushAddress pushes a geo address with name and address to a specific PushBullet device.
func (c *Client) PushAddress(deviceId int, name, address string) error {
	data := url.Values{
		"type":    {"address"},
		"name":    {name},
		"address": {address},
	}
	return c.Push(deviceId, data)
}

// PushList pushes a list with name and a slice of items to a specific PushBullet device.
func (c *Client) PushList(deviceId int, title string, items []string) error {
	data := url.Values{
		"type":  {"list"},
		"title": {title},
		"items": items,
	}
	return c.Push(deviceId, data)
}

// PushLink pushes a link with a title and url to a specific PushBullet device.
func (c *Client) PushLink(deviceId int, title, u string) error {
	data := url.Values{
		"type":  {"link"},
		"title": {title},
		"url":   {u},
	}
	return c.Push(deviceId, data)
}
<commit_msg>Update for API hostname change<commit_after>/*
Package pushbullet provides simple access to the API of http://pushbullet.com.

Example client:
	pb := pushbullet.New("YOUR_API_KEY")
	devices, err := pb.Devices()
	...
	err = pb.PushNote(devices[0].Id, "Hello!", "Hi from go-pushbullet!")
*/
package pushbullet

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const HOST = "https://api.pushbullet.com/api"

// A Client connects to PushBullet with an API Key.
type Client struct {
	Key    string
	Client *http.Client
}

// New creates a new client with your personal API key.
func New(apikey string) *Client {
	return &Client{apikey, http.DefaultClient}
}

// New creates a new client with your personal API key and the given http Client
func NewWithClient(apikey string, client *http.Client) *Client {
	return &Client{apikey, client}
}

// A Device represents an Android Device as reported by PushBullet.
type Device struct {
	Id        int
	OwnerName string `json:"owner_name"`
	Extras    struct {
		Manufacturer   string
		Model          string
		AndroidVersion string `json:"android_version"`
		SdkVersion     string `json:"sdk_version"`
		AppVersion     string `json:"app_version"`
		Nickname       string
	}
}

type deviceResponse struct {
	Devices       []*Device
	SharedDevices []*Device `json:"shared_devices"`
}

func (c *Client) buildRequest(endpoint string, data url.Values) *http.Request {
	r, err := http.NewRequest("GET", HOST+endpoint, nil)
	if err != nil {
		panic(err)
	}

	// appengine sdk requires us to set the auth header by hand
	u := url.UserPassword(c.Key, "")
	r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(u.String())))

	if data != nil {
		r.Method = "POST"
		r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		r.Body = ioutil.NopCloser(strings.NewReader(data.Encode()))
	}

	return r
}

// Devices fetches a list of devices from PushBullet.
func (c *Client) Devices() ([]*Device, error) {
	req := c.buildRequest("/devices", nil)
	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() {
		resp.Body.Close()
	}()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(resp.Status)
	}

	var devResp deviceResponse
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&devResp)
	if err != nil {
		return nil, err
	}

	devices := append(devResp.Devices, devResp.SharedDevices...)
	return devices, nil
}

// Push pushes the data to a specific device registered with PushBullet.
func (c *Client) Push(deviceId int, data url.Values) error {
	data.Set("device_id", strconv.Itoa(deviceId))
	req := c.buildRequest("/pushes", data)
	resp, err := c.Client.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusOK {
		return errors.New(resp.Status)
	}

	return nil
}

// PushNote pushes a note with title and body to a specific PushBullet device.
func (c *Client) PushNote(deviceId int, title, body string) error {
	data := url.Values{
		"type":  {"note"},
		"title": {title},
		"body":  {body},
	}
	return c.Push(deviceId, data)
}

// PushAddress pushes a geo address with name and address to a specific PushBullet device.
func (c *Client) PushAddress(deviceId int, name, address string) error {
	data := url.Values{
		"type":    {"address"},
		"name":    {name},
		"address": {address},
	}
	return c.Push(deviceId, data)
}

// PushList pushes a list with name and a slice of items to a specific PushBullet device.
func (c *Client) PushList(deviceId int, title string, items []string) error {
	data := url.Values{
		"type":  {"list"},
		"title": {title},
		"items": items,
	}
	return c.Push(deviceId, data)
}

// PushLink pushes a link with a title and url to a specific PushBullet device.
func (c *Client) PushLink(deviceId int, title, u string) error {
	data := url.Values{
		"type":  {"link"},
		"title": {title},
		"url":   {u},
	}
	return c.Push(deviceId, data)
}
<|endoftext|>