<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>Fix get nested association<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>


<commit_before>package gadget

import (
	"io/ioutil"
	. "launchpad.net/gocheck"
	"net/http"
	"net/http/httptest"
	"time"
)

type ResponseSuite struct{}

type responseApp struct {
	*App
}

var ra *responseApp

var _ = Suite(&ResponseSuite{})

func (s *ResponseSuite) SetUpSuite(c *C) {
	ra = &responseApp{&App{}}
	ra.Register(&ResponseController{})
	ra.Register(&ImplicitController{})
	ra.Accept("application/json").Via(JsonBroker)
	ra.Accept("text/html").Via(HtmlBroker)
	ra.Routes(ra.Resource("responses"), ra.Resource("implicits"))
}
func (s *ResponseSuite) TearDownSuite(c *C) {
	ra.Controllers = make(map[string]Controller)
}

var cookie = &http.Cookie{
	Name:    "foo",
	Value:   "bar",
	Expires: time.Now().Add(time.Duration(10 * time.Hour)),
}

type ResponseController struct {
	*DefaultController
}

func (c *ResponseController) Index(*Request) (int, interface{}) {
	body := struct{ Foo, Bar string }{"baz", "quux"}
	response := NewResponse(body)
	response.Headers.Set("X-Framework", "Gadget")
	return 200, response
}

func (c *ResponseController) Show(*Request) (int, interface{}) {
	response := NewResponse("test")
	response.AddCookie(cookie)
	return 200, response
}

func (c *ResponseController) CookieAndRedirect(*Request) (int, interface{}) {
	response := NewResponse("/responses")
	response.AddCookie(cookie)
	return 302, response
}

type ImplicitController struct {
	*DefaultController
}

func (c *ImplicitController) Index(*Request) (int, interface{}) {
	body := struct{ Foo, Bar string }{"baz", "quux"}
	return 200, body
}

func HtmlBroker(r *Request, status int, body interface{}, data *RouteData) (int, string) {
	return 200, ""
}

//Headers set on a Response in a controller method are correctly transferred to the http.Response
func (s *ResponseSuite) TestHeadersSetOnGadgetresponseControllerMethodAreCorrectlyTransferredToHttpresponse(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses", nil)
	c.Assert(err, IsNil)

	resp := httptest.NewRecorder()
	handler(resp, req)
	c.Assert(resp.Header().Get("X-Framework"), Equals, "Gadget")
}

//Cookies added to a Response in a controller method are correctly transferred to the http.Response
func (s *ResponseSuite) TestCookiesAddedToGadgetresponseControllerMethodAreCorrectlyTransferredToHttpresponse(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses/1", nil)
	c.Assert(err, IsNil)

	resp := httptest.NewRecorder()
	handler(resp, req)
	c.Assert(resp.Code, Equals, 200)
	c.Assert(resp.Header().Get("Set-Cookie"), Equals, cookie.String())
}

//The body of the http.Response is identical between a controller method that returns an anonymous struct and one that returns a Response with its Body set to that struct
func (s *ResponseSuite) TestBodyHttpresponseIsIdenticalBetweenControllerMethodThatReturnsAnonymousStructAndOneThatReturnsGadgetresponseItsBodySetToThatStruct(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses", nil)
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)
	body1, err := ioutil.ReadAll(resp.Body)
	c.Assert(err, IsNil)

	req, err = http.NewRequest("GET", "http://127.0.0.1:8000/implicits", nil)
	c.Assert(err, IsNil)
	resp = httptest.NewRecorder()
	handler(resp, req)
	body2, err := ioutil.ReadAll(resp.Body)
	c.Assert(err, IsNil)

	c.Assert(string(body1), Equals, string(body2))
}

// The Content-Type of the outgoing response should be the same as the incoming request provided our app supports that mime type.
func (s *ResponseSuite) TestContentTypeOfCustomHttpResponseMatchesRequestContentType(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses", nil)
	req.Header.Add("Content-Type", "text/html")
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)

	c.Assert(resp.Header().Get("Content-Type"), Equals, req.Header.Get("Content-Type"))
}

// We should be able to set a cookie and redirect using the technique outlined in the CookieAndRedirect action.
func (s *ResponseSuite) TestSetCookieAndRedirect(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses/cookie-and-redirect", nil)
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)

	c.Assert(resp.Code, Equals, 302)
	c.Assert(resp.Header().Get("Set-Cookie"), Equals, cookie.String())
}
<commit_msg>adds a failing and passing test<commit_after>package gadget

import (
	"io/ioutil"
	. "launchpad.net/gocheck"
	"net/http"
	"net/http/httptest"
	"time"
)

type ResponseSuite struct{}

type responseApp struct {
	*App
}

var ra *responseApp

var _ = Suite(&ResponseSuite{})

func (s *ResponseSuite) SetUpSuite(c *C) {
	ra = &responseApp{&App{}}
	ra.Register(&ResponseController{})
	ra.Register(&ImplicitController{})
	ra.Accept("application/json").Via(JsonBroker)
	ra.Accept("text/html").Via(HtmlBroker)
	ra.Routes(ra.Resource("responses"), ra.Resource("implicits"))
}
func (s *ResponseSuite) TearDownSuite(c *C) {
	ra.Controllers = make(map[string]Controller)
}

var cookie = &http.Cookie{
	Name:    "foo",
	Value:   "bar",
	Expires: time.Now().Add(time.Duration(10 * time.Hour)),
}

type ResponseController struct {
	*DefaultController
}

func (c *ResponseController) Index(*Request) (int, interface{}) {
	body := struct{ Foo, Bar string }{"baz", "quux"}
	response := NewResponse(body)
	response.Headers.Set("X-Framework", "Gadget")
	return 200, response
}

func (c *ResponseController) Show(*Request) (int, interface{}) {
	response := NewResponse("test")
	response.AddCookie(cookie)
	return 200, response
}

func (c *ResponseController) CookieAndRedirect(*Request) (int, interface{}) {
	response := NewResponse("/responses")
	response.AddCookie(cookie)
	return 302, response
}

func (c *ResponseController) RedirectWithString(*Request) (int, interface{}) {
	return 301, "/somewhere"
}

type ImplicitController struct {
	*DefaultController
}

func (c *ImplicitController) Index(*Request) (int, interface{}) {
	body := struct{ Foo, Bar string }{"baz", "quux"}
	return 200, body
}

func HtmlBroker(r *Request, status int, body interface{}, data *RouteData) (int, string) {
	return 200, ""
}

//Headers set on a Response in a controller method are correctly transferred to the http.Response
func (s *ResponseSuite) TestHeadersSetOnGadgetresponseControllerMethodAreCorrectlyTransferredToHttpresponse(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses", nil)
	c.Assert(err, IsNil)

	resp := httptest.NewRecorder()
	handler(resp, req)
	c.Assert(resp.Header().Get("X-Framework"), Equals, "Gadget")
}

//Cookies added to a Response in a controller method are correctly transferred to the http.Response
func (s *ResponseSuite) TestCookiesAddedToGadgetresponseControllerMethodAreCorrectlyTransferredToHttpresponse(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses/1", nil)
	c.Assert(err, IsNil)

	resp := httptest.NewRecorder()
	handler(resp, req)
	c.Assert(resp.Code, Equals, 200)
	c.Assert(resp.Header().Get("Set-Cookie"), Equals, cookie.String())
}

//The body of the http.Response is identical between a controller method that returns an anonymous struct and one that returns a Response with its Body set to that struct
func (s *ResponseSuite) TestBodyHttpresponseIsIdenticalBetweenControllerMethodThatReturnsAnonymousStructAndOneThatReturnsGadgetresponseItsBodySetToThatStruct(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses", nil)
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)
	body1, err := ioutil.ReadAll(resp.Body)
	c.Assert(err, IsNil)

	req, err = http.NewRequest("GET", "http://127.0.0.1:8000/implicits", nil)
	c.Assert(err, IsNil)
	resp = httptest.NewRecorder()
	handler(resp, req)
	body2, err := ioutil.ReadAll(resp.Body)
	c.Assert(err, IsNil)

	c.Assert(string(body1), Equals, string(body2))
}

// The Content-Type of the outgoing response should be the same as the incoming request provided our app supports that mime type.
func (s *ResponseSuite) TestContentTypeOfCustomHttpResponseMatchesRequestContentType(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses", nil)
	req.Header.Add("Content-Type", "text/html")
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)

	c.Assert(resp.Header().Get("Content-Type"), Equals, req.Header.Get("Content-Type"))
}

// We should be able to set a cookie and redirect using the technique outlined in the CookieAndRedirect action.
func (s *ResponseSuite) TestSetCookieAndRedirect(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses/cookie-and-redirect", nil)
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)

	c.Assert(resp.Code, Equals, 302)
	c.Assert(resp.Header().Get("Set-Cookie"), Equals, cookie.String())
}

func (s *ResponseSuite) TestRedirectWithString(c *C) {
	handler := ra.Handler()

	req, err := http.NewRequest("GET", "http://127.0.0.1:8000/responses/redirect-with-string", nil)
	c.Assert(err, IsNil)
	resp := httptest.NewRecorder()
	handler(resp, req)

	c.Assert(resp.Code, Equals, 301)
	c.Assert(resp.Header().Get("Location"), Equals, "/somewhere")
}
<|endoftext|>


<commit_before>package configfile

import (
	"fmt"
	"testing"
	"time"

	"github.com/rfjakob/gocryptfs/internal/tlog"
)

func TestLoadV1(t *testing.T) {
	_, _, err := LoadConfFile("config_test/v1.conf", "test")
	if err == nil {
		t.Errorf("Outdated v1 config file must fail to load but it didn't")
	} else if testing.Verbose() {
		fmt.Print(err)
	}
}

// Load a known-good config file and verify that it takes at least 100ms
// (brute-force protection)
func TestLoadV2(t *testing.T) {
	t1 := time.Now()

	_, _, err := LoadConfFile("config_test/v2.conf", "test")
	if err != nil {
		t.Errorf("Could not load v2 config file: %v", err)
	}

	elapsed := time.Since(t1)
	if elapsed < 100*time.Millisecond {
		t.Errorf("scrypt calculation runs too fast: %d ms", elapsed/time.Millisecond)
	}
}

func TestLoadV2PwdError(t *testing.T) {
	if !testing.Verbose() {
		tlog.Warn.Enabled = false
	}
	_, _, err := LoadConfFile("config_test/v2.conf", "wrongpassword")
	if err == nil {
		t.Errorf("Loading with wrong password must fail but it didn't")
	}
}

func TestLoadV2Feature(t *testing.T) {
	_, _, err := LoadConfFile("config_test/PlaintextNames.conf", "test")
	if err != nil {
		t.Errorf("Could not load v2 PlaintextNames config file: %v", err)
	}
}

func TestLoadV2StrangeFeature(t *testing.T) {
	_, _, err := LoadConfFile("config_test/StrangeFeature.conf", "test")
	if err == nil {
		t.Errorf("Loading unknown feature must fail but it didn't")
	} else if testing.Verbose() {
		fmt.Print(err)
	}
}

func TestCreateConfFile(t *testing.T) {
	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, false)
	if err != nil {
		t.Fatal(err)
	}
	_, _, err = LoadConfFile("config_test/tmp.conf", "test")
	if err != nil {
		t.Fatal(err)
	}
}

func TestCreateConfFileAESSIV(t *testing.T) {
	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true, false)
	if err != nil {
		t.Fatal(err)
	}
	_, c, err := LoadConfFile("config_test/tmp.conf", "test")
	if err != nil {
		t.Fatal(err)
	}
	if !c.IsFeatureFlagSet(FlagAESSIV) {
		t.Error("AESSIV flag should be set but is not")
	}
}

func TestCreateConfFileRaw64(t *testing.T) {
	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, true)
	if err != nil {
		t.Fatal(err)
	}
	_, c, err := LoadConfFile("config_test/tmp.conf", "test")
	if err != nil {
		t.Fatal(err)
	}
	if !c.IsFeatureFlagSet(FlagRaw64) {
		t.Error("FlagRaw64 flag should be set but is not")
	}
}

func TestIsFeatureFlagKnown(t *testing.T) {
	// Test a few hardcoded values
	testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"}
	// And also everything in knownFlags (yes, it is likely that we end up with
	// some duplicates. Does not matter.)
	for _, f := range knownFlags {
		testKnownFlags = append(testKnownFlags, f)
	}

	var cf ConfFile
	for _, f := range testKnownFlags {
		if !cf.isFeatureFlagKnown(f) {
			t.Errorf("flag %q should be known", f)
		}
	}

	f := "StrangeFeatureFlag"
	if cf.isFeatureFlagKnown(f) {
		t.Errorf("flag %q should be NOT known", f)
	}
}
<commit_msg>tests: configfile: add missing newlines in verbose output<commit_after>package configfile

import (
	"fmt"
	"testing"
	"time"

	"github.com/rfjakob/gocryptfs/internal/tlog"
)

func TestLoadV1(t *testing.T) {
	_, _, err := LoadConfFile("config_test/v1.conf", "test")
	if err == nil {
		t.Errorf("Outdated v1 config file must fail to load but it didn't")
	} else if testing.Verbose() {
		fmt.Println(err)
	}
}

// Load a known-good config file and verify that it takes at least 100ms
// (brute-force protection)
func TestLoadV2(t *testing.T) {
	t1 := time.Now()

	_, _, err := LoadConfFile("config_test/v2.conf", "test")
	if err != nil {
		t.Errorf("Could not load v2 config file: %v", err)
	}

	elapsed := time.Since(t1)
	if elapsed < 100*time.Millisecond {
		t.Errorf("scrypt calculation runs too fast: %d ms", elapsed/time.Millisecond)
	}
}

func TestLoadV2PwdError(t *testing.T) {
	if !testing.Verbose() {
		tlog.Warn.Enabled = false
	}
	_, _, err := LoadConfFile("config_test/v2.conf", "wrongpassword")
	if err == nil {
		t.Errorf("Loading with wrong password must fail but it didn't")
	}
}

func TestLoadV2Feature(t *testing.T) {
	_, _, err := LoadConfFile("config_test/PlaintextNames.conf", "test")
	if err != nil {
		t.Errorf("Could not load v2 PlaintextNames config file: %v", err)
	}
}

func TestLoadV2StrangeFeature(t *testing.T) {
	_, _, err := LoadConfFile("config_test/StrangeFeature.conf", "test")
	if err == nil {
		t.Errorf("Loading unknown feature must fail but it didn't")
	} else if testing.Verbose() {
		fmt.Println(err)
	}
}

func TestCreateConfFile(t *testing.T) {
	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, false)
	if err != nil {
		t.Fatal(err)
	}
	_, _, err = LoadConfFile("config_test/tmp.conf", "test")
	if err != nil {
		t.Fatal(err)
	}
}

func TestCreateConfFileAESSIV(t *testing.T) {
	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true, false)
	if err != nil {
		t.Fatal(err)
	}
	_, c, err := LoadConfFile("config_test/tmp.conf", "test")
	if err != nil {
		t.Fatal(err)
	}
	if !c.IsFeatureFlagSet(FlagAESSIV) {
		t.Error("AESSIV flag should be set but is not")
	}
}

func TestCreateConfFileRaw64(t *testing.T) {
	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, true)
	if err != nil {
		t.Fatal(err)
	}
	_, c, err := LoadConfFile("config_test/tmp.conf", "test")
	if err != nil {
		t.Fatal(err)
	}
	if !c.IsFeatureFlagSet(FlagRaw64) {
		t.Error("FlagRaw64 flag should be set but is not")
	}
}

func TestIsFeatureFlagKnown(t *testing.T) {
	// Test a few hardcoded values
	testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"}
	// And also everything in knownFlags (yes, it is likely that we end up with
	// some duplicates. Does not matter.)
	for _, f := range knownFlags {
		testKnownFlags = append(testKnownFlags, f)
	}

	var cf ConfFile
	for _, f := range testKnownFlags {
		if !cf.isFeatureFlagKnown(f) {
			t.Errorf("flag %q should be known", f)
		}
	}

	f := "StrangeFeatureFlag"
	if cf.isFeatureFlagKnown(f) {
		t.Errorf("flag %q should be NOT known", f)
	}
}
<|endoftext|>


<commit_before>package provider

import (
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceUuid() *schema.Resource {
	return &schema.Resource{
		Description: "The resource `random_uuid` generates random uuid string that is intended to be " +
			"used as unique identifiers for other resources.\n" +
			"\n" +
			"This resource uses [hashicorp/go-uuid](https://github.com/hashicorp/go-uuid) to generate a " +
			"UUID-formatted string for use with services needed a unique string identifier.",
		Create: CreateUuid,
		Read:   schema.Noop,
		Delete: schema.RemoveFromState,
		Importer: &schema.ResourceImporter{
			State: ImportUuid,
		},

		Schema: map[string]*schema.Schema{
			"keepers": {
				Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
					"resource. See [the main provider documentation](../index.html) for more information.",
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"result": {
				Description: "The generated uuid presented in string format.",
				Type:        schema.TypeString,
				Computed:    true,
			},

			"id": {
				Description: "The generated uuid presented in string format.",
				Type:        schema.TypeString,
				Computed:    true,
			},
		},
	}
}

func CreateUuid(d *schema.ResourceData, meta interface{}) error {
	result, err := uuid.GenerateUUID()
	if err != nil {
		return errwrap.Wrapf("error generating uuid: {{err}}", err)
	}
	d.Set("result", result)
	d.SetId(result)
	return nil
}

func ImportUuid(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	id := d.Id()
	bytes, err := uuid.ParseUUID(id)
	if err != nil {
		return nil, errwrap.Wrapf("error parsing uuid bytes: {{err}}", err)
	}
	result, err2 := uuid.FormatUUID(bytes)
	if err2 != nil {
		return nil, errwrap.Wrapf("error formatting uuid bytes: {{err2}}", err2)
	}

	d.Set("result", result)
	d.SetId(result)

	return []*schema.ResourceData{d}, nil
}
<commit_msg>Replace usage of Create, Read, Delete and State fields with CreateContext, ReadContext, DeleteContext and StateContext in resource_uuid (#230)<commit_after>package provider

import (
	"context"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceUuid() *schema.Resource {
	return &schema.Resource{
		Description: "The resource `random_uuid` generates random uuid string that is intended to be " +
			"used as unique identifiers for other resources.\n" +
			"\n" +
			"This resource uses [hashicorp/go-uuid](https://github.com/hashicorp/go-uuid) to generate a " +
			"UUID-formatted string for use with services needed a unique string identifier.",
		CreateContext: CreateUuid,
		ReadContext:   schema.NoopContext,
		DeleteContext: DeleteUuid,
		Importer: &schema.ResourceImporter{
			StateContext: ImportUuid,
		},

		Schema: map[string]*schema.Schema{
			"keepers": {
				Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
					"resource. See [the main provider documentation](../index.html) for more information.",
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"result": {
				Description: "The generated uuid presented in string format.",
				Type:        schema.TypeString,
				Computed:    true,
			},

			"id": {
				Description: "The generated uuid presented in string format.",
				Type:        schema.TypeString,
				Computed:    true,
			},
		},
	}
}

func CreateUuid(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	var diags diag.Diagnostics
	result, err := uuid.GenerateUUID()
	if err != nil {
		return append(diags, diag.Errorf("error generating uuid: %s", err)...)
	}
	d.Set("result", result)
	d.SetId(result)
	return nil
}

func DeleteUuid(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
	d.SetId("")
	return nil
}

func ImportUuid(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	id := d.Id()
	bytes, err := uuid.ParseUUID(id)
	if err != nil {
		return nil, errwrap.Wrapf("error parsing uuid bytes: {{err}}", err)
	}
	result, err2 := uuid.FormatUUID(bytes)
	if err2 != nil {
		return nil, errwrap.Wrapf("error formatting uuid bytes: {{err2}}", err2)
	}

	d.Set("result", result)
	d.SetId(result)

	return []*schema.ResourceData{d}, nil
}
<|endoftext|>


<commit_before>/*
Copyright (C) 2018 Gravitational, Inc.

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 crd

import (
	"fmt"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// StolonUpgradeList is a list of StolonUpgradeResource objects
// with additional information related to kubernetes Custom Resource Definition
type StolonUpgradeList struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Items             []StolonUpgradeResource `json:"items"`
}

func (cr *StolonUpgradeList) GetObjectKind() schema.ObjectKind {
	return &cr.TypeMeta
}

// StolonUpgradeResource is the definition of kubernetes custom
// resource for upgrade stolon application
type StolonUpgradeResource struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Spec              StolonUpgradeSpec `json:"spec"`
}

func (cr *StolonUpgradeResource) GetObjectKind() schema.ObjectKind {
	return &cr.TypeMeta
}

func (cr *StolonUpgradeResource) String() string {
	return fmt.Sprintf("StolonUpgradeResource(namespace=%v, name=%v, status=%v)",
		cr.Namespace, cr.Name, cr.Spec.Status)
}

// StolonUpgradeSpec is a specification of Custom Resource Definition
// for upgrade stolon application
type StolonUpgradeSpec struct {
	// Status is a status of stolon upgrade
	Status string `json:"status"`
	// Phases is a list of phases to upgrade stolon
	Phases []StolonUpgradePhase `json:"phases"`
	// CreationTimestamp is a starting time of upgrade
	CreationTimestamp time.Time `json:"startTime"`
	// FinishTimestamp is a time when upgrade are finished
	FinishTimestamp time.Time `json:"finishTime"`
}

// StolonUpgradePhase defines phase of upgrade
type StolonUpgradePhase struct {
	// Status is a status of upgrade step(phase)
	Status string `json:"status"`
	// Name is a name of upgrade step
	Name string `json:"name"`
	// Description is a small description of upgrade step
	Description string `json:"description"`
	// CreationTimestamp is a starting time of upgrade step
	CreationTimestamp time.Time `json:"startTime"`
	// FinishTimestamp is a time when upgrade step are finished
	FinishTimestamp time.Time `json:"finishTime"`
}
<commit_msg>Fix Godoc nitpicks<commit_after>/*
Copyright (C) 2018 Gravitational, Inc.

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 crd

import (
	"fmt"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// StolonUpgradeList is a list of StolonUpgradeResource objects
// with additional information related to kubernetes Custom Resource Definition
type StolonUpgradeList struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Items             []StolonUpgradeResource `json:"items"`
}

func (cr *StolonUpgradeList) GetObjectKind() schema.ObjectKind {
	return &cr.TypeMeta
}

// StolonUpgradeResource is the definition of kubernetes custom
// resource for upgrade stolon application
type StolonUpgradeResource struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Spec              StolonUpgradeSpec `json:"spec"`
}

func (cr *StolonUpgradeResource) GetObjectKind() schema.ObjectKind {
	return &cr.TypeMeta
}

func (cr *StolonUpgradeResource) String() string {
	return fmt.Sprintf("StolonUpgradeResource(namespace=%v, name=%v, status=%v)",
		cr.Namespace, cr.Name, cr.Spec.Status)
}

// StolonUpgradeSpec is a specification of Custom Resource Definition
// for upgrade stolon application
type StolonUpgradeSpec struct {
	// Status is a status of stolon upgrade
	Status string `json:"status"`
	// Phases is a list of phases to upgrade stolon
	Phases []StolonUpgradePhase `json:"phases"`
	// CreationTimestamp is a starting time of upgrade
	CreationTimestamp time.Time `json:"startTime"`
	// FinishTimestamp is a time when upgrade finished
	FinishTimestamp time.Time `json:"finishTime"`
}

// StolonUpgradePhase defines phase of upgrade
type StolonUpgradePhase struct {
	// Status is a status of upgrade step(phase)
	Status string `json:"status"`
	// Name is a name of upgrade step
	Name string `json:"name"`
	// Description is a small description of upgrade step
	Description string `json:"description"`
	// CreationTimestamp is a starting time of upgrade step
	CreationTimestamp time.Time `json:"startTime"`
	// FinishTimestamp is a time when upgrade step finished
	FinishTimestamp time.Time `json:"finishTime"`
}
<|endoftext|>


<commit_before>package progressbar

import (
	"testing"
)

func TestBuildProgressDescription(t *testing.T) {
	// Set an arbitrary terminal width
	terminalWidth = 100
	tests := getTestCases()
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			desc := buildProgressDescription(test.prefix, test.path, test.extraCharsLen)

			// Validate result
			if desc != test.expectedDesc {
				t.Errorf("Expected value of: \"%s\", got: \"%s\".", test.expectedDesc, desc)
			}
		})
	}
}

func getTestCases() []testCase {
	prefix := "downloading"
	path := "/a/path/to/a/file"
	separator := " | "

	fullDesc := separator + prefix + separator + path + separator
	emptyPathDesc := separator + prefix + separator + "..." + separator
	shortenedDesc := separator + prefix + separator + "...ggggg/path/to/a/file" + separator

	widthMinusProgress := terminalWidth - progressBarWidth*2
	return []testCase{
		{"commonUseCase", prefix, path, 17, fullDesc},
		{"zeroExtraChars", prefix, path, 0, fullDesc},
		{"minDescLength", prefix, path, widthMinusProgress - len(emptyPathDesc), emptyPathDesc},
		{"longPath", prefix, "/a/longggggggggggggggggggggg/path/to/a/file", 17, shortenedDesc},
		{"longPrefix", "longgggggggggggggggggggggg prefix", path, 17, ""},
		{"manyExtraChars", prefix, path, widthMinusProgress - len(emptyPathDesc) + 1, ""},
	}
}

type testCase struct {
	name          string
	prefix        string
	path          string
	extraCharsLen int
	expectedDesc  string
}
<commit_msg>Fixed Progress bar unit testing (#924)<commit_after>package progressbar

import (
	"testing"
)

func TestBuildProgressDescription(t *testing.T) {
	// Set an arbitrary terminal width
	terminalWidth = 100
	tests := getTestCases()
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			desc := buildProgressDescription(test.prefix, test.path, test.extraCharsLen)

			// Validate result
			if desc != test.expectedDesc {
				t.Errorf("Expected value of: \"%s\", got: \"%s\".", test.expectedDesc, desc)
			}
		})
	}
}

func getTestCases() []testCase {
	prefix := "  downloading"
	path := "/a/path/to/a/file"
	separator := " | "

	fullDesc := " " + prefix + separator + path + separator
	emptyPathDesc := " " + prefix + separator + "..." + separator
	shortenedDesc := " " + prefix + separator + "...ggggg/path/to/a/file" + separator

	widthMinusProgress := terminalWidth - progressBarWidth*2
	return []testCase{
		{"commonUseCase", prefix, path, 17, fullDesc},
		{"zeroExtraChars", prefix, path, 0, fullDesc},
		{"minDescLength", prefix, path, widthMinusProgress - len(emptyPathDesc), emptyPathDesc},
		{"longPath", prefix, "/a/longggggggggggggggggggggg/path/to/a/file", 17, shortenedDesc},
		{"longPrefix", "longggggggggggggggggggggggggg prefix", path, 17, ""},
		{"manyExtraChars", prefix, path, widthMinusProgress - len(emptyPathDesc) + 1, ""},
	}
}

type testCase struct {
	name          string
	prefix        string
	path          string
	extraCharsLen int
	expectedDesc  string
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"

	"github.com/vmihailenco/redis/v2"
)

var client *redis.Client

type measurement struct {
	Id                string  `json:"id"`
	CheckId           string  `json:"check_id"`
	Location          string  `json:"location"`
	Url               string  `json:"url"`
	T                 int     `json:"t"`
	ExitStatus        int     `json:"exit_status"`
	ConnectTime       float64 `json:"connect_time,omitempty"`
	StartTransferTime float64 `json:"starttransfer_time,omitempty"`
	LocalIp           string  `json:"local_ip,omitempty"`
	PrimaryIp         string  `json:"primary_ip,omitempty"`
	TotalTime         float64 `json:"total_time,omitempty"`
	HttpStatus        int     `json:"http_status,omitempty"`
	NameLookupTime    float64 `json:"namelookup_time,omitempty"`
}

func checks(res http.ResponseWriter, req *http.Request) {

}

func post_measurements(res http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	measurements := make([]measurement, 0, 100)

	err := decoder.Decode(&measurements)
	if err != nil {
		panic(err)
	}

	for _, m := range measurements {
		s, _ := json.Marshal(m)
		z := redis.Z{Score: float64(m.T), Member: string(s)}
		client.ZAdd("measurements:"+m.CheckId, z)
	}

	log.Printf("fn=post_measurements count=%d\n", len(measurements))
}

func connect_to_redis() {
	u, err := url.Parse(os.Getenv("REDIS_URL"))
	if err != nil {
		panic(err)
	}

	client = redis.NewTCPClient(&redis.Options{
		Addr:     u.Host,
		Password: "", // no password set
		DB:       0,  // use default DB
	})
}

func main() {
	connect_to_redis()

	http.HandleFunc("/checks", checks)
	http.HandleFunc("/measurements", post_measurements)

	fmt.Println("fn=main listening=true")
	err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
	if err != nil {
		panic(err)
	}
}
<commit_msg>only keep an hour of data<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/vmihailenco/redis/v2"
)

var client *redis.Client

type measurement struct {
	Id                string  `json:"id"`
	CheckId           string  `json:"check_id"`
	Location          string  `json:"location"`
	Url               string  `json:"url"`
	T                 int     `json:"t"`
	ExitStatus        int     `json:"exit_status"`
	ConnectTime       float64 `json:"connect_time,omitempty"`
	StartTransferTime float64 `json:"starttransfer_time,omitempty"`
	LocalIp           string  `json:"local_ip,omitempty"`
	PrimaryIp         string  `json:"primary_ip,omitempty"`
	TotalTime         float64 `json:"total_time,omitempty"`
	HttpStatus        int     `json:"http_status,omitempty"`
	NameLookupTime    float64 `json:"namelookup_time,omitempty"`
}

func checks(res http.ResponseWriter, req *http.Request) {

}

func post_measurements(res http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	measurements := make([]measurement, 0, 100)

	err := decoder.Decode(&measurements)
	if err != nil {
		panic(err)
	}

	for _, m := range measurements {
		s, _ := json.Marshal(m)
		z := redis.Z{Score: float64(m.T), Member: string(s)}
		client.ZAdd("measurements:"+m.CheckId, z)
		now := time.Now()
		epoch := now.Unix() - 60*60
		client.ZRemRangeByScore("measurements:"+m.CheckId, "-inf", string(epoch))
	}

	log.Printf("fn=post_measurements count=%d\n", len(measurements))
}

func connect_to_redis() {
	u, err := url.Parse(os.Getenv("REDIS_URL"))
	if err != nil {
		panic(err)
	}

	client = redis.NewTCPClient(&redis.Options{
		Addr:     u.Host,
		Password: "", // no password set
		DB:       0,  // use default DB
	})
}

func main() {
	connect_to_redis()

	http.HandleFunc("/checks", checks)
	http.HandleFunc("/measurements", post_measurements)

	fmt.Println("fn=main listening=true")
	err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/alexjlockwood/gcm"
	"github.com/cubicdaiya/apns"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge  int          `json:"badge,omitempty"`
	Sound  string       `json:"sound,omitempty"`
	Expiry int          `json:"expiry,omitempty"`
	Retry  int          `json:"retry,omitempty"`
	Extend []ExtendJSON `json:"extend,omitempty"`
	// meta
	IDs []uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitGCMClient() {
	TransportGaurun = &http.Transport{MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum}
	GCMClient = &gcm.Sender{ApiKey: ConfGaurun.Android.ApiKey}
	GCMClient.Http = &http.Client{Transport: TransportGaurun}
	GCMClient.Http.Timeout = time.Duration(ConfGaurun.Android.Timeout) * time.Second
}

func StartPushWorkers(workerNum, queueNum int) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := 0; i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err)
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			notification.IDs = make([]uint64, len(notification.Tokens))
			for i := 0; i < len(notification.IDs); i++ {
				notification.IDs[i] = numberingPush()
				LogPush(notification.IDs[i], StatusAcceptedPush, notification.Tokens[i], 0, notification, nil)
			}
			QueueNotification <- notification
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification, client *apns.Client) bool {
	LogError.Debug("START push notification for iOS")

	for i, token := range req.Tokens {
		id := req.IDs[i]
		payload := apns.NewPayload()
		payload.Alert = req.Message
		payload.Badge = req.Badge
		payload.Sound = req.Sound

		pn := apns.NewPushNotification()
		pn.DeviceToken = token
		pn.Expiry = uint32(req.Expiry)
		pn.AddPayload(payload)

		if len(req.Extend) > 0 {
			for _, extend := range req.Extend {
				pn.Set(extend.Key, extend.Value)
			}
		}

		stime := time.Now()
		resp := client.Send(pn)
		etime := time.Now()
		ptime := etime.Sub(stime).Seconds()

		if resp.Error != nil {
			atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, resp.Error)
			client.Conn.Close()
			client.ConnTls.Close()
			return false
		} else {
			LogPush(id, StatusSucceededPush, token, ptime, req, nil)
			atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
		}
	}

	client = nil
	LogError.Debug("END push notification for iOS")
	return true
}

func pushNotificationAndroid(req RequestGaurunNotification) bool {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	msg := gcm.NewMessage(data, req.Tokens...)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		for i, token := range req.Tokens {
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, err)
		}
		return false
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		if len(resp.Results) == len(req.Tokens) {
			for i, token := range req.Tokens {
				if resp.Results[i].Error != "" {
					LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, errors.New(resp.Results[i].Error))
				}
			}
		}
		return true
	}

	for i, token := range req.Tokens {
		LogPush(req.IDs[i], StatusSucceededPush, token, ptime, req, nil)
	}
	atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(len(req.Tokens)))
	LogError.Debug("END push notification for Android")
	return true
}

func pushNotificationWorker() {
	var (
		success    bool
		retryMax   int
		ep         string
		apnsClient *apns.Client
		loop       int
		err        error
	)
	if ConfGaurun.Ios.Sandbox {
		ep = EpApnsSandbox
	} else {
		ep = EpApnsProd
	}

	apnsClient = nil
	loop = 0
	for {
		stime := time.Now()

		notification := <-QueueNotification

		etime := time.Now()
		itime := etime.Sub(stime).Seconds()

		if notification.Platform == PlatFormIos {
			if apnsClient != nil && int(itime) > ConfGaurun.Ios.KeepAliveIdleTimeout {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
			}

			if apnsClient != nil && ConfGaurun.Ios.KeepAliveMax > 0 && loop > ConfGaurun.Ios.KeepAliveMax {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
				loop = 0
			}

			loop++

			if apnsClient == nil {
				apnsClient, err = apns.NewClient(
					ep,
					ConfGaurun.Ios.PemCertPath,
					ConfGaurun.Ios.PemKeyPath,
					0,
				)
				if err != nil {
					LogError.Errorf("failed to connect to APNS: %s", err.Error())
					apnsClient = nil
					loop = 0
					QueueNotification <- notification
					continue
				}
				apnsClient.TimeoutWaitError = time.Duration(ConfGaurun.Ios.TimeoutError) * time.Millisecond
			}
		}

		switch notification.Platform {
		case PlatFormIos:
			success = pushNotificationIos(notification, apnsClient)
			if !success {
				apnsClient = nil
			}
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			success = pushNotificationAndroid(notification)
			retryMax = ConfGaurun.Android.RetryMax
		}
		if !success && notification.Retry < retryMax {
			if len(QueueNotification) < cap(QueueNotification) {
				notification.Retry++
				QueueNotification <- notification
			}
		}
	}
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var (
		respGaurun ResponseGaurun
	)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", serverHeader())

	w.WriteHeader(code)
	respGaurun.Message = msg
	err := json.NewEncoder(w).Encode(&respGaurun)
	if err != nil {
		// Internal Server Error(500) should be returned by right.
		// But 'code' is returned because of the limitation of json.NewEncoder and WriteHeader.
		msg := "Response-body could not be created"
		fmt.Fprintf(w, msg)
		LogError.Error(msg)
		return
	}
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	var (
		reqGaurun RequestGaurun
		err       error
	)

	if ConfGaurun.Log.Level == "debug" {
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			sendResponse(w, "failed to read request-body", http.StatusInternalServerError)
			return
		}
		LogError.Debugf("parse request body: %s", reqBody)
		err = json.Unmarshal(reqBody, &reqGaurun)
	} else {
		LogError.Debug("parse request body")
		err = json.NewDecoder(r.Body).Decode(&reqGaurun)
	}

	if err != nil {
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<commit_msg>improved error handlings: output error to error_log when 400(Bad Request) is returned.<commit_after>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/alexjlockwood/gcm"
	"github.com/cubicdaiya/apns"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge  int          `json:"badge,omitempty"`
	Sound  string       `json:"sound,omitempty"`
	Expiry int          `json:"expiry,omitempty"`
	Retry  int          `json:"retry,omitempty"`
	Extend []ExtendJSON `json:"extend,omitempty"`
	// meta
	IDs []uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitGCMClient() {
	TransportGaurun = &http.Transport{MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum}
	GCMClient = &gcm.Sender{ApiKey: ConfGaurun.Android.ApiKey}
	GCMClient.Http = &http.Client{Transport: TransportGaurun}
	GCMClient.Http.Timeout = time.Duration(ConfGaurun.Android.Timeout) * time.Second
}

func StartPushWorkers(workerNum, queueNum int) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := 0; i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err)
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			notification.IDs = make([]uint64, len(notification.Tokens))
			for i := 0; i < len(notification.IDs); i++ {
				notification.IDs[i] = numberingPush()
				LogPush(notification.IDs[i], StatusAcceptedPush, notification.Tokens[i], 0, notification, nil)
			}
			QueueNotification <- notification
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification, client *apns.Client) bool {
	LogError.Debug("START push notification for iOS")

	for i, token := range req.Tokens {
		id := req.IDs[i]
		payload := apns.NewPayload()
		payload.Alert = req.Message
		payload.Badge = req.Badge
		payload.Sound = req.Sound

		pn := apns.NewPushNotification()
		pn.DeviceToken = token
		pn.Expiry = uint32(req.Expiry)
		pn.AddPayload(payload)

		if len(req.Extend) > 0 {
			for _, extend := range req.Extend {
				pn.Set(extend.Key, extend.Value)
			}
		}

		stime := time.Now()
		resp := client.Send(pn)
		etime := time.Now()
		ptime := etime.Sub(stime).Seconds()

		if resp.Error != nil {
			atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, resp.Error)
			client.Conn.Close()
			client.ConnTls.Close()
			return false
		} else {
			LogPush(id, StatusSucceededPush, token, ptime, req, nil)
			atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
		}
	}

	client = nil
	LogError.Debug("END push notification for iOS")
	return true
}

func pushNotificationAndroid(req RequestGaurunNotification) bool {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	msg := gcm.NewMessage(data, req.Tokens...)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		for i, token := range req.Tokens {
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, err)
		}
		return false
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		if len(resp.Results) == len(req.Tokens) {
			for i, token := range req.Tokens {
				if resp.Results[i].Error != "" {
					LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, errors.New(resp.Results[i].Error))
				}
			}
		}
		return true
	}

	for i, token := range req.Tokens {
		LogPush(req.IDs[i], StatusSucceededPush, token, ptime, req, nil)
	}
	atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(len(req.Tokens)))
	LogError.Debug("END push notification for Android")
	return true
}

func pushNotificationWorker() {
	var (
		success    bool
		retryMax   int
		ep         string
		apnsClient *apns.Client
		loop       int
		err        error
	)
	if ConfGaurun.Ios.Sandbox {
		ep = EpApnsSandbox
	} else {
		ep = EpApnsProd
	}

	apnsClient = nil
	loop = 0
	for {
		stime := time.Now()

		notification := <-QueueNotification

		etime := time.Now()
		itime := etime.Sub(stime).Seconds()

		if notification.Platform == PlatFormIos {
			if apnsClient != nil && int(itime) > ConfGaurun.Ios.KeepAliveIdleTimeout {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
			}

			if apnsClient != nil && ConfGaurun.Ios.KeepAliveMax > 0 && loop > ConfGaurun.Ios.KeepAliveMax {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
				loop = 0
			}

			loop++

			if apnsClient == nil {
				apnsClient, err = apns.NewClient(
					ep,
					ConfGaurun.Ios.PemCertPath,
					ConfGaurun.Ios.PemKeyPath,
					0,
				)
				if err != nil {
					LogError.Errorf("failed to connect to APNS: %s", err.Error())
					apnsClient = nil
					loop = 0
					QueueNotification <- notification
					continue
				}
				apnsClient.TimeoutWaitError = time.Duration(ConfGaurun.Ios.TimeoutError) * time.Millisecond
			}
		}

		switch notification.Platform {
		case PlatFormIos:
			success = pushNotificationIos(notification, apnsClient)
			if !success {
				apnsClient = nil
			}
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			success = pushNotificationAndroid(notification)
			retryMax = ConfGaurun.Android.RetryMax
		}
		if !success && notification.Retry < retryMax {
			if len(QueueNotification) < cap(QueueNotification) {
				notification.Retry++
				QueueNotification <- notification
			}
		}
	}
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var (
		respGaurun ResponseGaurun
	)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", serverHeader())

	w.WriteHeader(code)
	respGaurun.Message = msg
	err := json.NewEncoder(w).Encode(&respGaurun)
	if err != nil {
		// Internal Server Error(500) should be returned by right.
		// But 'code' is returned because of the limitation of json.NewEncoder and WriteHeader.
		msg := "Response-body could not be created"
		fmt.Fprintf(w, msg)
		LogError.Error(msg)
		return
	}
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	var (
		reqGaurun RequestGaurun
		err       error
	)

	if ConfGaurun.Log.Level == "debug" {
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			sendResponse(w, "failed to read request-body", http.StatusInternalServerError)
			return
		}
		LogError.Debugf("parse request body: %s", reqBody)
		err = json.Unmarshal(reqBody, &reqGaurun)
	} else {
		LogError.Debug("parse request body")
		err = json.NewDecoder(r.Body).Decode(&reqGaurun)
	}

	if err != nil {
		LogError.Error(err)
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		LogError.Error("empty notification")
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		LogError.Error(msg)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	var x int

	fmt.Scanf("%d", &x)

	// Section 1: If Statement
	// What "if" looks like
	if x > 0 {
		fmt.Println("x > 0")
	} else if x < 0 {
		fmt.Println("x < 0")
	} else {
		fmt.Println("x = 0")
	}

	// or
	if fmt.Scanf("%d", &x); x > 0 {
		fmt.Println("x > 0")
	} else if x < 0 {
		fmt.Println("x < 0")
	} else {
		fmt.Println("x = 0")
	}

	// Section 2: For Statement
	// for loop
	for i := 0; i < 3; i++ {
		fmt.Println("Current i = ", i)
	}

	// or without initialization and continuation
	// looks more like a while statement
	i := 0
	for i < 3 {
		fmt.Println("Current i = ", i)
		i++
	}

	// and dead loop
	/*
	   for {}

	*/

	// for with break or continue is same as its actions in C
	// or
outerLoop:
	for i := 0; i < 3; i++ {
	innerLoop:
		for j := 0; j < 5; j++ {
			if j > 4 {
				break innerLoop // by default, or write as "continue outerLoop"
			}
			if j > 2 {
				break outerLoop
			}
		}
	}

	// keyword range
	for pos, c := range "Hello, 中国" {
		fmt.Printf("%c in position %v\n", c, pos)
	}

	// Section 3: Switch Statement
	var c int
	fmt.Scanf("%d", &c)
	switch c {
	case 1:
		fmt.Println(1)
	case 2:
		fmt.Println(2)
	case 3:
		fallthrough
	case 4:
		fmt.Println("3 & 4")
	case 5, 6, 7:
		fmt.Println("5, 6 & 7")
	}

	// goto
	accessed := false
aLabel:
	if accessed {
		fmt.Println("End")
		return
	}
	fmt.Println("Not end")
	accessed = true
	goto aLabel
}
<commit_msg>docs: add info about '++' and '--'<commit_after>package main

import "fmt"

func main() {
	var x int

	fmt.Scanf("%d", &x)

	// Section 1: If Statement
	// What "if" looks like
	if x > 0 {
		fmt.Println("x > 0")
	} else if x < 0 {
		fmt.Println("x < 0")
	} else {
		fmt.Println("x = 0")
	}

	// or
	if fmt.Scanf("%d", &x); x > 0 {
		fmt.Println("x > 0")
	} else if x < 0 {
		fmt.Println("x < 0")
	} else {
		fmt.Println("x = 0")
	}

	// Section 2: For Statement
	// for loop
	for i := 0; i < 3; i++ {
		fmt.Println("Current i = ", i)
	}

	// or without initialization and continuation
	// looks more like a while statement
	i := 0
	for i < 3 {
		fmt.Println("Current i = ", i)
		i++
	}

	// and dead loop
	/*
	   for {}

	*/

	// for with break or continue is same as its actions in C
	// or
outerLoop:
	for i := 0; i < 3; i++ { // i++ and i-- are not expressions but statements
	innerLoop:
		for j := 0; j < 5; j++ {
			if j > 4 {
				break innerLoop // by default, or write as "continue outerLoop"
			}
			if j > 2 {
				break outerLoop
			}
		}
	}

	// keyword range
	for pos, c := range "Hello, 中国" {
		fmt.Printf("%c in position %v\n", c, pos)
	}

	// Section 3: Switch Statement
	var c int
	fmt.Scanf("%d", &c)
	switch c {
	case 1:
		fmt.Println(1)
	case 2:
		fmt.Println(2)
	case 3:
		fallthrough
	case 4:
		fmt.Println("3 & 4")
	case 5, 6, 7:
		fmt.Println("5, 6 & 7")
	}

	// goto
	accessed := false
aLabel:
	if accessed {
		fmt.Println("End")
		return
	}
	fmt.Println("Not end")
	accessed = true
	goto aLabel
}
<|endoftext|>


<commit_before>// Sequence operations

package py

// Converts a sequence object v into a Tuple
func SequenceTuple(v Object) Tuple {
	// FIXME need to support iterable objects etc!
	switch x := v.(type) {
	case Tuple:
		return x
	case *List:
		return Tuple(x.Items).Copy()
	}
	panic("SequenceTuple not fully implemented")
}

// Converts a sequence object v into a List
func SequenceList(v Object) *List {
	// FIXME need to support iterable objects etc!
	switch x := v.(type) {
	case Tuple:
		return NewListFromItems(x)
	case *List:
		return x.Copy()
	}
	panic("SequenceList not fully implemented")
}
<commit_msg>Better error reporting in sequence<commit_after>// Sequence operations

package py

// Converts a sequence object v into a Tuple
func SequenceTuple(v Object) Tuple {
	// FIXME need to support iterable objects etc!
	switch x := v.(type) {
	case Tuple:
		return x
	case *List:
		return Tuple(x.Items).Copy()
	}
	panic(ExceptionNewf(TypeError, "SequenceTuple not fully implemented, can't convert %s", v.Type().Name))
}

// Converts a sequence object v into a List
func SequenceList(v Object) *List {
	// FIXME need to support iterable objects etc!
	switch x := v.(type) {
	case Tuple:
		return NewListFromItems(x)
	case *List:
		return x.Copy()
	}
	panic(ExceptionNewf(TypeError, "SequenceList not fully implemented, can't convert %s", v.Type().Name))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	"github.com/jessevdk/go-flags"
	"github.com/robbiet480/cec"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

type Options struct {
	Host        string `short:"i" long:"ip" description:"ip to listen on" default:"127.0.0.1"`
	Port        string `short:"p" long:"port" description:"tcp port to listen on" default:"8080"`
	Adapter     string `short:"a" long:"adapter" description:"cec adapter to connect to [RPI, usb, ...]"`
	Name        string `short:"n" long:"name" description:"OSD name to announce on the cec bus" default:"REST Gateway"`
	Type        string `short:"t" long:"type" description:"The device type to register as" default:"tuner"`
	AudioDevice string `short:"d" long:"audio-device" description:"The audio device to target [TV, Audio]" default:"TV"`
}

var options Options
var parser = flags.NewParser(&options, flags.Default)

var volume_level int
var input_number int
var is_muted = false

func main() {
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}

	cec.Open(options.Adapter, options.Name, options.Type)

	r := gin.Default()
	r.GET("/info", info)
	r.GET("/input", input_status)
	r.PUT("/input/:number", input_change)
	r.GET("/power/:device", power_status)
	r.PUT("/power/:device", power_on)
	r.DELETE("/power/:device", power_off)
	r.GET("/volume", vol_status)
	r.PUT("/volume/up", vol_up)
	r.PUT("/volume/down", vol_down)
	r.PUT("/volume/mute", vol_mute)
	r.GET("/volume/mute", vol_mute_status)
	r.PUT("/volume/reset", vol_reset)
	r.PUT("/volume/step/:direction/:steps", vol_step)
	r.PUT("/volume/set/:level", vol_set)
	r.PUT("/key/:device/:key", key)
	r.PUT("/channel/:device/:channel", change_channel)
	r.POST("/transmit", transmit)

	// Let's reset the volume level to 0
	time.Sleep(5 * time.Second)
	log.Println("Resetting volume to 0")
	for i := 0; i < 100; i++ {
		addr := cec.GetLogicalAddressByName(options.AudioDevice)
		log.Println("Sending VolumeDown")
		cec.Key(addr, "VolumeDown")
	}
	volume_level = 0

	log.Println("Volume has been set to 0")

	log.Println("Getting the current active input")

	for address, active := range cec.GetActiveDevices() {
		if (active) && (cec.IsActiveSource(address)) {
			input_str := strings.Split(cec.GetDevicePhysicalAddress(address), ".")[0]
			input_atoi, _ := strconv.Atoi(input_str)
			input_number = int(input_atoi)
		}
	}

	r.Run(options.Host + ":" + options.Port)
}

func info(c *gin.Context) {
	c.JSON(200, cec.List())
}

func power_on(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.PowerOn(addr)
	c.String(200, "on")
}

func power_off(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.Standby(addr)
	c.String(200, "off")
}

func power_status(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	status := cec.GetDevicePowerStatus(addr)
	if status == "on" {
		c.String(200, "on")
	} else if status == "standby" {
		c.String(200, "off")
	} else {
		c.String(500, "invalid power state")
	}
}

func input_status(c *gin.Context) {
	c.String(200, "INPUT HDMI "+strconv.Itoa(input_number))
}

func input_change(c *gin.Context) {
	input := c.Params.ByName("number")
	cec.Transmit("3f:82:" + input + "0:00")
	input_atoi, _ := strconv.Atoi(input)
	input_number = int(input_atoi)
	c.String(200, "INPUT HDMI "+input)
}

func change_channel(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	channel := c.Params.ByName("channel")

	for _, number := range channel {
		cec.Key(addr, "0x2"+string(number))
	}

	c.String(200, channel)
}

func vol_step(c *gin.Context) {
	steps_str := c.Params.ByName("steps")
	steps_atoi, _ := strconv.Atoi(steps_str)
	steps := int(steps_atoi)
	direction := c.Params.ByName("direction")

	for i := 0; i < steps; i++ {
		if direction == "up" {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeUp")
			volume_level = volume_level + steps
		} else if direction == "down" {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeDown")
			volume_level = volume_level - steps
		} else {
			c.String(400, "Invalid direction. Valid directions are up or down.")
		}
	}

	c.String(204, "")
}

func vol_set(c *gin.Context) {
	level_str := c.Params.ByName("level")
	level_atoi, _ := strconv.Atoi(level_str)
	wanted_level := int(level_atoi)

	log.Println("Wanted_level is " + strconv.Itoa(wanted_level) + " and volume_level is " + strconv.Itoa(volume_level))

	if wanted_level > 100 {
		c.String(400, "The maximum volume level is 100")
	} else if wanted_level > volume_level { // Requested level is greater then current volume level
		log.Println("FIRST")
		var final_level = wanted_level - volume_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeUp")
		}
	} else if wanted_level < volume_level { // Requested level is less than current volume level
		log.Println("SECOND")
		var final_level = volume_level - wanted_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeDown")
		}
	}

	volume_level = wanted_level

	c.String(200, strconv.Itoa(volume_level))
}

func vol_status(c *gin.Context) {
	if is_muted == true {
		c.String(200, "muted")
	} else {
		c.String(200, strconv.Itoa(volume_level))
	}
}

func vol_up(c *gin.Context) {
	if volume_level == 100 {
		c.String(400, "Volume already at maximum")
	} else {
		addr := cec.GetLogicalAddressByName(options.AudioDevice)
		cec.Key(addr, "VolumeUp")
		volume_level = volume_level + 1
		c.String(204, "")
	}
}

func vol_down(c *gin.Context) {
	if volume_level == 0 {
		c.String(400, "Volume is already at minimum")
	} else {
		addr := cec.GetLogicalAddressByName(options.AudioDevice)
		cec.Key(addr, "VolumeDown")
		volume_level = volume_level - 1
		c.String(204, "")
	}
}

func vol_mute(c *gin.Context) {
	cec.Mute()
	is_muted = true
	c.String(204, "")
}

func vol_mute_status(c *gin.Context) {
	c.String(200, strconv.FormatBool(is_muted))
}

func vol_reset(c *gin.Context) {
	for i := 0; i < 100; i++ {
		log.Println("Sending VolumeDown")
		addr := cec.GetLogicalAddressByName(options.AudioDevice)

		cec.Key(addr, "VolumeDown")
	}
	volume_level = 0
	c.String(200, strconv.Itoa(volume_level))
}

func key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")

	cec.Key(addr, key)
	c.String(204, "")
}

func transmit(c *gin.Context) {
	var commands []string
	c.Bind(&commands)

	for _, val := range commands {
		cec.Transmit(val)
	}
	c.String(204, "")
}
<commit_msg>Change help text<commit_after>package main

import (
	"github.com/gin-gonic/gin"
	"github.com/jessevdk/go-flags"
	"github.com/robbiet480/cec"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

type Options struct {
	Host        string `short:"i" long:"ip" description:"ip to listen on" default:"127.0.0.1"`
	Port        string `short:"p" long:"port" description:"tcp port to listen on" default:"8080"`
	Adapter     string `short:"a" long:"adapter" description:"cec adapter to connect to [RPI, usb, ...]"`
	Name        string `short:"n" long:"name" description:"OSD name to announce on the cec bus" default:"REST Gateway"`
	Type        string `short:"t" long:"type" description:"The device type to register as" default:"tuner"`
	AudioDevice string `short:"d" long:"audio-device" description:"The audio device to use for volume control and status [TV, Audio]" default:"TV"`
}

var options Options
var parser = flags.NewParser(&options, flags.Default)

var volume_level int
var input_number int
var is_muted = false

func main() {
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}

	cec.Open(options.Adapter, options.Name, options.Type)

	r := gin.Default()
	r.GET("/info", info)
	r.GET("/input", input_status)
	r.PUT("/input/:number", input_change)
	r.GET("/power/:device", power_status)
	r.PUT("/power/:device", power_on)
	r.DELETE("/power/:device", power_off)
	r.GET("/volume", vol_status)
	r.PUT("/volume/up", vol_up)
	r.PUT("/volume/down", vol_down)
	r.PUT("/volume/mute", vol_mute)
	r.GET("/volume/mute", vol_mute_status)
	r.PUT("/volume/reset", vol_reset)
	r.PUT("/volume/step/:direction/:steps", vol_step)
	r.PUT("/volume/set/:level", vol_set)
	r.PUT("/key/:device/:key", key)
	r.PUT("/channel/:device/:channel", change_channel)
	r.POST("/transmit", transmit)

	// Let's reset the volume level to 0
	time.Sleep(5 * time.Second)
	log.Println("Resetting volume to 0")
	for i := 0; i < 100; i++ {
		addr := cec.GetLogicalAddressByName(options.AudioDevice)
		log.Println("Sending VolumeDown")
		cec.Key(addr, "VolumeDown")
	}
	volume_level = 0

	log.Println("Volume has been set to 0")

	log.Println("Getting the current active input")

	for address, active := range cec.GetActiveDevices() {
		if (active) && (cec.IsActiveSource(address)) {
			input_str := strings.Split(cec.GetDevicePhysicalAddress(address), ".")[0]
			input_atoi, _ := strconv.Atoi(input_str)
			input_number = int(input_atoi)
		}
	}

	r.Run(options.Host + ":" + options.Port)
}

func info(c *gin.Context) {
	c.JSON(200, cec.List())
}

func power_on(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.PowerOn(addr)
	c.String(200, "on")
}

func power_off(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.Standby(addr)
	c.String(200, "off")
}

func power_status(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	status := cec.GetDevicePowerStatus(addr)
	if status == "on" {
		c.String(200, "on")
	} else if status == "standby" {
		c.String(200, "off")
	} else {
		c.String(500, "invalid power state")
	}
}

func input_status(c *gin.Context) {
	c.String(200, "INPUT HDMI "+strconv.Itoa(input_number))
}

func input_change(c *gin.Context) {
	input := c.Params.ByName("number")
	cec.Transmit("3f:82:" + input + "0:00")
	input_atoi, _ := strconv.Atoi(input)
	input_number = int(input_atoi)
	c.String(200, "INPUT HDMI "+input)
}

func change_channel(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	channel := c.Params.ByName("channel")

	for _, number := range channel {
		cec.Key(addr, "0x2"+string(number))
	}

	c.String(200, channel)
}

func vol_step(c *gin.Context) {
	steps_str := c.Params.ByName("steps")
	steps_atoi, _ := strconv.Atoi(steps_str)
	steps := int(steps_atoi)
	direction := c.Params.ByName("direction")

	for i := 0; i < steps; i++ {
		if direction == "up" {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeUp")
			volume_level = volume_level + steps
		} else if direction == "down" {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeDown")
			volume_level = volume_level - steps
		} else {
			c.String(400, "Invalid direction. Valid directions are up or down.")
		}
	}

	c.String(204, "")
}

func vol_set(c *gin.Context) {
	level_str := c.Params.ByName("level")
	level_atoi, _ := strconv.Atoi(level_str)
	wanted_level := int(level_atoi)

	log.Println("Wanted_level is " + strconv.Itoa(wanted_level) + " and volume_level is " + strconv.Itoa(volume_level))

	if wanted_level > 100 {
		c.String(400, "The maximum volume level is 100")
	} else if wanted_level > volume_level { // Requested level is greater then current volume level
		log.Println("FIRST")
		var final_level = wanted_level - volume_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeUp")
		}
	} else if wanted_level < volume_level { // Requested level is less than current volume level
		log.Println("SECOND")
		var final_level = volume_level - wanted_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.AudioDevice)
			cec.Key(addr, "VolumeDown")
		}
	}

	volume_level = wanted_level

	c.String(200, strconv.Itoa(volume_level))
}

func vol_status(c *gin.Context) {
	if is_muted == true {
		c.String(200, "muted")
	} else {
		c.String(200, strconv.Itoa(volume_level))
	}
}

func vol_up(c *gin.Context) {
	if volume_level == 100 {
		c.String(400, "Volume already at maximum")
	} else {
		addr := cec.GetLogicalAddressByName(options.AudioDevice)
		cec.Key(addr, "VolumeUp")
		volume_level = volume_level + 1
		c.String(204, "")
	}
}

func vol_down(c *gin.Context) {
	if volume_level == 0 {
		c.String(400, "Volume is already at minimum")
	} else {
		addr := cec.GetLogicalAddressByName(options.AudioDevice)
		cec.Key(addr, "VolumeDown")
		volume_level = volume_level - 1
		c.String(204, "")
	}
}

func vol_mute(c *gin.Context) {
	cec.Mute()
	is_muted = true
	c.String(204, "")
}

func vol_mute_status(c *gin.Context) {
	c.String(200, strconv.FormatBool(is_muted))
}

func vol_reset(c *gin.Context) {
	for i := 0; i < 100; i++ {
		log.Println("Sending VolumeDown")
		addr := cec.GetLogicalAddressByName(options.AudioDevice)

		cec.Key(addr, "VolumeDown")
	}
	volume_level = 0
	c.String(200, strconv.Itoa(volume_level))
}

func key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")

	cec.Key(addr, key)
	c.String(204, "")
}

func transmit(c *gin.Context) {
	var commands []string
	c.Bind(&commands)

	for _, val := range commands {
		cec.Transmit(val)
	}
	c.String(204, "")
}
<|endoftext|>