<commit_before>package parser

import (
	"log"
	"sync"

	textapi "github.com/AYLIEN/aylien_textapi_go"
)

const (
	// AylienAPIMaxConcurrentCount maximum concurrent api request count for aylien
	AylienAPIMaxConcurrentCount = 5
)

// Analyzer interface to provider analyze function for articles
type Analyzer interface {
	Summarize([]Article)
}

// AylienAnalyzer analyzer using aylien api
type AylienAnalyzer struct {
	AppID      string
	AppKey     string
	Client     *textapi.Client
	APIChannel chan struct{}
}

func (ay *AylienAnalyzer) init() error {
	auth := textapi.Auth{ay.AppID, ay.AppKey}
	client, err := textapi.NewClient(auth, true)
	if err != nil {
		log.Printf("could not create aylien api client, %v", err)
		return err
	}
	ay.Client = client
	ay.APIChannel = make(chan struct{}, AylienAPIMaxConcurrentCount)
	for i := 0; i < AylienAPIMaxConcurrentCount; i++ {
		ay.APIChannel <- struct{}{}
	}
	return nil
}

// Summarize adding summary to all articles by query the aylien text analyze api
func (ay *AylienAnalyzer) Summarize(articles []Article) {
	if len(articles) == 0 {
		return
	}

	totalCount := len(articles)

	wg := sync.WaitGroup{}
	wg.Add(totalCount)

	for i := 0; i < totalCount; i++ {
		<-ay.APIChannel

		go func(index int) {
			articles[index] = getSummary(ay.Client, articles[index])

			ay.APIChannel <- struct{}{}
			wg.Done()
		}(i)
	}

	wg.Wait()
}

func getSummary(client *textapi.Client, article Article) Article {
	log.Printf("getting summary for article: %s", article.Title)
	summarizeParams := &textapi.SummarizeParams{
		URL:               article.Link,
		NumberOfSentences: 3,
		Title:             article.Title,
	}
	summary, err := client.Summarize(summarizeParams)
	if err != nil {
		log.Printf("failed to get summary, %v", err)
		return article
	}
	if summary.Text != "" {
		article.Summary = summary.Text
	}

	return article
}

// NewAylienAnalyzer create an aylien analyzer
func NewAylienAnalyzer(appid, appkey string) Analyzer {
	analyzer := &AylienAnalyzer{AppID: appid, AppKey: appkey}
	err := analyzer.init()

	if err != nil {
		log.Printf("failed to create aylien api client")
		return nil
	}

	return analyzer
}
<commit_msg>fix mistook use summary.text<commit_after>package parser

import (
	"log"
	"strings"
	"sync"

	textapi "github.com/AYLIEN/aylien_textapi_go"
)

const (
	// AylienAPIMaxConcurrentCount maximum concurrent api request count for aylien
	AylienAPIMaxConcurrentCount = 5
)

// Analyzer interface to provider analyze function for articles
type Analyzer interface {
	Summarize([]Article)
}

// AylienAnalyzer analyzer using aylien api
type AylienAnalyzer struct {
	AppID      string
	AppKey     string
	Client     *textapi.Client
	APIChannel chan struct{}
}

func (ay *AylienAnalyzer) init() error {
	auth := textapi.Auth{ay.AppID, ay.AppKey}
	client, err := textapi.NewClient(auth, true)
	if err != nil {
		log.Printf("could not create aylien api client, %v", err)
		return err
	}
	ay.Client = client
	ay.APIChannel = make(chan struct{}, AylienAPIMaxConcurrentCount)
	for i := 0; i < AylienAPIMaxConcurrentCount; i++ {
		ay.APIChannel <- struct{}{}
	}
	return nil
}

// Summarize adding summary to all articles by query the aylien text analyze api
func (ay *AylienAnalyzer) Summarize(articles []Article) {
	if len(articles) == 0 {
		return
	}

	totalCount := len(articles)

	wg := sync.WaitGroup{}
	wg.Add(totalCount)

	for i := 0; i < totalCount; i++ {
		<-ay.APIChannel

		go func(index int) {
			articles[index] = getSummary(ay.Client, articles[index])

			ay.APIChannel <- struct{}{}
			wg.Done()
		}(i)
	}

	wg.Wait()
}

func getSummary(client *textapi.Client, article Article) Article {
	log.Printf("getting summary for article: %s", article.Title)
	summarizeParams := &textapi.SummarizeParams{
		URL:               article.Link,
		NumberOfSentences: 3,
		Title:             article.Title,
	}
	summary, err := client.Summarize(summarizeParams)
	if err != nil {
		log.Printf("failed to get summary, %v", err)
		return article
	}
	if len(summary.Sentences) != 0 {
		article.Summary = strings.Join(summary.Sentences, "\n")
	}

	return article
}

// NewAylienAnalyzer create an aylien analyzer
func NewAylienAnalyzer(appid, appkey string) Analyzer {
	analyzer := &AylienAnalyzer{AppID: appid, AppKey: appkey}
	err := analyzer.init()

	if err != nil {
		log.Printf("failed to create aylien api client")
		return nil
	}

	return analyzer
}
<|endoftext|>


<commit_before>package parser

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/bigquery"

	"github.com/m-lab/etl/metrics"
	"github.com/m-lab/etl/schema"
	"github.com/prometheus/client_golang/prometheus"
)

// TODO(JosephMarques) See if there is a better way of determining
// where to send the request (there almost certainly is)
var BaseURL = "https://annotator-dot-" +
	os.Getenv("GCLOUD_PROJECT") +
	".appspot.com/annotate?"

// AddMetaDataPTConnSpec takes a pointer to a
// MLabConnectionSpecification struct and a timestamp. With these, it
// will fetch the appropriate metadata and add it to the hop struct
// referenced by the pointer.
func AddMetaDataPTConnSpec(spec *schema.MLabConnectionSpecification, timestamp time.Time) {
	if spec == nil {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT ConnSpec was nil!!!"}).Inc()
		return
	}
	// Time the response
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.AnnotationTimeSummary.
			With(prometheus.Labels{"test_type": "PT"}).
			Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)
	if spec.Server_ip != "" {
		GetAndInsertGeolocationIPStruct(&spec.Server_geolocation, spec.Server_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT ConnSpec had no server_ip!"}).Inc()
	}
	if spec.Client_ip != "" {
		GetAndInsertGeolocationIPStruct(&spec.Client_geolocation, spec.Client_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT ConnSpec had no client_ip!"}).Inc()
	}
}

// AddMetaDataPTHop takes a pointer to a ParisTracerouteHop and a
// timestamp. With these, it will fetch the appropriate metadata and
// add it to the hop struct referenced by the pointer.
func AddMetaDataPTHop(hop *schema.ParisTracerouteHop, timestamp time.Time) {
	if hop == nil {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT Hop was nil!!!"}).Inc()
		return
	}
	// Time the response
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.AnnotationTimeSummary.
			With(prometheus.Labels{"test_type": "PT-HOP"}).
			Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)
	if hop.Src_ip != "" {
		GetAndInsertGeolocationIPStruct(&hop.Src_geolocation, hop.Src_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT Hop had no src_ip!"}).Inc()
	}
	if hop.Dest_ip != "" {
		GetAndInsertGeolocationIPStruct(&hop.Dest_geolocation, hop.Dest_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT Hop had no dest_ip!"}).Inc()
	}
}

// GetAndInsertGeolocationIPStruct takes a NON-NIL pointer to a
// pre-allocated schema.GeolocationIP struct, an IP address, and a
// timestamp. It will connect to the annotation service, get the
// metadata, and insert the metadata into the reigion pointed to by
// the schema.GeolocationIP pointer.
func GetAndInsertGeolocationIPStruct(geo *schema.GeolocationIP, ip string, timestamp time.Time) {
	url := BaseURL + "ip_addr=" + url.QueryEscape(ip) +
		"&since_epoch=" + strconv.FormatInt(timestamp.Unix(), 10)
	annotationData := GetMetaData(url)
	if annotationData != nil && annotationData.Geo != nil {
		*geo = *annotationData.Geo
	}
}

// AddMetaDataNDTConnSpec takes a connection spec and a timestamp and
// annotates the connection spec with metadata associated with each IP
// Address. It will either sucessfully add the metadata or fail
// silently and make no changes.
func AddMetaDataNDTConnSpec(spec schema.Web100ValueMap, timestamp time.Time) {
	// Time the response
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.AnnotationTimeSummary.
			With(prometheus.Labels{"test_type": "NDT"}).
			Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)

	GetAndInsertMetaIntoNDTConnSpec("client", spec, timestamp)
	GetAndInsertMetaIntoNDTConnSpec("server", spec, timestamp)
}

// GetAndInsertNDT takes a timestamp, an NDT connection spec, and a
// string indicating whether it should get the metadata for the client
// end or the server end of the connection. It will either insert the
// data into the connection spec or silently fail.
func GetAndInsertMetaIntoNDTConnSpec(side string, spec schema.Web100ValueMap, timestamp time.Time) {
	ip, ok := spec.GetString([]string{side + "_ip"})
	if ok {
		url := BaseURL + "ip_addr=" + url.QueryEscape(ip) +
			"&since_epoch=" + strconv.FormatInt(timestamp.Unix(), 10)
		annotationData := GetMetaData(url)
		if annotationData != nil && annotationData.Geo != nil {
			CopyStructToMap(annotationData.Geo, spec.Get(side+"_geolocation"))
		} else {
			metrics.AnnotationErrorCount.With(prometheus.
				Labels{"source": "Couldn't get metadata for the " + side + " side."}).Inc()
		}
	}
}

// CopyStructToMap takes a POINTER to an arbitrary struct and copies
// it's fields into a value map. It will also make fields entirely
// lower case, for convienece when working with exported structs. Also,
// NEVER pass in something that is not a pointer to a struct, as this
// will cause a panic.
func CopyStructToMap(sourceStruct interface{}, destinationMap map[string]bigquery.Value) {
	structToCopy := reflect.ValueOf(sourceStruct).Elem()
	typeOfStruct := structToCopy.Type()
	for i := 0; i < typeOfStruct.NumField(); i++ {
		destinationMap[strings.ToLower(typeOfStruct.Field(i).Name)] =
			structToCopy.Field(i).Interface()
	}

}

// GetMetaData combines the functionality of QueryAnnotationService and
// ParseJSONMetaDataResponse to query the annotator service and return
// the corresponding MetaData if it can, or a nil pointer if it
// encounters any error and cannot get the data for any reason
func GetMetaData(url string) *schema.MetaData {
	// Query the service and grab the response safely
	annotatorResponse, err := QueryAnnotationService(url)
	if err != nil {
		metrics.AnnotationErrorCount.
			With(prometheus.Labels{"source": "Error querying annotation service"}).Inc()
		log.Println(err)
		return nil
	}

	// Safely parse the JSON response and pass it back to the caller
	metaDataFromResponse, err := ParseJSONMetaDataResponse(annotatorResponse)
	if err != nil {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "Failed to parse JSON"}).Inc()
		log.Println(err)
		return nil
	}
	return metaDataFromResponse
}

// QueryAnnotationService will connect to the annotation service and
// copy the body of a valid response to a byte slice and return it to a
// user, returning an error if any occurs
func QueryAnnotationService(url string) ([]byte, error) {
	// Make the actual request
	resp, err := http.Get(url)

	// Catch http errors
	if err != nil {
		metrics.AnnotationErrorCount.
			With(prometheus.Labels{"source": "Request to Annotator failed"}).Inc()
		return nil, err
	}

	// Catch errors reported by the service
	if resp.StatusCode != http.StatusOK {
		metrics.AnnotationErrorCount.
			With(prometheus.Labels{"source": "Webserver gave non-ok response"}).Inc()
		return nil, errors.New("URL:" + url + " gave response code " + resp.Status)
	}
	defer resp.Body.Close()

	// Copy response into a byte slice
	return ioutil.ReadAll(resp.Body)
}

// ParseJSONMetaDataResponse takes a byte slice containing the test of
// the JSON from the annotator service and parses it into a MetaData
// struct, for easy manipulation. It returns a pointer to the struct on
// success and an error if an error occurs.
func ParseJSONMetaDataResponse(jsonBuffer []byte) (*schema.MetaData, error) {
	parsedJSON := &schema.MetaData{}
	err := json.Unmarshal(jsonBuffer, parsedJSON)
	if err != nil {
		return nil, err
	}
	return parsedJSON, nil
}
<commit_msg>First attempt at making the pt annotator work. No tests yet.<commit_after>package parser

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/bigquery"

	"github.com/m-lab/etl/metrics"
	"github.com/m-lab/etl/schema"
	"github.com/prometheus/client_golang/prometheus"
)

// TODO(JosephMarques) See if there is a better way of determining
// where to send the request (there almost certainly is)
var BaseURL = "https://annotator-dot-" +
	os.Getenv("GCLOUD_PROJECT") +
	".appspot.com/annotate?"

// AddMetaDataPTConnSpec takes a pointer to a
// MLabConnectionSpecification struct and a timestamp. With these, it
// will fetch the appropriate metadata and add it to the hop struct
// referenced by the pointer.
func AddMetaDataPTConnSpec(spec *schema.MLabConnectionSpecification, timestamp time.Time) {
	if spec == nil {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT ConnSpec was nil!!!"}).Inc()
		return
	}
	// Time the response
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.AnnotationTimeSummary.
			With(prometheus.Labels{"test_type": "PT"}).
			Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)
	if spec.Server_ip != "" {
		GetAndInsertGeolocationIPStruct(&spec.Server_geolocation, spec.Server_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT ConnSpec had no server_ip!"}).Inc()
	}
	if spec.Client_ip != "" {
		GetAndInsertGeolocationIPStruct(&spec.Client_geolocation, spec.Client_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT ConnSpec had no client_ip!"}).Inc()
	}
}

// AddMetaDataPTHop takes a pointer to a ParisTracerouteHop and a
// timestamp. With these, it will fetch the appropriate metadata and
// add it to the hop struct referenced by the pointer.
func AddMetaDataPTHop(hop *schema.ParisTracerouteHop, timestamp time.Time) {
	if hop == nil {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT Hop was nil!!!"}).Inc()
		return
	}
	// Time the response
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.AnnotationTimeSummary.
			With(prometheus.Labels{"test_type": "PT-HOP"}).
			Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)
	if hop.Src_ip != "" {
		GetAndInsertGeolocationIPStruct(&hop.Src_geolocation, hop.Src_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT Hop had no src_ip!"}).Inc()
	}
	if hop.Dest_ip != "" {
		GetAndInsertGeolocationIPStruct(&hop.Dest_geolocation, hop.Dest_ip, timestamp)
	} else {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "PT Hop had no dest_ip!"}).Inc()
	}
}

func GetAndInsertGeolocationIPStruct(geo *schema.GeolocationIP, ip string, timestamp time.Time) {
	url := BaseURL + "ip_addr=" + url.QueryEscape(ip) +
		"&since_epoch=" + strconv.FormatInt(timestamp.Unix(), 10)
	annotationData := GetMetaData(url)
	if annotationData != nil && annotationData.Geo != nil {
		*geo = *annotationData.Geo
	}
}

// AddMetaDataNDTConnSpec takes a connection spec and a timestamp and
// annotates the connection spec with metadata associated with each IP
// Address. It will either sucessfully add the metadata or fail
// silently and make no changes.
func AddMetaDataNDTConnSpec(spec schema.Web100ValueMap, timestamp time.Time) {
	// Time the response
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.AnnotationTimeSummary.
			With(prometheus.Labels{"test_type": "NDT"}).
			Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)

	GetAndInsertMetaIntoNDTConnSpec("client", spec, timestamp)
	GetAndInsertMetaIntoNDTConnSpec("server", spec, timestamp)
}

// GetAndInsertNDT takes a timestamp, an NDT connection spec, and a
// string indicating whether it should get the metadata for the client
// end or the server end of the connection. It will either insert the
// data into the connection spec or silently fail.
func GetAndInsertMetaIntoNDTConnSpec(side string, spec schema.Web100ValueMap, timestamp time.Time) {
	ip, ok := spec.GetString([]string{side + "_ip"})
	if ok {
		url := BaseURL + "ip_addr=" + url.QueryEscape(ip) +
			"&since_epoch=" + strconv.FormatInt(timestamp.Unix(), 10)
		annotationData := GetMetaData(url)
		if annotationData != nil && annotationData.Geo != nil {
			CopyStructToMap(annotationData.Geo, spec.Get(side+"_geolocation"))
		} else {
			metrics.AnnotationErrorCount.With(prometheus.
				Labels{"source": "Couldn't get metadata for the " + side + " side."}).Inc()
		}
	}
}

// CopyStructToMap takes a POINTER to an arbitrary struct and copies
// it's fields into a value map. It will also make fields entirely
// lower case, for convienece when working with exported structs. Also,
// NEVER pass in something that is not a pointer to a struct, as this
// will cause a panic.
func CopyStructToMap(sourceStruct interface{}, destinationMap map[string]bigquery.Value) {
	structToCopy := reflect.ValueOf(sourceStruct).Elem()
	typeOfStruct := structToCopy.Type()
	for i := 0; i < typeOfStruct.NumField(); i++ {
		destinationMap[strings.ToLower(typeOfStruct.Field(i).Name)] =
			structToCopy.Field(i).Interface()
	}

}

// GetMetaData combines the functionality of QueryAnnotationService and
// ParseJSONMetaDataResponse to query the annotator service and return
// the corresponding MetaData if it can, or a nil pointer if it
// encounters any error and cannot get the data for any reason
func GetMetaData(url string) *schema.MetaData {
	// Query the service and grab the response safely
	annotatorResponse, err := QueryAnnotationService(url)
	if err != nil {
		metrics.AnnotationErrorCount.
			With(prometheus.Labels{"source": "Error querying annotation service"}).Inc()
		log.Println(err)
		return nil
	}

	// Safely parse the JSON response and pass it back to the caller
	metaDataFromResponse, err := ParseJSONMetaDataResponse(annotatorResponse)
	if err != nil {
		metrics.AnnotationErrorCount.With(prometheus.
			Labels{"source": "Failed to parse JSON"}).Inc()
		log.Println(err)
		return nil
	}
	return metaDataFromResponse
}

// QueryAnnotationService will connect to the annotation service and
// copy the body of a valid response to a byte slice and return it to a
// user, returning an error if any occurs
func QueryAnnotationService(url string) ([]byte, error) {
	// Make the actual request
	resp, err := http.Get(url)

	// Catch http errors
	if err != nil {
		metrics.AnnotationErrorCount.
			With(prometheus.Labels{"source": "Request to Annotator failed"}).Inc()
		return nil, err
	}

	// Catch errors reported by the service
	if resp.StatusCode != http.StatusOK {
		metrics.AnnotationErrorCount.
			With(prometheus.Labels{"source": "Webserver gave non-ok response"}).Inc()
		return nil, errors.New("URL:" + url + " gave response code " + resp.Status)
	}
	defer resp.Body.Close()

	// Copy response into a byte slice
	return ioutil.ReadAll(resp.Body)
}

// ParseJSONMetaDataResponse takes a byte slice containing the test of
// the JSON from the annotator service and parses it into a MetaData
// struct, for easy manipulation. It returns a pointer to the struct on
// success and an error if an error occurs.
func ParseJSONMetaDataResponse(jsonBuffer []byte) (*schema.MetaData, error) {
	parsedJSON := &schema.MetaData{}
	err := json.Unmarshal(jsonBuffer, parsedJSON)
	if err != nil {
		return nil, err
	}
	return parsedJSON, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package proxyupdater_test

import (
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils/proxy"
	gc "gopkg.in/check.v1"

	apitesting "github.com/juju/juju/api/base/testing"
	"github.com/juju/juju/api/proxyupdater"
	"github.com/juju/juju/apiserver/params"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/workertest"
	"github.com/juju/names"
)

type ProxyUpdaterSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&ProxyUpdaterSuite{})

func newAPI(c *gc.C, args []apitesting.CheckArgs) (*int, *proxyupdater.API) {
	var called int
	apiCaller := apitesting.CheckingAPICallerMultiArgs(c, args, &called, nil)
	api, err := proxyupdater.NewAPI(apiCaller, names.NewUnitTag("u/0"))
	c.Assert(err, gc.IsNil)
	c.Assert(api, gc.NotNil)
	c.Assert(called, gc.Equals, 0)

	return &called, api
}

func (s *ProxyUpdaterSuite) TestNewAPISuccess(c *gc.C) {
	newAPI(c, nil)
}

func (s *ProxyUpdaterSuite) TestNilAPICallerFails(c *gc.C) {
	api, err := proxyupdater.NewAPI(nil, names.NewUnitTag("u/0"))
	c.Check(api, gc.IsNil)
	c.Check(err, gc.ErrorMatches, "caller is nil")
}

func (s *ProxyUpdaterSuite) TestNilTagFails(c *gc.C) {
	var args []apitesting.CheckArgs
	var calls int
	apiCaller := apitesting.CheckingAPICallerMultiArgs(c, args, &calls, nil)
	api, err := proxyupdater.NewAPI(apiCaller, nil)
	c.Check(api, gc.IsNil)
	c.Check(err, gc.ErrorMatches, "tag is nil")
}

func (s *ProxyUpdaterSuite) TestWatchForProxyConfigAndAPIHostPortChanges(c *gc.C) {
	res := params.NotifyWatchResults{
		Results: []params.NotifyWatchResult{params.NotifyWatchResult{
			NotifyWatcherId: "4242",
		}},
	}
	args := []apitesting.CheckArgs{{
		Facade:  "ProxyUpdater",
		Method:  "WatchForProxyConfigAndAPIHostPortChanges",
		Results: res,
	}}
	called, api := newAPI(c, args)

	watcher, err := api.WatchForProxyConfigAndAPIHostPortChanges()
	workertest.CleanKill(c, watcher)
	c.Check(*called, gc.Equals, 2)
	c.Check(err, jc.ErrorIsNil)
}

func (s *ProxyUpdaterSuite) TestProxyConfig(c *gc.C) {
	conf := params.ProxyConfigResult{
		ProxySettings: params.ProxyConfig{
			HTTP:    "http",
			HTTPS:   "https",
			FTP:     "ftp",
			NoProxy: "NoProxy",
		},
		APTProxySettings: params.ProxyConfig{
			HTTP:    "http-apt",
			HTTPS:   "https-apt",
			FTP:     "ftp-apt",
			NoProxy: "NoProxy-apt",
		},
	}
	expected := params.ProxyConfigResults{
		Results: []params.ProxyConfigResult{conf},
	}

	args := []apitesting.CheckArgs{{
		Facade:  "ProxyUpdater",
		Method:  "ProxyConfig",
		Results: expected,
	}}
	called, api := newAPI(c, args)

	proxySettings, APTProxySettings, err := api.ProxyConfig()
	c.Assert(*called, gc.Equals, 1)
	c.Assert(err, jc.ErrorIsNil)
	c.Check(proxySettings, jc.DeepEquals, proxy.Settings{
		Http:    "http",
		Https:   "https",
		Ftp:     "ftp",
		NoProxy: "NoProxy",
	})
	c.Check(APTProxySettings, jc.DeepEquals, proxy.Settings{
		Http:    "http-apt",
		Https:   "https-apt",
		Ftp:     "ftp-apt",
		NoProxy: "NoProxy-apt",
	})
}
<commit_msg>Fixed a test<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package proxyupdater_test

import (
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils/proxy"
	gc "gopkg.in/check.v1"

	apitesting "github.com/juju/juju/api/base/testing"
	"github.com/juju/juju/api/proxyupdater"
	"github.com/juju/juju/apiserver/params"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/workertest"
	"github.com/juju/names"
)

type ProxyUpdaterSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&ProxyUpdaterSuite{})

func newAPI(c *gc.C, args []apitesting.CheckArgs) (*int, *proxyupdater.API) {
	var called int
	apiCaller := apitesting.CheckingAPICallerMultiArgs(c, args, &called, nil)
	api, err := proxyupdater.NewAPI(apiCaller, names.NewUnitTag("u/0"))
	c.Assert(err, gc.IsNil)
	c.Assert(api, gc.NotNil)
	c.Assert(called, gc.Equals, 0)

	return &called, api
}

func (s *ProxyUpdaterSuite) TestNewAPISuccess(c *gc.C) {
	newAPI(c, nil)
}

func (s *ProxyUpdaterSuite) TestNilAPICallerFails(c *gc.C) {
	api, err := proxyupdater.NewAPI(nil, names.NewUnitTag("u/0"))
	c.Check(api, gc.IsNil)
	c.Check(err, gc.ErrorMatches, "caller is nil")
}

func (s *ProxyUpdaterSuite) TestNilTagFails(c *gc.C) {
	var args []apitesting.CheckArgs
	var calls int
	apiCaller := apitesting.CheckingAPICallerMultiArgs(c, args, &calls, nil)
	api, err := proxyupdater.NewAPI(apiCaller, nil)
	c.Check(api, gc.IsNil)
	c.Check(err, gc.ErrorMatches, "tag is nil")
}

func (s *ProxyUpdaterSuite) TestWatchForProxyConfigAndAPIHostPortChanges(c *gc.C) {
	res := params.NotifyWatchResults{
		Results: []params.NotifyWatchResult{params.NotifyWatchResult{
			NotifyWatcherId: "4242",
		}},
	}
	args := []apitesting.CheckArgs{{
		Facade:  "ProxyUpdater",
		Method:  "WatchForProxyConfigAndAPIHostPortChanges",
		Results: res,
	}}
	called, api := newAPI(c, args)

	watcher, err := api.WatchForProxyConfigAndAPIHostPortChanges()
	workertest.CleanKill(c, watcher)
	c.Check(*called, jc.GreaterThan, 0)
	c.Check(err, jc.ErrorIsNil)
}

func (s *ProxyUpdaterSuite) TestProxyConfig(c *gc.C) {
	conf := params.ProxyConfigResult{
		ProxySettings: params.ProxyConfig{
			HTTP:    "http",
			HTTPS:   "https",
			FTP:     "ftp",
			NoProxy: "NoProxy",
		},
		APTProxySettings: params.ProxyConfig{
			HTTP:    "http-apt",
			HTTPS:   "https-apt",
			FTP:     "ftp-apt",
			NoProxy: "NoProxy-apt",
		},
	}
	expected := params.ProxyConfigResults{
		Results: []params.ProxyConfigResult{conf},
	}

	args := []apitesting.CheckArgs{{
		Facade:  "ProxyUpdater",
		Method:  "ProxyConfig",
		Results: expected,
	}}
	called, api := newAPI(c, args)

	proxySettings, APTProxySettings, err := api.ProxyConfig()
	c.Assert(*called, gc.Equals, 1)
	c.Assert(err, jc.ErrorIsNil)
	c.Check(proxySettings, jc.DeepEquals, proxy.Settings{
		Http:    "http",
		Https:   "https",
		Ftp:     "ftp",
		NoProxy: "NoProxy",
	})
	c.Check(APTProxySettings, jc.DeepEquals, proxy.Settings{
		Http:    "http-apt",
		Https:   "https-apt",
		Ftp:     "ftp-apt",
		NoProxy: "NoProxy-apt",
	})
}
<|endoftext|>


<commit_before>package public

import (
	"github.com/ridewindx/mel"
	"sync"
	"unsafe"
	"sync/atomic"
	"encoding/base64"
	"bytes"
	"sort"
	"crypto/sha1"
	"encoding/hex"
	"crypto/subtle"
	"strconv"
)

type Server struct {
	*mel.Mel

	tokenMutex sync.Mutex
	token unsafe.Pointer

	aesKeyMutex sync.Mutex
	aesKey unsafe.Pointer
}

type Token struct {
	current string
	last string
}

type AESKey struct {
	current []byte
	last []byte
}

func (srv *Server) GetToken() (string, string) {
	p := (*Token)(atomic.LoadPointer(&srv.token))
	if p != nil {
		return p.current, p.last
	}
	return "", ""
}

func (srv *Server) SetToken(token string) {
	if token == "" {
		return
	}

	srv.tokenMutex.Lock()
	defer srv.tokenMutex.Unlock()

	current, _ := srv.GetToken()
	if token == current {
		return
	}

	t := Token{
		current: token,
		last: current,
	}
	atomic.StorePointer(&srv.token, unsafe.Pointer(&t))
}

func (srv *Server) deleteLastToken() {
	srv.tokenMutex.Lock()
	defer srv.tokenMutex.Unlock()

	current, last := srv.GetToken()
	if last == "" {
		return
	}

	t := Token{
		current: current,
	}
	atomic.StorePointer(&srv.token, unsafe.Pointer(&t))
}

func (srv *Server) GetAESKey() (string, string) {
	p := (*AESKey)(atomic.LoadPointer(&srv.aesKey))
	if p != nil {
		return p.current, p.last
	}
	return "", ""
}

func (srv *Server) SetAESKey(base64AESKey string) {
	if len(base64AESKey) != 43 {
		return
	}
	aesKey, err := base64.StdEncoding.DecodeString(base64AESKey + "=")
	if err != nil {
		return
	}

	srv.aesKeyMutex.Lock()
	defer srv.aesKeyMutex.Unlock()

	current, _ := srv.GetAESKey()
	if bytes.Equal(aesKey, current) {
		return
	}

	k := AESKey{
		current: aesKey,
		last: current,
	}
	atomic.StorePointer(&srv.aesKey, unsafe.Pointer(&k))
}

func (srv *Server) deleteAESKey() {
	srv.aesKeyMutex.Lock()
	defer srv.aesKeyMutex.Unlock()

	current, last := srv.GetAESKey()
	if last == "" {
		return
	}

	k := AESKey{
		current: current,
	}
	atomic.StorePointer(&srv.aesKey, unsafe.Pointer(&k))
}

func NewServer() *Server {
	srv := &Server{
		Mel: mel.New(),
	}

	validateSign := func(c *mel.Context) bool {
		signature := c.Query("signature")
		timestamp := c.Query("timestamp")
		nonce := c.Query("nonce")

		currentToken, lastToken := srv.GetToken()
		token := currentToken

		isValid := func() bool {
			computedSignature := Sign(token, timestamp, nonce)
			r := subtle.ConstantTimeCompare([]byte(signature), []byte(computedSignature))
			return r == 1
		}

		if isValid() {
			srv.deleteLastToken()
			return true
		}

		if lastToken == "" {
			return false
		}

		token = lastToken
		return isValid()
	}

	srv.Get("/", func(c *mel.Context) {
		if validateSign(c) {
			echostr := c.Query("echostr")
			c.Text(200, echostr)
		}
	})

	srv.Post("/", func(c *mel.Context) {
		encryptType := c.Query("encrypt_type")
		switch encryptType {
		case "aes":
			if !validateSign(c) {
				return
			}

			msgSignature := c.Query("msg_signature")
			timestamp, err := strconv.ParseInt(c.Query("timestamp"), 10, 64)
			if err != nil {
				return
			}

		case "", "raw":
			if !validateSign(c) {
				return
			}

		default:
			return
		}
	})

	return srv
}

func Sign(token, timestamp, nonce string) string {
	strs := sort.StringSlice{token, timestamp, nonce}
	strs.Sort()

	buf := make([]byte, 0, len(token)+len(timestamp)+len(nonce))
	buf = append(buf, strs[0]...)
	buf = append(buf, strs[1]...)
	buf = append(buf, strs[2]...)

	hashsum := sha1.Sum(buf)
	return hex.EncodeToString(hashsum[:])
}
<commit_msg>Update server.go<commit_after>package public

import (
	"github.com/ridewindx/mel"
	"github.com/ridewindx/mel/binding"
	"sync"
	"unsafe"
	"sync/atomic"
	"encoding/base64"
	"bytes"
	"sort"
	"crypto/sha1"
	"encoding/hex"
	"encoding/xml"
	"crypto/subtle"
	"strconv"
    "bufio"
)

type Server struct {
	*mel.Mel

	appId string
	userId string

	tokenMutex sync.Mutex
	token unsafe.Pointer

	aesKeyMutex sync.Mutex
	aesKey unsafe.Pointer
}

type Token struct {
	current string
	last string
}

type AESKey struct {
	current []byte
	last []byte
}

func (srv *Server) GetToken() (string, string) {
	p := (*Token)(atomic.LoadPointer(&srv.token))
	if p != nil {
		return p.current, p.last
	}
	return "", ""
}

func (srv *Server) SetToken(token string) {
	if token == "" {
		return
	}

	srv.tokenMutex.Lock()
	defer srv.tokenMutex.Unlock()

	current, _ := srv.GetToken()
	if token == current {
		return
	}

	t := Token{
		current: token,
		last: current,
	}
	atomic.StorePointer(&srv.token, unsafe.Pointer(&t))
}

func (srv *Server) deleteLastToken() {
	srv.tokenMutex.Lock()
	defer srv.tokenMutex.Unlock()

	current, last := srv.GetToken()
	if last == "" {
		return
	}

	t := Token{
		current: current,
	}
	atomic.StorePointer(&srv.token, unsafe.Pointer(&t))
}

func (srv *Server) GetAESKey() (string, string) {
	p := (*AESKey)(atomic.LoadPointer(&srv.aesKey))
	if p != nil {
		return p.current, p.last
	}
	return "", ""
}

func (srv *Server) SetAESKey(base64AESKey string) {
	if len(base64AESKey) != 43 {
		return
	}
	aesKey, err := base64.StdEncoding.DecodeString(base64AESKey + "=")
	if err != nil {
		return
	}

	srv.aesKeyMutex.Lock()
	defer srv.aesKeyMutex.Unlock()

	current, _ := srv.GetAESKey()
	if bytes.Equal(aesKey, current) {
		return
	}

	k := AESKey{
		current: aesKey,
		last: current,
	}
	atomic.StorePointer(&srv.aesKey, unsafe.Pointer(&k))
}

func (srv *Server) deleteLastAESKey() {
	srv.aesKeyMutex.Lock()
	defer srv.aesKeyMutex.Unlock()

	current, last := srv.GetAESKey()
	if last == "" {
		return
	}

	k := AESKey{
		current: current,
	}
	atomic.StorePointer(&srv.aesKey, unsafe.Pointer(&k))
}

func NewServer() *Server {
	srv := &Server{
		Mel: mel.New(),
	}

	equal := func(a, b string) bool {
		return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
	}

	validateSign := func(c *mel.Context) bool {
		signature := c.Query("signature")
		timestamp := c.Query("timestamp")
		nonce := c.Query("nonce")

		currentToken, lastToken := srv.GetToken()
		token := currentToken

		isValid := func() bool {
			computedSignature := Sign(token, timestamp, nonce)
			return equal(signature, computedSignature)
		}

		if isValid() {
			srv.deleteLastToken()
			return true
		}

		if lastToken == "" {
			return false
		}

		token = lastToken
		return isValid()
	}

	type EncryptMsg struct {
		ToUserName string `xml:",cdata"`
		Encrypt string `xml:",cdata"`
	}

	srv.Get("/", func(c *mel.Context) {
		if validateSign(c) {
			echostr := c.Query("echostr")
			c.Text(200, echostr)
		}
	})

	srv.Post("/", func(c *mel.Context) {
		encryptType := c.Query("encrypt_type")
		timestamp := c.Query("timestamp")
		nonce := c.Query("nonce")

		switch encryptType {
		case "aes":
			if !validateSign(c) {
				return
			}

			msgSign := c.Query("msg_signature")
			timestamp, err := strconv.ParseInt(c.Query("timestamp"), 10, 64)
			if err != nil {
				return
			}

			var obj EncryptMsg
			err := c.BindWith(&obj, binding.XML)
			if err != nil {
				return
			}

			if srv.userId != "" && !equal(obj.ToUserName, userId) {
				return
			}

			computedSign := MsgSign(token, timestamp, nonce, obj.Encrypt)
			if !equal(computedSign, msgSign) {
				return
			}

			encryptedMsg, err := base64.StdEncoding.DecodeString(obj.Encrypt)
			if err != nil {
				return
			}

			current, last := srv.GetAESKey()
			aesKey := current
			random, msg, appId, err := decryptMsg(encryptedMsg, aesKey)
            if err != nil {
				if last == "" {
					return
				}
				aesKey = last
				random, msg, appId, err := decryptMsg(encryptedMsg, aesKey)
                if err != nil {
					return
				}
			} else {
				srv.deleteLastAESKey()
			}
			if srv.appId != "" && string(appId) != srv.appId {
				return
			}

		case "", "raw":
			if !validateSign(c) {
				return
			}

		default:
			return
		}
	})

	return srv
}

func Sign(token, timestamp, nonce string) string {
	strs := sort.StringSlice{token, timestamp, nonce}
	strs.Sort()

	buf := make([]byte, 0, len(token)+len(timestamp)+len(nonce))
	buf = append(buf, strs[0]...)
	buf = append(buf, strs[1]...)
	buf = append(buf, strs[2]...)

	hashsum := sha1.Sum(buf)
	return hex.EncodeToString(hashsum[:])
}

func MsgSign(token, timestamp, nonce, encryptedMsg string) string {
	strs := sort.StringSlice{token, timestamp, nonce, encryptedMsg}
	strs.Sort()

	h := sha1.New()

	bufw := bufio.NewWriterSize(h, 1024)
	bufw.WriteString(strs[0])
	bufw.WriteString(strs[1])
	bufw.WriteString(strs[2])
	bufw.WriteString(strs[3])
	bufw.Flush()

	hashsum := h.Sum(nil)
	return hex.EncodeToString(hashsum)
}
<|endoftext|>


<commit_before>package gdrj

import (
	"errors"

	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

var (
	NetMarginPLCode   string = "PL24NM"
	NetMarginPLHeader string = "Net Margin"
)

type PLModel struct {
	orm.ModelBase `json:"-" bson:"-"`
	ID            string `json:"_id" bson:"_id"` //PLCode
	OrderIndex    string
	PLHeader1     string
	PLHeader2     string
	PLHeader3     string
	Amount        float64
	GLReff        string
}

func (t *PLModel) RecordID() interface{} {
	return t.ID
}

func (t *PLModel) TableName() string {
	return "plmodel"
}

func PLModelGetByID(id string) *PLModel {
	t := new(PLModel)
	DB().GetById(t, id)
	return t
}

func PLModelGetAll() ([]*PLModel, error) {
	cursor, err := DB().Find(new(PLModel), nil)
	if err != nil {
		return nil, err
	}

	result := []*PLModel{}
	err = cursor.Fetch(&result, 0, false)
	if err != nil {
		return nil, err
	}
	cursor.Close()

	hasNetMargin := false
	for _, each := range result {
		// put Adv & Promo below the Gross Margin
		if each.ID == "PL32A" {
			each.OrderIndex = "PL0027A"
		}

		// if net margin is not yet added to pnl
		if each.PLHeader3 == NetMarginPLHeader && !hasNetMargin {
			hasNetMargin = true
		}
	}

	// if net margin is not yet added to pnl
	// inject fake one, the amount will be calculated from gross sales - a&p
	if !hasNetMargin {
		plNetMargin := new(PLModel)
		plNetMargin.ID = NetMarginPLCode
		plNetMargin.PLHeader1 = NetMarginPLHeader
		plNetMargin.PLHeader2 = plNetMargin.PLHeader1
		plNetMargin.PLHeader3 = plNetMargin.PLHeader1
		plNetMargin.OrderIndex = "PL0027B"
		result = append(result, plNetMargin)
	}

	return result, nil
}

func (t *PLModel) Save() error {
	e := Save(t)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", t.TableName(), "save", e.Error()))
	}
	return e
}

func (t *PLModel) Delete() error {
	e := Delete(t)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", t.TableName(), "delete", e.Error()))
	}
	return e
}
<commit_msg>no message<commit_after>package gdrj

import (
	"errors"

	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

var (
	NetMarginPLCode   string = "PL74D"
	NetMarginPLHeader string = "Net Margin"
)

type PLModel struct {
	orm.ModelBase `json:"-" bson:"-"`
	ID            string `json:"_id" bson:"_id"` //PLCode
	OrderIndex    string
	PLHeader1     string
	PLHeader2     string
	PLHeader3     string
	Amount        float64
	GLReff        string
}

func (t *PLModel) RecordID() interface{} {
	return t.ID
}

func (t *PLModel) TableName() string {
	return "plmodel"
}

func PLModelGetByID(id string) *PLModel {
	t := new(PLModel)
	DB().GetById(t, id)
	return t
}

func PLModelGetAll() ([]*PLModel, error) {
	cursor, err := DB().Find(new(PLModel), nil)
	if err != nil {
		return nil, err
	}

	result := []*PLModel{}
	err = cursor.Fetch(&result, 0, false)
	if err != nil {
		return nil, err
	}
	cursor.Close()

	hasNetMargin := false
	for _, each := range result {
		// put Adv & Promo below the Gross Margin
		if each.ID == "PL32A" {
			each.OrderIndex = "PL0027A"
		}

		// if net margin is not yet added to pnl
		if each.PLHeader3 == NetMarginPLHeader && !hasNetMargin {
			hasNetMargin = true
		}
	}

	// if net margin is not yet added to pnl
	// inject fake one, the amount will be calculated from gross sales - a&p
	if !hasNetMargin {
		plNetMargin := new(PLModel)
		plNetMargin.ID = NetMarginPLCode
		plNetMargin.PLHeader1 = NetMarginPLHeader
		plNetMargin.PLHeader2 = plNetMargin.PLHeader1
		plNetMargin.PLHeader3 = plNetMargin.PLHeader1
		plNetMargin.OrderIndex = "PL0027C"
		result = append(result, plNetMargin)
	}

	return result, nil
}

func (t *PLModel) Save() error {
	e := Save(t)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", t.TableName(), "save", e.Error()))
	}
	return e
}

func (t *PLModel) Delete() error {
	e := Delete(t)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", t.TableName(), "delete", e.Error()))
	}
	return e
}
<|endoftext|>


<commit_before>package model

import (
	"time"
)

type Project struct {
	ID        int       `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}
<commit_msg>Simple required validation for project model<commit_after>package model

import (
	"time"
)

type Project struct {
	ID        int       `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func (p Project) Errors() map[string][]string {
	errors := make(map[string][]string)

	if len(p.Name) <= 0 {
		errors["name"] = append(errors["name"], "is required")
	}

	return errors
}
<|endoftext|>


<commit_before>package layout

import (
	"bytes"
	"io/ioutil"
	"log"
	"sync"
	"text/template"

	"github.com/goccy/go-yaml"
	tele "gopkg.in/tucnak/telebot.v3"
)

type (
	// Layout provides an interface to interact with the layout,
	// parsed from the config file and locales.
	Layout struct {
		pref  *tele.Settings
		mu    sync.RWMutex // protects ctxs
		ctxs  map[tele.Context]string
		funcs template.FuncMap

		buttons map[string]Button
		markups map[string]Markup
		locales map[string]*template.Template

		*Config
	}

	// Button is a shortcut for tele.Btn.
	Button = tele.Btn

	// Markup represents layout-specific markup to be parsed.
	Markup struct {
		inline          *bool
		keyboard        *template.Template
		ResizeKeyboard  *bool `yaml:"resize_keyboard,omitempty"` // nil == true
		ForceReply      bool  `yaml:"force_reply,omitempty"`
		OneTimeKeyboard bool  `yaml:"one_time_keyboard,omitempty"`
		RemoveKeyboard  bool  `yaml:"remove_keyboard,omitempty"`
		Selective       bool  `yaml:"selective,omitempty"`
	}
)

// New reads and parses the given layout file.
func New(path string) (*Layout, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	lt := Layout{
		ctxs:  make(map[tele.Context]string),
		funcs: make(template.FuncMap),
	}

	for k, v := range funcs {
		lt.funcs[k] = v
	}

	return &lt, yaml.Unmarshal(data, &lt)
}

var funcs = template.FuncMap{
	// Built-in blank and helper functions.
	"locale": func() string { return "" },
	"config": func(string) string { return "" },
	"text":   func(string) string { return "" },
}

// AddFunc adds the given function to the template FuncMap.
// Note: to make it come into effect, always add functions before New().
func AddFunc(key string, fn interface{}) {
	funcs[key] = fn
}

// AddFuncs extends the template FuncMap with the given one.
// Note: to make it come into effect, always add functions before New().
func AddFuncs(fm template.FuncMap) {
	for k, v := range fm {
		funcs[k] = v
	}
}

// Settings returns built telebot Settings required for bot initialising.
//
//		settings:
//		  url: (custom url if needed)
//		  token: (not recommended)
//		  updates: (chan capacity)
//		  locales_dir: (optional)
//		  token_env: (token env var name, example: TOKEN)
// 		  parse_mode: (default parse mode)
// 		  long_poller: (long poller settings)
//		  webhook: (or webhook settings)
//
// Usage:
//		lt, err := layout.New("bot.yml")
//		b, err := tele.NewBot(lt.Settings())
//		// That's all!
//
func (lt *Layout) Settings() tele.Settings {
	if lt.pref == nil {
		panic("telebot/layout: settings is empty")
	}
	return *lt.pref
}

// Locale returns the context locale.
func (lt *Layout) Locale(c tele.Context) (string, bool) {
	lt.mu.RLock()
	defer lt.mu.RUnlock()
	locale, ok := lt.ctxs[c]
	return locale, ok
}

// SetLocale allows you to change a locale for the passed context.
func (lt *Layout) SetLocale(c tele.Context, locale string) {
	lt.mu.Lock()
	lt.ctxs[c] = locale
	lt.mu.Unlock()
}

// Text returns a text, which locale is dependent on the context.
// The given optional argument will be passed to the template engine.
//
// Example of en.yml:
//		start: Hi, {{.FirstName}}!
//
// Usage:
//		func OnStart(c tele.Context) error {
//			return c.Send(lt.Text(c, "start", c.Sender()))
//		}
//
func (lt *Layout) Text(c tele.Context, k string, args ...interface{}) string {
	locale, ok := lt.Locale(c)
	if !ok {
		return ""
	}

	return lt.TextLocale(locale, k, args...)
}

// TextLocale returns a localised text processed with standard template engine.
// See Text for more details.
func (lt *Layout) TextLocale(locale, k string, args ...interface{}) string {
	tmpl, ok := lt.locales[locale]
	if !ok {
		return ""
	}

	var arg interface{}
	if len(args) > 0 {
		arg = args[0]
	}

	var buf bytes.Buffer
	if err := lt.template(tmpl, locale).ExecuteTemplate(&buf, k, arg); err != nil {
		log.Println("telebot/layout:", err)
	}

	return buf.String()
}

// Callback returns casted to CallbackEndpoint button, which mostly
// useful for handlers registering.
//
// Example:
//
//		// Handling settings button
//		b.Handle(lt.Callback("settings"), OnSettings)
//
func (lt *Layout) Callback(k string) tele.CallbackEndpoint {
	btn, ok := lt.buttons[k]
	if !ok {
		return nil
	}
	return &btn
}

// Button returns a button, which locale is dependent on the context.
// The given optional argument will be passed to the template engine.
//
//		buttons:
//		  item:
//		    unique: item
//		    callback_data: {{.ID}}
//		    text: Item #{{.Number}}
//
// Usage:
//		btns := make([]tele.Btn, len(items))
//		for i, item := range items {
//			btns[i] = lt.Button(c, "item", struct {
//				Number int
//				Item   Item
//			}{
//				Number: i,
//				Item:   item,
//			})
//		}
//
//		m := b.NewMarkup()
//		m.Inline(m.Row(btns...))
//		// Your generated markup is ready.
//
func (lt *Layout) Button(c tele.Context, k string, args ...interface{}) *tele.Btn {
	locale, ok := lt.Locale(c)
	if !ok {
		return nil
	}

	return lt.ButtonLocale(locale, k, args...)
}

// ButtonLocale returns a localised button processed with standard template engine.
// See Button for more details.
func (lt *Layout) ButtonLocale(locale, k string, args ...interface{}) *tele.Btn {
	btn, ok := lt.buttons[k]
	if !ok {
		return nil
	}

	var arg interface{}
	if len(args) > 0 {
		arg = args[0]
	}

	data, err := yaml.Marshal(btn)
	if err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	tmpl, err := lt.template(template.New(k), locale).Parse(string(data))
	if err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, arg); err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	if err := yaml.Unmarshal(buf.Bytes(), &btn); err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	return &btn
}

// Markup returns a markup, which locale is dependent on the context.
// The given optional argument will be passed to the template engine.
//
//		buttons:
//		  settings: 'Settings'
//		markups:
//		  menu:
//		    - [settings]
//
// Usage:
//		func OnStart(c tele.Context) error {
//			return c.Send(
//				lt.Text(c, "start"),
//				lt.Markup(c, "menu"))
//		}
//
func (lt *Layout) Markup(c tele.Context, k string, args ...interface{}) *tele.ReplyMarkup {
	locale, ok := lt.Locale(c)
	if !ok {
		return nil
	}

	return lt.MarkupLocale(locale, k, args...)
}

// MarkupLocale returns a localised markup processed with standard template engine.
// See Markup for more details.
func (lt *Layout) MarkupLocale(locale, k string, args ...interface{}) *tele.ReplyMarkup {
	markup, ok := lt.markups[k]
	if !ok {
		return nil
	}

	var arg interface{}
	if len(args) > 0 {
		arg = args[0]
	}

	var buf bytes.Buffer
	if err := lt.template(markup.keyboard, locale).Execute(&buf, arg); err != nil {
		log.Println("telebot/layout:", err)
	}

	r := &tele.ReplyMarkup{}
	if *markup.inline {
		if err := yaml.Unmarshal(buf.Bytes(), &r.InlineKeyboard); err != nil {
			log.Println("telebot/layout:", err)
		}
	} else {
		r.ResizeKeyboard = markup.ResizeKeyboard == nil || *markup.ResizeKeyboard
		r.ForceReply = markup.ForceReply
		r.OneTimeKeyboard = markup.OneTimeKeyboard
		r.RemoveKeyboard = markup.RemoveKeyboard
		r.Selective = markup.Selective

		if err := yaml.Unmarshal(buf.Bytes(), &r.ReplyKeyboard); err != nil {
			log.Println("telebot/layout:", err)
		}
	}

	return r
}

func (lt *Layout) template(tmpl *template.Template, locale string) *template.Template {
	funcs := make(template.FuncMap)

	// Redefining built-in blank functions
	funcs["config"] = lt.String
	funcs["text"] = func(k string) string { return lt.TextLocale(locale, k) }
	funcs["locale"] = func() string { return locale }

	return tmpl.Funcs(funcs)
}
<commit_msg>layout: implement Locales function<commit_after>package layout

import (
	"bytes"
	"io/ioutil"
	"log"
	"sync"
	"text/template"

	"github.com/goccy/go-yaml"
	tele "gopkg.in/tucnak/telebot.v3"
)

type (
	// Layout provides an interface to interact with the layout,
	// parsed from the config file and locales.
	Layout struct {
		pref  *tele.Settings
		mu    sync.RWMutex // protects ctxs
		ctxs  map[tele.Context]string
		funcs template.FuncMap

		buttons map[string]Button
		markups map[string]Markup
		locales map[string]*template.Template

		*Config
	}

	// Button is a shortcut for tele.Btn.
	Button = tele.Btn

	// Markup represents layout-specific markup to be parsed.
	Markup struct {
		inline          *bool
		keyboard        *template.Template
		ResizeKeyboard  *bool `yaml:"resize_keyboard,omitempty"` // nil == true
		ForceReply      bool  `yaml:"force_reply,omitempty"`
		OneTimeKeyboard bool  `yaml:"one_time_keyboard,omitempty"`
		RemoveKeyboard  bool  `yaml:"remove_keyboard,omitempty"`
		Selective       bool  `yaml:"selective,omitempty"`
	}
)

// New reads and parses the given layout file.
func New(path string) (*Layout, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	lt := Layout{
		ctxs:  make(map[tele.Context]string),
		funcs: make(template.FuncMap),
	}

	for k, v := range funcs {
		lt.funcs[k] = v
	}

	return &lt, yaml.Unmarshal(data, &lt)
}

var funcs = template.FuncMap{
	// Built-in blank and helper functions.
	"locale": func() string { return "" },
	"config": func(string) string { return "" },
	"text":   func(string) string { return "" },
}

// AddFunc adds the given function to the template FuncMap.
// Note: to make it come into effect, always add functions before New().
func AddFunc(key string, fn interface{}) {
	funcs[key] = fn
}

// AddFuncs extends the template FuncMap with the given one.
// Note: to make it come into effect, always add functions before New().
func AddFuncs(fm template.FuncMap) {
	for k, v := range fm {
		funcs[k] = v
	}
}

// Settings returns built telebot Settings required for bot initialising.
//
//		settings:
//		  url: (custom url if needed)
//		  token: (not recommended)
//		  updates: (chan capacity)
//		  locales_dir: (optional)
//		  token_env: (token env var name, example: TOKEN)
// 		  parse_mode: (default parse mode)
// 		  long_poller: (long poller settings)
//		  webhook: (or webhook settings)
//
// Usage:
//		lt, err := layout.New("bot.yml")
//		b, err := tele.NewBot(lt.Settings())
//		// That's all!
//
func (lt *Layout) Settings() tele.Settings {
	if lt.pref == nil {
		panic("telebot/layout: settings is empty")
	}
	return *lt.pref
}

// Locales returns all presented locales.
func (lt *Layout) Locales() []string {
	var keys []string
	for k := range lt.locales {
		keys = append(keys, k)
	}
	return keys
}

// Locale returns the context locale.
func (lt *Layout) Locale(c tele.Context) (string, bool) {
	lt.mu.RLock()
	defer lt.mu.RUnlock()
	locale, ok := lt.ctxs[c]
	return locale, ok
}

// SetLocale allows you to change a locale for the passed context.
func (lt *Layout) SetLocale(c tele.Context, locale string) {
	lt.mu.Lock()
	lt.ctxs[c] = locale
	lt.mu.Unlock()
}

// Text returns a text, which locale is dependent on the context.
// The given optional argument will be passed to the template engine.
//
// Example of en.yml:
//		start: Hi, {{.FirstName}}!
//
// Usage:
//		func OnStart(c tele.Context) error {
//			return c.Send(lt.Text(c, "start", c.Sender()))
//		}
//
func (lt *Layout) Text(c tele.Context, k string, args ...interface{}) string {
	locale, ok := lt.Locale(c)
	if !ok {
		return ""
	}

	return lt.TextLocale(locale, k, args...)
}

// TextLocale returns a localised text processed with standard template engine.
// See Text for more details.
func (lt *Layout) TextLocale(locale, k string, args ...interface{}) string {
	tmpl, ok := lt.locales[locale]
	if !ok {
		return ""
	}

	var arg interface{}
	if len(args) > 0 {
		arg = args[0]
	}

	var buf bytes.Buffer
	if err := lt.template(tmpl, locale).ExecuteTemplate(&buf, k, arg); err != nil {
		log.Println("telebot/layout:", err)
	}

	return buf.String()
}

// Callback returns casted to CallbackEndpoint button, which mostly
// useful for handlers registering.
//
// Example:
//
//		// Handling settings button
//		b.Handle(lt.Callback("settings"), OnSettings)
//
func (lt *Layout) Callback(k string) tele.CallbackEndpoint {
	btn, ok := lt.buttons[k]
	if !ok {
		return nil
	}
	return &btn
}

// Button returns a button, which locale is dependent on the context.
// The given optional argument will be passed to the template engine.
//
//		buttons:
//		  item:
//		    unique: item
//		    callback_data: {{.ID}}
//		    text: Item #{{.Number}}
//
// Usage:
//		btns := make([]tele.Btn, len(items))
//		for i, item := range items {
//			btns[i] = lt.Button(c, "item", struct {
//				Number int
//				Item   Item
//			}{
//				Number: i,
//				Item:   item,
//			})
//		}
//
//		m := b.NewMarkup()
//		m.Inline(m.Row(btns...))
//		// Your generated markup is ready.
//
func (lt *Layout) Button(c tele.Context, k string, args ...interface{}) *tele.Btn {
	locale, ok := lt.Locale(c)
	if !ok {
		return nil
	}

	return lt.ButtonLocale(locale, k, args...)
}

// ButtonLocale returns a localised button processed with standard template engine.
// See Button for more details.
func (lt *Layout) ButtonLocale(locale, k string, args ...interface{}) *tele.Btn {
	btn, ok := lt.buttons[k]
	if !ok {
		return nil
	}

	var arg interface{}
	if len(args) > 0 {
		arg = args[0]
	}

	data, err := yaml.Marshal(btn)
	if err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	tmpl, err := lt.template(template.New(k), locale).Parse(string(data))
	if err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, arg); err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	if err := yaml.Unmarshal(buf.Bytes(), &btn); err != nil {
		log.Println("telebot/layout:", err)
		return nil
	}

	return &btn
}

// Markup returns a markup, which locale is dependent on the context.
// The given optional argument will be passed to the template engine.
//
//		buttons:
//		  settings: 'Settings'
//		markups:
//		  menu:
//		    - [settings]
//
// Usage:
//		func OnStart(c tele.Context) error {
//			return c.Send(
//				lt.Text(c, "start"),
//				lt.Markup(c, "menu"))
//		}
//
func (lt *Layout) Markup(c tele.Context, k string, args ...interface{}) *tele.ReplyMarkup {
	locale, ok := lt.Locale(c)
	if !ok {
		return nil
	}

	return lt.MarkupLocale(locale, k, args...)
}

// MarkupLocale returns a localised markup processed with standard template engine.
// See Markup for more details.
func (lt *Layout) MarkupLocale(locale, k string, args ...interface{}) *tele.ReplyMarkup {
	markup, ok := lt.markups[k]
	if !ok {
		return nil
	}

	var arg interface{}
	if len(args) > 0 {
		arg = args[0]
	}

	var buf bytes.Buffer
	if err := lt.template(markup.keyboard, locale).Execute(&buf, arg); err != nil {
		log.Println("telebot/layout:", err)
	}

	r := &tele.ReplyMarkup{}
	if *markup.inline {
		if err := yaml.Unmarshal(buf.Bytes(), &r.InlineKeyboard); err != nil {
			log.Println("telebot/layout:", err)
		}
	} else {
		r.ResizeKeyboard = markup.ResizeKeyboard == nil || *markup.ResizeKeyboard
		r.ForceReply = markup.ForceReply
		r.OneTimeKeyboard = markup.OneTimeKeyboard
		r.RemoveKeyboard = markup.RemoveKeyboard
		r.Selective = markup.Selective

		if err := yaml.Unmarshal(buf.Bytes(), &r.ReplyKeyboard); err != nil {
			log.Println("telebot/layout:", err)
		}
	}

	return r
}

func (lt *Layout) template(tmpl *template.Template, locale string) *template.Template {
	funcs := make(template.FuncMap)

	// Redefining built-in blank functions
	funcs["config"] = lt.String
	funcs["text"] = func(k string) string { return lt.TextLocale(locale, k) }
	funcs["locale"] = func() string { return locale }

	return tmpl.Funcs(funcs)
}
<|endoftext|>


<commit_before>package pegasus

import (
	"github.com/HearthSim/hs-proto/go"
	"github.com/golang/protobuf/proto"
	"log"
	"time"
)

type Account struct {
	ID        int
	BnetID    int
	UpdatedAt time.Time
	Flags     int64

	Progress []SeasonProgress
	Licenses []License
}

func (v *Account) Init(sess *Session) {
	sess.RegisterUtilHandler(0, 201, OnGetAccountInfo)
	sess.RegisterUtilHandler(0, 205, OnUpdateLogin)
	sess.RegisterUtilHandler(0, 239, OnSetOptions)
	sess.RegisterUtilHandler(0, 240, OnGetOptions)
	sess.RegisterUtilHandler(0, 253, OnGetAchieves)
	sess.RegisterUtilHandler(0, 267, OnCheckAccountLicenses)
	sess.RegisterUtilHandler(1, 276, OnCheckGameLicenses)
	sess.RegisterUtilHandler(0, 284, OnValidateAchieve)
	sess.RegisterUtilHandler(0, 305, OnGetAdventureProgress)
}

func OnCheckAccountLicenses(s *Session, body []byte) ([]byte, error) {
	return OnCheckLicenses(true)
}

func OnCheckGameLicenses(s *Session, body []byte) ([]byte, error) {
	return OnCheckLicenses(false)
}

func OnCheckLicenses(accountLevel bool) ([]byte, error) {
	res := hsproto.PegasusUtil_CheckLicensesResponse{}
	res.AccountLevel = proto.Bool(accountLevel)
	res.Success = proto.Bool(true)
	return EncodeUtilResponse(277, &res)
}

func OnUpdateLogin(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_UpdateLogin{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_UpdateLoginComplete{}
	return EncodeUtilResponse(307, &res)
}

func OnGetAccountInfo(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_GetAccountInfo{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	switch req.Request.String() {
	case "CAMPAIGN_INFO":
		res := hsproto.PegasusUtil_ProfileProgress{}
		res.Progress = proto.Int64(6)  // ILLIDAN_COMPLETE
		res.BestForge = proto.Int32(0) // Arena wins
		return EncodeUtilResponse(233, &res)
	case "BOOSTERS":
		res := hsproto.PegasusUtil_BoosterList{}
		return EncodeUtilResponse(224, &res)
	case "FEATURES":
		res := hsproto.PegasusUtil_GuardianVars{}
		res.ShowUserUI = proto.Int32(1)
		return EncodeUtilResponse(264, &res)
	case "MEDAL_INFO":
		res := hsproto.PegasusUtil_MedalInfo{}
		res.SeasonWins = proto.Int32(0)
		res.Stars = proto.Int32(2)
		res.Streak = proto.Int32(0)
		res.StarLevel = proto.Int32(1)
		res.LevelStart = proto.Int32(1)
		res.LevelEnd = proto.Int32(3)
		res.CanLose = proto.Bool(false)
		return EncodeUtilResponse(232, &res)
	case "MEDAL_HISTORY":
		res := hsproto.PegasusUtil_MedalHistory{}
		for i := int32(1); i <= 3; i++ {
			info := &hsproto.PegasusUtil_MedalHistoryInfo{}
			info.When = PegasusDate(time.Date(2015, 8, 1, 7, 0, 0, 0, time.UTC))
			info.Season = proto.Int32(i)
			info.Stars = proto.Int32(0)
			info.StarLevel = proto.Int32(0)
			info.LevelStart = proto.Int32(0)
			info.LevelEnd = proto.Int32(0)
			info.LegendRank = proto.Int32(1)
			res.Medals = append(res.Medals, info)
		}
		return EncodeUtilResponse(234, &res)
	case "NOTICES":
		res := hsproto.PegasusUtil_ProfileNotices{}
		return EncodeUtilResponse(212, &res)
	case "DECK_LIST":
		res := hsproto.PegasusUtil_DeckList{}
		for i := 2; i <= 10; i++ {
			info := &hsproto.PegasusShared_DeckInfo{}
			info.Id = proto.Int64(int64(1000 + i))
			info.Name = proto.String("precon")
			info.CardBack = proto.Int32(0)
			info.Hero = proto.Int32(int32(heroIdToAssetId[i]))
			precon := hsproto.PegasusShared_DeckType_PRECON_DECK
			info.DeckType = &precon
			info.Validity = proto.Uint64(31)
			info.HeroPremium = proto.Int32(0)
			info.CardBackOverride = proto.Bool(false)
			info.HeroOverride = proto.Bool(false)
			res.Decks = append(res.Decks, info)
		}
		return EncodeUtilResponse(202, &res)
	case "COLLECTION":
		res := hsproto.PegasusUtil_Collection{}
		return EncodeUtilResponse(207, &res)
	case "DECK_LIMIT":
		res := hsproto.PegasusUtil_ProfileDeckLimit{}
		res.DeckLimit = proto.Int32(9)
		return EncodeUtilResponse(231, &res)
	case "CARD_VALUES":
		res := hsproto.PegasusUtil_CardValues{}
		res.CardNerfIndex = proto.Int32(0)
		return EncodeUtilResponse(260, &res)
	case "ARCANE_DUST_BALANCE":
		res := hsproto.PegasusUtil_ArcaneDustBalance{}
		res.Balance = proto.Int64(10000)
		return EncodeUtilResponse(262, &res)
	case "GOLD_BALANCE":
		res := hsproto.PegasusUtil_GoldBalance{}
		res.Cap = proto.Int64(999999)
		res.CapWarning = proto.Int64(2000)
		res.CappedBalance = proto.Int64(1234)
		res.BonusBalance = proto.Int64(0)
		return EncodeUtilResponse(278, &res)
	case "HERO_XP":
		res := hsproto.PegasusUtil_HeroXP{}
		for i := 2; i <= 10; i++ {
			info := &hsproto.PegasusUtil_HeroXPInfo{}
			level := 2*i + 5
			maxXp := 60 + level*10
			info.ClassId = proto.Int32(int32(i))
			info.Level = proto.Int32(int32(level))
			info.CurrXp = proto.Int64(int64(maxXp / 2))
			info.MaxXp = proto.Int64(int64(maxXp))
			res.XpInfos = append(res.XpInfos, info)
		}
		return EncodeUtilResponse(283, &res)
	case "NOT_SO_MASSIVE_LOGIN":
		res := hsproto.PegasusUtil_NotSoMassiveLoginReply{}
		return EncodeUtilResponse(300, &res)
	case "REWARD_PROGRESS":
		res := hsproto.PegasusUtil_RewardProgress{}
		nextMonth := time.Date(2015, 8, 1, 7, 0, 0, 0, time.UTC)
		res.SeasonEnd = PegasusDate(nextMonth)
		res.WinsPerGold = proto.Int32(3)
		res.GoldPerReward = proto.Int32(10)
		res.MaxGoldPerDay = proto.Int32(100)
		res.SeasonNumber = proto.Int32(21)
		res.XpSoloLimit = proto.Int32(60)
		res.MaxHeroLevel = proto.Int32(60)
		res.NextQuestCancel = PegasusDate(time.Now().UTC())
		res.EventTimingMod = proto.Float32(0.291667)
		return EncodeUtilResponse(271, &res)
	case "PVP_QUEUE":
		res := hsproto.PegasusUtil_PlayQueue{}
		queue := hsproto.PegasusShared_PlayQueueInfo{}
		gametype := hsproto.PegasusShared_BnetGameType_BGT_NORMAL
		queue.GameType = &gametype
		res.Queue = &queue
		return EncodeUtilResponse(286, &res)

	case "PLAYER_RECORD":
		res := hsproto.PegasusUtil_PlayerRecords{}
		return EncodeUtilResponse(270, &res)
	case "CARD_BACKS":
		res := hsproto.PegasusUtil_CardBacks{}
		res.DefaultCardBack = proto.Int32(13)
		res.CardBacks = []int32{0, 13, 24}
		return EncodeUtilResponse(236, &res)
	case "FAVORITE_HEROES":
		res := hsproto.PegasusUtil_FavoriteHeroesResponse{}
		for i := 2; i <= 10; i++ {
			fav := &hsproto.PegasusShared_FavoriteHero{}
			fav.ClassId = proto.Int32(int32(i))
			carddef := &hsproto.PegasusShared_CardDef{}
			carddef.Asset = proto.Int32(int32(heroIdToAssetId[i]))
			fav.Hero = carddef
			res.FavoriteHeroes = append(res.FavoriteHeroes, fav)
		}
		return EncodeUtilResponse(318, &res)
	case "ACCOUNT_LICENSES":
		res := hsproto.PegasusUtil_AccountLicensesInfoResponse{}
		return EncodeUtilResponse(325, &res)
	case "BOOSTER_TALLY":
		res := hsproto.PegasusUtil_BoosterTallyList{}
		return EncodeUtilResponse(313, &res)
	default:

		return nil, nyi
	}
}

func OnGetAdventureProgress(s *Session, body []byte) ([]byte, error) {
	res := hsproto.PegasusUtil_AdventureProgressResponse{}
	return EncodeUtilResponse(306, &res)
}

func OnSetOptions(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_SetOptions{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	return nil, nil
}

func OnGetOptions(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_GetOptions{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_ClientOptions{}
	res.Options = append(res.Options, &hsproto.PegasusUtil_ClientOption{
		Index:    proto.Int32(1),
		AsUint64: proto.Uint64(0x20FFFF3FFFCCFCFF),
	})
	res.Options = append(res.Options, &hsproto.PegasusUtil_ClientOption{
		Index:    proto.Int32(2),
		AsUint64: proto.Uint64(0xF0BFFFEF3FFF),
	})
	res.Options = append(res.Options, &hsproto.PegasusUtil_ClientOption{
		Index:   proto.Int32(18),
		AsInt64: proto.Int64(0xB765A8C),
	})
	return EncodeUtilResponse(241, &res)
}

func OnGetAchieves(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_GetAchieves{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_Achieves{}
	for i := 1; i <= 9; i++ {
		info := &hsproto.PegasusUtil_Achieve{}
		info.Id = proto.Int32(int32(i))
		info.Progress = proto.Int32(1)
		info.AckProgress = proto.Int32(1)
		info.CompletionCount = proto.Int32(1)
		info.StartedCount = proto.Int32(1)
		info.DateGiven = PegasusDate(time.Now())
		res.List = append(res.List, info)
	}
	return EncodeUtilResponse(252, &res)
}

func OnValidateAchieve(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_ValidateAchieve{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_ValidateAchieveResponse{}
	res.Achieve = proto.Int32(req.GetAchieve())
	return EncodeUtilResponse(285, &res)
}

func PegasusDate(t time.Time) *hsproto.PegasusShared_Date {
	return &hsproto.PegasusShared_Date{
		Year:  proto.Int32(int32(t.Year())),
		Month: proto.Int32(int32(t.Month())),
		Day:   proto.Int32(int32(t.Day())),
		Hours: proto.Int32(int32(t.Hour())),
		Min:   proto.Int32(int32(t.Minute())),
		Sec:   proto.Int32(int32(t.Second())),
	}
}

// A map from TAG_CLASS ids to DBF ids
var heroIdToAssetId = map[int]int{
	2:  274,
	3:  31,
	4:  637,
	5:  671,
	6:  813,
	7:  930,
	8:  1066,
	9:  893,
	10: 7,
}
<commit_msg>Hit the database for account collection<commit_after>package pegasus

import (
	"github.com/HearthSim/hs-proto/go"
	"github.com/golang/protobuf/proto"
	"log"
	"time"
)

type Account struct {
	ID        int
	BnetID    int
	UpdatedAt time.Time
	Flags     int64

	Progress []SeasonProgress
	Licenses []License
}

func (v *Account) Init(sess *Session) {
	sess.RegisterUtilHandler(0, 201, OnGetAccountInfo)
	sess.RegisterUtilHandler(0, 205, OnUpdateLogin)
	sess.RegisterUtilHandler(0, 239, OnSetOptions)
	sess.RegisterUtilHandler(0, 240, OnGetOptions)
	sess.RegisterUtilHandler(0, 253, OnGetAchieves)
	sess.RegisterUtilHandler(0, 267, OnCheckAccountLicenses)
	sess.RegisterUtilHandler(1, 276, OnCheckGameLicenses)
	sess.RegisterUtilHandler(0, 284, OnValidateAchieve)
	sess.RegisterUtilHandler(0, 305, OnGetAdventureProgress)
}

func OnCheckAccountLicenses(s *Session, body []byte) ([]byte, error) {
	return OnCheckLicenses(true)
}

func OnCheckGameLicenses(s *Session, body []byte) ([]byte, error) {
	return OnCheckLicenses(false)
}

func OnCheckLicenses(accountLevel bool) ([]byte, error) {
	res := hsproto.PegasusUtil_CheckLicensesResponse{}
	res.AccountLevel = proto.Bool(accountLevel)
	res.Success = proto.Bool(true)
	return EncodeUtilResponse(277, &res)
}

func OnUpdateLogin(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_UpdateLogin{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_UpdateLoginComplete{}
	return EncodeUtilResponse(307, &res)
}

func OnGetAccountInfo(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_GetAccountInfo{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	switch req.Request.String() {
	case "CAMPAIGN_INFO":
		res := hsproto.PegasusUtil_ProfileProgress{}
		res.Progress = proto.Int64(6)  // ILLIDAN_COMPLETE
		res.BestForge = proto.Int32(0) // Arena wins
		return EncodeUtilResponse(233, &res)
	case "BOOSTERS":
		res := hsproto.PegasusUtil_BoosterList{}
		return EncodeUtilResponse(224, &res)
	case "FEATURES":
		res := hsproto.PegasusUtil_GuardianVars{}
		res.ShowUserUI = proto.Int32(1)
		return EncodeUtilResponse(264, &res)
	case "MEDAL_INFO":
		res := hsproto.PegasusUtil_MedalInfo{}
		res.SeasonWins = proto.Int32(0)
		res.Stars = proto.Int32(2)
		res.Streak = proto.Int32(0)
		res.StarLevel = proto.Int32(1)
		res.LevelStart = proto.Int32(1)
		res.LevelEnd = proto.Int32(3)
		res.CanLose = proto.Bool(false)
		return EncodeUtilResponse(232, &res)
	case "MEDAL_HISTORY":
		res := hsproto.PegasusUtil_MedalHistory{}
		for i := int32(1); i <= 3; i++ {
			info := &hsproto.PegasusUtil_MedalHistoryInfo{}
			info.When = PegasusDate(time.Date(2015, 8, 1, 7, 0, 0, 0, time.UTC))
			info.Season = proto.Int32(i)
			info.Stars = proto.Int32(0)
			info.StarLevel = proto.Int32(0)
			info.LevelStart = proto.Int32(0)
			info.LevelEnd = proto.Int32(0)
			info.LegendRank = proto.Int32(1)
			res.Medals = append(res.Medals, info)
		}
		return EncodeUtilResponse(234, &res)
	case "NOTICES":
		res := hsproto.PegasusUtil_ProfileNotices{}
		return EncodeUtilResponse(212, &res)
	case "DECK_LIST":
		res := hsproto.PegasusUtil_DeckList{}
		for i := 2; i <= 10; i++ {
			info := &hsproto.PegasusShared_DeckInfo{}
			info.Id = proto.Int64(int64(1000 + i))
			info.Name = proto.String("precon")
			info.CardBack = proto.Int32(0)
			info.Hero = proto.Int32(int32(heroIdToAssetId[i]))
			precon := hsproto.PegasusShared_DeckType_PRECON_DECK
			info.DeckType = &precon
			info.Validity = proto.Uint64(31)
			info.HeroPremium = proto.Int32(0)
			info.CardBackOverride = proto.Bool(false)
			info.HeroOverride = proto.Bool(false)
			res.Decks = append(res.Decks, info)
		}
		return EncodeUtilResponse(202, &res)
	case "COLLECTION":
		res := hsproto.PegasusUtil_Collection{}
		dbfCards := []DbfCard{}
		db.Where("is_collectible").Find(&dbfCards)
		for _, card := range dbfCards {
			stack1 := &hsproto.PegasusShared_CardStack{}
			stack1.LatestInsertDate = PegasusDate(time.Now().UTC())
			stack1.NumSeen = proto.Int32(2)
			stack1.Count = proto.Int32(2)
			carddef := &hsproto.PegasusShared_CardDef{}
			carddef.Asset = proto.Int32(int32(card.ID))
			carddef.Premium = proto.Int32(0)
			stack1.CardDef = carddef
			res.Stacks = append(res.Stacks, stack1)
		}
		return EncodeUtilResponse(207, &res)
	case "DECK_LIMIT":
		res := hsproto.PegasusUtil_ProfileDeckLimit{}
		res.DeckLimit = proto.Int32(9)
		return EncodeUtilResponse(231, &res)
	case "CARD_VALUES":
		res := hsproto.PegasusUtil_CardValues{}
		res.CardNerfIndex = proto.Int32(0)
		return EncodeUtilResponse(260, &res)
	case "ARCANE_DUST_BALANCE":
		res := hsproto.PegasusUtil_ArcaneDustBalance{}
		res.Balance = proto.Int64(10000)
		return EncodeUtilResponse(262, &res)
	case "GOLD_BALANCE":
		res := hsproto.PegasusUtil_GoldBalance{}
		res.Cap = proto.Int64(999999)
		res.CapWarning = proto.Int64(2000)
		res.CappedBalance = proto.Int64(1234)
		res.BonusBalance = proto.Int64(0)
		return EncodeUtilResponse(278, &res)
	case "HERO_XP":
		res := hsproto.PegasusUtil_HeroXP{}
		for i := 2; i <= 10; i++ {
			info := &hsproto.PegasusUtil_HeroXPInfo{}
			level := 2*i + 5
			maxXp := 60 + level*10
			info.ClassId = proto.Int32(int32(i))
			info.Level = proto.Int32(int32(level))
			info.CurrXp = proto.Int64(int64(maxXp / 2))
			info.MaxXp = proto.Int64(int64(maxXp))
			res.XpInfos = append(res.XpInfos, info)
		}
		return EncodeUtilResponse(283, &res)
	case "NOT_SO_MASSIVE_LOGIN":
		res := hsproto.PegasusUtil_NotSoMassiveLoginReply{}
		return EncodeUtilResponse(300, &res)
	case "REWARD_PROGRESS":
		res := hsproto.PegasusUtil_RewardProgress{}
		nextMonth := time.Date(2015, 8, 1, 7, 0, 0, 0, time.UTC)
		res.SeasonEnd = PegasusDate(nextMonth)
		res.WinsPerGold = proto.Int32(3)
		res.GoldPerReward = proto.Int32(10)
		res.MaxGoldPerDay = proto.Int32(100)
		res.SeasonNumber = proto.Int32(21)
		res.XpSoloLimit = proto.Int32(60)
		res.MaxHeroLevel = proto.Int32(60)
		res.NextQuestCancel = PegasusDate(time.Now().UTC())
		res.EventTimingMod = proto.Float32(0.291667)
		return EncodeUtilResponse(271, &res)
	case "PVP_QUEUE":
		res := hsproto.PegasusUtil_PlayQueue{}
		queue := hsproto.PegasusShared_PlayQueueInfo{}
		gametype := hsproto.PegasusShared_BnetGameType_BGT_NORMAL
		queue.GameType = &gametype
		res.Queue = &queue
		return EncodeUtilResponse(286, &res)

	case "PLAYER_RECORD":
		res := hsproto.PegasusUtil_PlayerRecords{}
		return EncodeUtilResponse(270, &res)
	case "CARD_BACKS":
		res := hsproto.PegasusUtil_CardBacks{}
		res.DefaultCardBack = proto.Int32(13)
		res.CardBacks = []int32{0, 13, 24}
		return EncodeUtilResponse(236, &res)
	case "FAVORITE_HEROES":
		res := hsproto.PegasusUtil_FavoriteHeroesResponse{}
		for i := 2; i <= 10; i++ {
			fav := &hsproto.PegasusShared_FavoriteHero{}
			fav.ClassId = proto.Int32(int32(i))
			carddef := &hsproto.PegasusShared_CardDef{}
			carddef.Asset = proto.Int32(int32(heroIdToAssetId[i]))
			fav.Hero = carddef
			res.FavoriteHeroes = append(res.FavoriteHeroes, fav)
		}
		return EncodeUtilResponse(318, &res)
	case "ACCOUNT_LICENSES":
		res := hsproto.PegasusUtil_AccountLicensesInfoResponse{}
		return EncodeUtilResponse(325, &res)
	case "BOOSTER_TALLY":
		res := hsproto.PegasusUtil_BoosterTallyList{}
		return EncodeUtilResponse(313, &res)
	default:

		return nil, nyi
	}
}

func OnGetAdventureProgress(s *Session, body []byte) ([]byte, error) {
	res := hsproto.PegasusUtil_AdventureProgressResponse{}
	return EncodeUtilResponse(306, &res)
}

func OnSetOptions(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_SetOptions{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	return nil, nil
}

func OnGetOptions(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_GetOptions{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_ClientOptions{}
	res.Options = append(res.Options, &hsproto.PegasusUtil_ClientOption{
		Index:    proto.Int32(1),
		AsUint64: proto.Uint64(0x20FFFF3FFFCCFCFF),
	})
	res.Options = append(res.Options, &hsproto.PegasusUtil_ClientOption{
		Index:    proto.Int32(2),
		AsUint64: proto.Uint64(0xF0BFFFEF3FFF),
	})
	res.Options = append(res.Options, &hsproto.PegasusUtil_ClientOption{
		Index:   proto.Int32(18),
		AsInt64: proto.Int64(0xB765A8C),
	})
	return EncodeUtilResponse(241, &res)
}

func OnGetAchieves(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_GetAchieves{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_Achieves{}
	for i := 1; i <= 9; i++ {
		info := &hsproto.PegasusUtil_Achieve{}
		info.Id = proto.Int32(int32(i))
		info.Progress = proto.Int32(1)
		info.AckProgress = proto.Int32(1)
		info.CompletionCount = proto.Int32(1)
		info.StartedCount = proto.Int32(1)
		info.DateGiven = PegasusDate(time.Now())
		res.List = append(res.List, info)
	}
	return EncodeUtilResponse(252, &res)
}

func OnValidateAchieve(s *Session, body []byte) ([]byte, error) {
	req := hsproto.PegasusUtil_ValidateAchieve{}
	err := proto.Unmarshal(body, &req)
	if err != nil {
		return nil, err
	}
	log.Printf("req = %s", req.String())
	res := hsproto.PegasusUtil_ValidateAchieveResponse{}
	res.Achieve = proto.Int32(req.GetAchieve())
	return EncodeUtilResponse(285, &res)
}

func PegasusDate(t time.Time) *hsproto.PegasusShared_Date {
	return &hsproto.PegasusShared_Date{
		Year:  proto.Int32(int32(t.Year())),
		Month: proto.Int32(int32(t.Month())),
		Day:   proto.Int32(int32(t.Day())),
		Hours: proto.Int32(int32(t.Hour())),
		Min:   proto.Int32(int32(t.Minute())),
		Sec:   proto.Int32(int32(t.Second())),
	}
}

// A map from TAG_CLASS ids to DBF ids
var heroIdToAssetId = map[int]int{
	2:  274,
	3:  31,
	4:  637,
	5:  671,
	6:  813,
	7:  930,
	8:  1066,
	9:  893,
	10: 7,
}
<|endoftext|>


<commit_before>package models

// Idea from http://thenewstack.io/make-a-restful-json-api-go/
import (
	"log"
	"net/http"
	"time"
)

func Logger(inner http.Handler, name string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		inner.ServeHTTP(w, r)

		log.Printf(
			"%s\t%s\t%s\t%s",
			r.Method,
			r.RequestURI,
			name,
			time.Since(start),
		)
	})
}
<commit_msg>add comment for logger reference<commit_after>package models

// Idea for the logger from http://thenewstack.io/make-a-restful-json-api-go/
import (
	"log"
	"net/http"
	"time"
)

func Logger(inner http.Handler, name string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		inner.ServeHTTP(w, r)

		log.Printf(
			"%s\t%s\t%s\t%s",
			r.Method,
			r.RequestURI,
			name,
			time.Since(start),
		)
	})
}
<|endoftext|>


<commit_before>package models

// EnvironmentItemOptionsModel ...
type EnvironmentItemOptionsModel struct {
	// These fields are processed by envman at envman run
	IsExpand    *bool `json:"is_expand,omitempty" yaml:"is_expand,omitempty"`
	SkipIfEmpty *bool `json:"skip_if_empty,omitempty" yaml:"skip_if_empty,omitempty"`
	// These fields used only by bitrise
	Title             *string  `json:"title,omitempty" yaml:"title,omitempty"`
	Description       *string  `json:"description,omitempty" yaml:"description,omitempty"`
	Summary           *string  `json:"summary,omitempty" yaml:"summary,omitempty"`
	Category          *string  `json:"category,omitempty" yaml:"category,omitempty"`
	ValueOptions      []string `json:"value_options,omitempty" yaml:"value_options,omitempty"`
	IsRequired        *bool    `json:"is_required,omitempty" yaml:"is_required,omitempty"`
	IsDontChangeValue *bool    `json:"is_dont_change_value,omitempty" yaml:"is_dont_change_value,omitempty"`
	IsTemplate        *bool    `json:"is_template,omitempty" yaml:"is_template,omitempty"`
}

// EnvironmentItemModel ...
type EnvironmentItemModel map[string]interface{}

// EnvsSerializeModel ...
type EnvsSerializeModel struct {
	Envs []EnvironmentItemModel `json:"envs",yaml:"envs"`
}

// EnvsJSONListModel ...
type EnvsJSONListModel map[string]string
<commit_msg>fixed EnvsSerializeModel (#123)<commit_after>package models

// EnvironmentItemOptionsModel ...
type EnvironmentItemOptionsModel struct {
	// These fields are processed by envman at envman run
	IsExpand    *bool `json:"is_expand,omitempty" yaml:"is_expand,omitempty"`
	SkipIfEmpty *bool `json:"skip_if_empty,omitempty" yaml:"skip_if_empty,omitempty"`
	// These fields used only by bitrise
	Title             *string  `json:"title,omitempty" yaml:"title,omitempty"`
	Description       *string  `json:"description,omitempty" yaml:"description,omitempty"`
	Summary           *string  `json:"summary,omitempty" yaml:"summary,omitempty"`
	Category          *string  `json:"category,omitempty" yaml:"category,omitempty"`
	ValueOptions      []string `json:"value_options,omitempty" yaml:"value_options,omitempty"`
	IsRequired        *bool    `json:"is_required,omitempty" yaml:"is_required,omitempty"`
	IsDontChangeValue *bool    `json:"is_dont_change_value,omitempty" yaml:"is_dont_change_value,omitempty"`
	IsTemplate        *bool    `json:"is_template,omitempty" yaml:"is_template,omitempty"`
}

// EnvironmentItemModel ...
type EnvironmentItemModel map[string]interface{}

// EnvsSerializeModel ...
type EnvsSerializeModel struct {
	Envs []EnvironmentItemModel `json:"envs" yaml:"envs"`
}

// EnvsJSONListModel ...
type EnvsJSONListModel map[string]string
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"fmt"
	"github.com/ghodss/yaml"
	"io/ioutil"
	"k8s.io/contrib/test-utils/utils"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// To add new e2e test support, you need to:
//   1) Transform e2e performance test result into *PerfData* in k8s/kubernetes/test/e2e/perftype,
//   and print the PerfData in e2e test log.
//   2) Add corresponding bucket, job and test into *TestConfig*.

// TestDescription contains test name, output file prefix and parser function.
type TestDescription struct {
	Name             string
	OutputFilePrefix string
	Parser           func(data []byte, buildNumber int, testResult *BuildData)
}

// TestDescriptions is a map job->component->description.
type TestDescriptions map[string]map[string]TestDescription

// Tests is a map from test label to test description.
type Tests struct {
	Prefix       string
	Descriptions TestDescriptions
	BuildsCount  int
}

// Jobs is a map from job name to all supported tests in the job.
type Jobs map[string]Tests

// Buckets is a map from bucket url to all supported jobs in the bucket.
type Buckets map[string]Jobs

var (
	// performanceDescriptions contains metrics exported by a --ginko.focus=[Feature:Performance]
	// e2e test
	performanceDescriptions = TestDescriptions{
		"E2E": {
			"DensityResources": {
				Name:             "density",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			},
			"DensityPodStartup": {
				Name:             "density",
				OutputFilePrefix: "PodStartupLatency",
				Parser:           parseResponsivenessData,
			},
			"DensityTestPhaseTimer": {
				Name:             "density",
				OutputFilePrefix: "TestPhaseTimer",
				Parser:           parseResponsivenessData,
			},
			"LoadResources": {
				Name:             "load",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			},
			"LoadTestPhaseTimer": {
				Name:             "load",
				OutputFilePrefix: "TestPhaseTimer",
				Parser:           parseResponsivenessData,
			},
		},
		"APIServer": {
			"DensityResponsiveness": {
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseResponsivenessData,
			},
			"DensityRequestCount": {
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			},
			"DensityRequestCountByClient": {
				Name:             "density",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			},
			"LoadResponsiveness": {
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseResponsivenessData,
			},
			"LoadRequestCount": {
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			},
			"LoadRequestCountByClient": {
				Name:             "load",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			},
		},
		"Scheduler": {
			"SchedulingLatency": {
				Name:             "density",
				OutputFilePrefix: "SchedulingMetrics",
				Parser:           parseSchedulingLatency,
			},
			"SchedulingThroughput": {
				Name:             "density",
				OutputFilePrefix: "SchedulingMetrics",
				Parser:           pareseSchedulingThroughput,
			},
		},
		"Etcd": {
			"BackendCommitDuration": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("backendCommitDuration"),
			},
			"SnapshotSaveTotalDuration": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("snapshotSaveTotalDuration"),
			},
			"PeerRoundTripTime": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("peerRoundTripTime"),
			},
			"WalFsyncDuration": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("walFsyncDuration"),
			},
		},
	}

	// benchmarkDescriptions contains metrics exported by test/integration/scheduler_perf
	benchmarkDescriptions = TestDescriptions{
		"Scheduler": {
			"BenchmarkResults": {
				Name:             "benchmark",
				OutputFilePrefix: "BenchmarkResults",
				Parser:           parseResponsivenessData,
			},
		},
	}

	jobTypeToDescriptions = map[string]TestDescriptions{
		"performance": performanceDescriptions,
		"benchmark":   benchmarkDescriptions,
	}

	// TestConfig contains all the test PerfDash supports now. Downloader will download and
	// analyze build log from all these Jobs, and parse the data from all these Test.
	// Notice that all the tests should have different name for now.
	TestConfig = Buckets{utils.KubekinsBucket: getProwConfigOrDie()}
)

func getProwConfigOrDie() Jobs {
	jobs, err := getProwConfig()
	if err != nil {
		panic(err)
	}
	return jobs
}

// Minimal subset of the prow config definition at k8s.io/test-infra/prow/config
type config struct {
	Periodics []periodic `json:"periodics"`
}
type periodic struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func getProwConfig() (Jobs, error) {
	fmt.Fprintf(os.Stderr, "Fetching prow config from GitHub...\n")
	resp, err := http.Get("https://raw.githubusercontent.com/kubernetes/test-infra/master/config/jobs/kubernetes/sig-scalability/sig-scalability-periodic-jobs.yaml")
	if err != nil {
		return nil, fmt.Errorf("error fetching prow config from GitHub: %v", err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading prow config from GitHub: %v", err)
	}
	conf := &config{}
	if err := yaml.Unmarshal(b, conf); err != nil {
		return nil, fmt.Errorf("error unmarshaling prow config from GitHub: %v", err)
	}
	jobs := Jobs{}
	for _, periodic := range conf.Periodics {
		var thisPeriodicConfig Tests
		for _, tag := range periodic.Tags {
			if strings.HasPrefix(tag, "perfDashPrefix:") {
				split := strings.SplitN(tag, ":", 2)
				thisPeriodicConfig.Prefix = strings.TrimSpace(split[1])
				continue
			}
			if strings.HasPrefix(tag, "perfDashJobType:") {
				split := strings.SplitN(tag, ":", 2)
				jobType := strings.TrimSpace(split[1])
				thisPeriodicConfig.Descriptions = jobTypeToDescriptions[jobType]
				continue
			}
			if strings.HasPrefix(tag, "perfDashBuildsCount:") {
				split := strings.SplitN(tag, ":", 2)
				i, err := strconv.Atoi(strings.TrimSpace(split[1]))
				if err != nil {
					fmt.Fprintf(os.Stderr, "warning: unparsable builds count - %v\n", split[1])
				}
				if i < 1 {
					fmt.Fprintf(os.Stderr, "warning: non-positive builds count - %v\n", i)
					continue
				}
				thisPeriodicConfig.BuildsCount = i
				continue
			}
			if strings.HasPrefix(tag, "perfDash") {
				fmt.Fprintf(os.Stderr, "warning: unknown perfdash tag name: %q\n", tag)
			}
		}
		if thisPeriodicConfig.Prefix == "" && thisPeriodicConfig.Descriptions == nil {
			continue
		}
		if thisPeriodicConfig.Prefix == "" || thisPeriodicConfig.Descriptions == nil {
			return nil, fmt.Errorf("invalid perfdash config of periodic %q: none or both of prefix and job type must be specified", periodic.Name)
		}
		jobs[periodic.Name] = thisPeriodicConfig
	}
	fmt.Printf("Read config with %d jobs\n", len(jobs))
	return jobs, nil
}
<commit_msg>handling unknown job types<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"fmt"
	"github.com/ghodss/yaml"
	"io/ioutil"
	"k8s.io/contrib/test-utils/utils"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// To add new e2e test support, you need to:
//   1) Transform e2e performance test result into *PerfData* in k8s/kubernetes/test/e2e/perftype,
//   and print the PerfData in e2e test log.
//   2) Add corresponding bucket, job and test into *TestConfig*.

// TestDescription contains test name, output file prefix and parser function.
type TestDescription struct {
	Name             string
	OutputFilePrefix string
	Parser           func(data []byte, buildNumber int, testResult *BuildData)
}

// TestDescriptions is a map job->component->description.
type TestDescriptions map[string]map[string]TestDescription

// Tests is a map from test label to test description.
type Tests struct {
	Prefix       string
	Descriptions TestDescriptions
	BuildsCount  int
}

// Jobs is a map from job name to all supported tests in the job.
type Jobs map[string]Tests

// Buckets is a map from bucket url to all supported jobs in the bucket.
type Buckets map[string]Jobs

var (
	// performanceDescriptions contains metrics exported by a --ginko.focus=[Feature:Performance]
	// e2e test
	performanceDescriptions = TestDescriptions{
		"E2E": {
			"DensityResources": {
				Name:             "density",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			},
			"DensityPodStartup": {
				Name:             "density",
				OutputFilePrefix: "PodStartupLatency",
				Parser:           parseResponsivenessData,
			},
			"DensityTestPhaseTimer": {
				Name:             "density",
				OutputFilePrefix: "TestPhaseTimer",
				Parser:           parseResponsivenessData,
			},
			"LoadResources": {
				Name:             "load",
				OutputFilePrefix: "ResourceUsageSummary",
				Parser:           parseResourceUsageData,
			},
			"LoadTestPhaseTimer": {
				Name:             "load",
				OutputFilePrefix: "TestPhaseTimer",
				Parser:           parseResponsivenessData,
			},
		},
		"APIServer": {
			"DensityResponsiveness": {
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseResponsivenessData,
			},
			"DensityRequestCount": {
				Name:             "density",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			},
			"DensityRequestCountByClient": {
				Name:             "density",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			},
			"LoadResponsiveness": {
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseResponsivenessData,
			},
			"LoadRequestCount": {
				Name:             "load",
				OutputFilePrefix: "APIResponsiveness",
				Parser:           parseRequestCountData,
			},
			"LoadRequestCountByClient": {
				Name:             "load",
				OutputFilePrefix: "MetricsForE2E",
				Parser:           parseApiserverRequestCount,
			},
		},
		"Scheduler": {
			"SchedulingLatency": {
				Name:             "density",
				OutputFilePrefix: "SchedulingMetrics",
				Parser:           parseSchedulingLatency,
			},
			"SchedulingThroughput": {
				Name:             "density",
				OutputFilePrefix: "SchedulingMetrics",
				Parser:           pareseSchedulingThroughput,
			},
		},
		"Etcd": {
			"BackendCommitDuration": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("backendCommitDuration"),
			},
			"SnapshotSaveTotalDuration": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("snapshotSaveTotalDuration"),
			},
			"PeerRoundTripTime": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("peerRoundTripTime"),
			},
			"WalFsyncDuration": {
				Name:             "density",
				OutputFilePrefix: "EtcdMetrics",
				Parser:           parseHistogramMetric("walFsyncDuration"),
			},
		},
	}

	// benchmarkDescriptions contains metrics exported by test/integration/scheduler_perf
	benchmarkDescriptions = TestDescriptions{
		"Scheduler": {
			"BenchmarkResults": {
				Name:             "benchmark",
				OutputFilePrefix: "BenchmarkResults",
				Parser:           parseResponsivenessData,
			},
		},
	}

	jobTypeToDescriptions = map[string]TestDescriptions{
		"performance": performanceDescriptions,
		"benchmark":   benchmarkDescriptions,
	}

	// TestConfig contains all the test PerfDash supports now. Downloader will download and
	// analyze build log from all these Jobs, and parse the data from all these Test.
	// Notice that all the tests should have different name for now.
	TestConfig = Buckets{utils.KubekinsBucket: getProwConfigOrDie()}
)

func getProwConfigOrDie() Jobs {
	jobs, err := getProwConfig()
	if err != nil {
		panic(err)
	}
	return jobs
}

// Minimal subset of the prow config definition at k8s.io/test-infra/prow/config
type config struct {
	Periodics []periodic `json:"periodics"`
}
type periodic struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func getProwConfig() (Jobs, error) {
	fmt.Fprintf(os.Stderr, "Fetching prow config from GitHub...\n")
	resp, err := http.Get("https://raw.githubusercontent.com/kubernetes/test-infra/master/config/jobs/kubernetes/sig-scalability/sig-scalability-periodic-jobs.yaml")
	if err != nil {
		return nil, fmt.Errorf("error fetching prow config from GitHub: %v", err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading prow config from GitHub: %v", err)
	}
	conf := &config{}
	if err := yaml.Unmarshal(b, conf); err != nil {
		return nil, fmt.Errorf("error unmarshaling prow config from GitHub: %v", err)
	}
	jobs := Jobs{}
	for _, periodic := range conf.Periodics {
		var thisPeriodicConfig Tests
		for _, tag := range periodic.Tags {
			if strings.HasPrefix(tag, "perfDashPrefix:") {
				split := strings.SplitN(tag, ":", 2)
				thisPeriodicConfig.Prefix = strings.TrimSpace(split[1])
				continue
			}
			if strings.HasPrefix(tag, "perfDashJobType:") {
				split := strings.SplitN(tag, ":", 2)
				jobType := strings.TrimSpace(split[1])
				var exists bool
				if thisPeriodicConfig.Descriptions, exists = jobTypeToDescriptions[jobType]; !exists {
					fmt.Fprintf(os.Stderr, "warning: unknown job type - %s\n", jobType)
				}
				continue
			}
			if strings.HasPrefix(tag, "perfDashBuildsCount:") {
				split := strings.SplitN(tag, ":", 2)
				i, err := strconv.Atoi(strings.TrimSpace(split[1]))
				if err != nil {
					fmt.Fprintf(os.Stderr, "warning: unparsable builds count - %v\n", split[1])
				}
				if i < 1 {
					fmt.Fprintf(os.Stderr, "warning: non-positive builds count - %v\n", i)
					continue
				}
				thisPeriodicConfig.BuildsCount = i
				continue
			}
			if strings.HasPrefix(tag, "perfDash") {
				fmt.Fprintf(os.Stderr, "warning: unknown perfdash tag name: %q\n", tag)
			}
		}
		if thisPeriodicConfig.Prefix == "" && thisPeriodicConfig.Descriptions == nil {
			continue
		}
		if thisPeriodicConfig.Prefix == "" || thisPeriodicConfig.Descriptions == nil {
			return nil, fmt.Errorf("invalid perfdash config of periodic %q: none or both of prefix and job type must be specified", periodic.Name)
		}
		jobs[periodic.Name] = thisPeriodicConfig
	}
	fmt.Printf("Read config with %d jobs\n", len(jobs))
	return jobs, nil
}
<|endoftext|>


<commit_before>package model_fields

import (
	"os"
	"testing"

	"github.com/satori/go.uuid"
)

var modifier *Modifier

func TestMain(m *testing.M) {
	modifier = NewModifier(
		uuid.NewV4(),
		"John",
		"Doe",
		"john@doe.com",
		"user",
		false,
	)

	code := m.Run()

	os.Exit(code)
}

func TestModifierIsCreated(t *testing.T) {
	if len(modifier.Uuid.Bytes()) != 16 {
		t.Errorf("Uuid is incorrect, actual is %d; expected %d", len(modifier.Uuid.Bytes()), 16)
	}

	if modifier.Firstname != "John" {
		t.Errorf("Firstname is incorrect, actual is %s; expected %s", modifier.Firstname, "John")
	}

	if modifier.Lastname != "Doe" {
		t.Errorf("Lastname is incorrect, actual is %s; expected %s", modifier.Lastname, "Doe")
	}

	if modifier.Email != "john@doe.com" {
		t.Errorf("Email is incorrect, actual is %s; expected %s", modifier.Email, "john@doe.com")
	}

	if modifier.PolymorficType != "user" {
		t.Errorf("PolymorficType is incorrect, actual is %s; expected %s", modifier.PolymorficType, "user")
	}

	if modifier.Deleted {
		t.Errorf("Deleted is incorrect, actual is %t; expected %t", modifier.Deleted, false)
	}
}

func TestModifierIsMarkedAsDeleted(t *testing.T) {
	modifier.MarkAsDeleted()

	if modifier.Deleted == false {
		t.Errorf("Deleted is incorrect, actual is %t; expected %t", modifier.Deleted, true)
	}
}

func TestModifierIsUpdated(t *testing.T) {
	modifier.Update("John1", "Doe1", "john1@doe.com")

	if modifier.Firstname != "John1" {
		t.Errorf("Firstname is incorrect, actual is %s; expected %s", modifier.Firstname, "John1")
	}

	if modifier.Lastname != "Doe1" {
		t.Errorf("Lastname is incorrect, actual is %s; expected %s", modifier.Lastname, "Doe1")
	}

	if modifier.Email != "john1@doe.com" {
		t.Errorf("Email is incorrect, actual is %s; expected %s", modifier.Email, "john1@doe.com")
	}
}
<commit_msg>Improve test organization<commit_after>package model_fields

import (
	"testing"

	"github.com/satori/go.uuid"
)

func createModifier() *Modifier {
	return NewModifier(
		uuid.NewV4(),
		"John",
		"Doe",
		"john@doe.com",
		"user",
		false,
	)
}

func TestModifierIsCreated(t *testing.T) {
	modifier := createModifier()

	if len(modifier.Uuid.Bytes()) != 16 {
		t.Errorf("Uuid is incorrect, actual is %d; expected %d", len(modifier.Uuid.Bytes()), 16)
	}

	if modifier.Firstname != "John" {
		t.Errorf("Firstname is incorrect, actual is %s; expected %s", modifier.Firstname, "John")
	}

	if modifier.Lastname != "Doe" {
		t.Errorf("Lastname is incorrect, actual is %s; expected %s", modifier.Lastname, "Doe")
	}

	if modifier.Email != "john@doe.com" {
		t.Errorf("Email is incorrect, actual is %s; expected %s", modifier.Email, "john@doe.com")
	}

	if modifier.PolymorficType != "user" {
		t.Errorf("PolymorficType is incorrect, actual is %s; expected %s", modifier.PolymorficType, "user")
	}

	if modifier.Deleted {
		t.Errorf("Deleted is incorrect, actual is %t; expected %t", modifier.Deleted, false)
	}
}

func TestModifierIsMarkedAsDeleted(t *testing.T) {
	modifier := createModifier()

	modifier.MarkAsDeleted()

	if modifier.Deleted == false {
		t.Errorf("Deleted is incorrect, actual is %t; expected %t", modifier.Deleted, true)
	}
}

func TestModifierIsUpdated(t *testing.T) {
	modifier := createModifier()

	modifier.Update("John1", "Doe1", "john1@doe.com")

	if modifier.Firstname != "John1" {
		t.Errorf("Firstname is incorrect, actual is %s; expected %s", modifier.Firstname, "John1")
	}

	if modifier.Lastname != "Doe1" {
		t.Errorf("Lastname is incorrect, actual is %s; expected %s", modifier.Lastname, "Doe1")
	}

	if modifier.Email != "john1@doe.com" {
		t.Errorf("Email is incorrect, actual is %s; expected %s", modifier.Email, "john1@doe.com")
	}
}
<|endoftext|>