<commit_before>package coreutil

import (
	"net/http"
	"reflect"
)

// ResponseStatus returns the HTTP response status.
// Remember that the status is only set by the server after ResponseWriter.WriteHeader() has been called.
func ResponseStatus(w http.ResponseWriter) int {
	return int(httpResponseStruct(reflect.ValueOf(w)).FieldByName("status").Int())
}

// httpResponseStruct returns the response structure after going trough all theintermediary ResponseWriterBinder.
func httpResponseStruct(v reflect.Value) reflect.Value {
	switch v.Type().String() {
	case "core.ResponseWriterBinder":
		return httpResponseStruct(v.FieldByName("ResponseWriter").Elem())
	case "*http.response":
		return v.Elem()
	default:
		panic("core: call of httpResponseStruct on unknown interface type")
	}
}

// SetContentType detects and sets the correct content type.
func SetContentType(w http.ResponseWriter, b []byte) {
	if len(w.Header().Get("Content-Type")) == 0 {
		w.Header().Set("Content-Type", http.DetectContentType(b))
	}
}
<commit_msg>Rename SetContentType into SetDetectedContentType<commit_after>package coreutil

import (
	"net/http"
	"reflect"
)

// ResponseStatus returns the HTTP response status.
// Remember that the status is only set by the server after ResponseWriter.WriteHeader() has been called.
func ResponseStatus(w http.ResponseWriter) int {
	return int(httpResponseStruct(reflect.ValueOf(w)).FieldByName("status").Int())
}

// httpResponseStruct returns the response structure after going trough all theintermediary ResponseWriterBinder.
func httpResponseStruct(v reflect.Value) reflect.Value {
	switch v.Type().String() {
	case "core.ResponseWriterBinder":
		return httpResponseStruct(v.FieldByName("ResponseWriter").Elem())
	case "*http.response":
		return v.Elem()
	default:
		panic("core: call of httpResponseStruct on unknown interface type")
	}
}

// SetDetectedContentType detects and sets the correct content type.
func SetDetectedContentType(w http.ResponseWriter, b []byte) {
	if len(w.Header().Get("Content-Type")) == 0 {
		w.Header().Set("Content-Type", http.DetectContentType(b))
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// 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 model

import (
	"strconv"
	"strings"

	"github.com/casbin/casbin/v2/rbac"
	"github.com/casbin/casbin/v2/util"
)

type (
	PolicyOp int
)

const (
	PolicyAdd PolicyOp = iota
	PolicyRemove
)

const DefaultSep = ","

// BuildIncrementalRoleLinks provides incremental build the role inheritance relations.
func (model Model) BuildIncrementalRoleLinks(rmMap map[string]rbac.RoleManager, op PolicyOp, sec string, ptype string, rules [][]string) error {
	if sec == "g" {
		return model[sec][ptype].buildIncrementalRoleLinks(rmMap[ptype], op, rules)
	}
	return nil
}

// BuildRoleLinks initializes the roles in RBAC.
func (model Model) BuildRoleLinks(rmMap map[string]rbac.RoleManager) error {
	model.PrintPolicy()
	for ptype, ast := range model["g"] {
		rm := rmMap[ptype]
		err := ast.buildRoleLinks(rm)
		if err != nil {
			return err
		}
	}

	return nil
}

// PrintPolicy prints the policy to log.
func (model Model) PrintPolicy() {
	if !model.GetLogger().IsEnabled() {
		return
	}

	policy := make(map[string][][]string)

	for key, ast := range model["p"] {
		value, found := policy[key]
		if found {
			value = append(value, ast.Policy...)
			policy[key] = value
		} else {
			policy[key] = ast.Policy
		}
	}

	for key, ast := range model["g"] {
		value, found := policy[key]
		if found {
			value = append(value, ast.Policy...)
			policy[key] = value
		} else {
			policy[key] = ast.Policy
		}
	}

	model.GetLogger().LogPolicy(policy)
}

// ClearPolicy clears all current policy.
func (model Model) ClearPolicy() {
	for _, ast := range model["p"] {
		ast.Policy = nil
		ast.PolicyMap = map[string]int{}
	}

	for _, ast := range model["g"] {
		ast.Policy = nil
		ast.PolicyMap = map[string]int{}
	}
}

// GetPolicy gets all rules in a policy.
func (model Model) GetPolicy(sec string, ptype string) [][]string {
	return model[sec][ptype].Policy
}

// GetFilteredPolicy gets rules based on field filters from a policy.
func (model Model) GetFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) [][]string {
	res := [][]string{}

	for _, rule := range model[sec][ptype].Policy {
		matched := true
		for i, fieldValue := range fieldValues {
			if fieldValue != "" && rule[fieldIndex+i] != fieldValue {
				matched = false
				break
			}
		}

		if matched {
			res = append(res, rule)
		}
	}

	return res
}

// HasPolicy determines whether a model has the specified policy rule.
func (model Model) HasPolicy(sec string, ptype string, rule []string) bool {
	_, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
	return ok
}

// HasPolicies determines whether a model has any of the specified policies. If one is found we return true.
func (model Model) HasPolicies(sec string, ptype string, rules [][]string) bool {
	for i := 0; i < len(rules); i++ {
		if model.HasPolicy(sec, ptype, rules[i]) {
			return true
		}
	}

	return false
}

// AddPolicy adds a policy rule to the model.
func (model Model) AddPolicy(sec string, ptype string, rule []string) {
	assertion := model[sec][ptype]
	assertion.Policy = append(assertion.Policy, rule)

	if sec == "p" && assertion.priorityIndex >= 0 {
		if idxInsert, err := strconv.Atoi(rule[assertion.priorityIndex]); err == nil {
			i := len(assertion.Policy) - 1
			for ; i > 0; i-- {
				idx, err := strconv.Atoi(assertion.Policy[i-1][assertion.priorityIndex])
				if err != nil {
					break
				}
				if idx > idxInsert {
					assertion.Policy[i] = assertion.Policy[i-1]
				} else {
					break
				}
			}
			assertion.Policy[i] = rule
			assertion.PolicyMap[strings.Join(rule, DefaultSep)] = i
		}
	}
	assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1
}

// AddPolicies adds policy rules to the model.
func (model Model) AddPolicies(sec string, ptype string, rules [][]string) {
	_ = model.AddPoliciesWithAffected(sec, ptype, rules)
}

// AddPoliciesWithAffected adds policy rules to the model, and returns effected rules.
func (model Model) AddPoliciesWithAffected(sec string, ptype string, rules [][]string) [][]string {
	var effected [][]string
	for _, rule := range rules {
		hashKey := strings.Join(rule, DefaultSep)
		_, ok := model[sec][ptype].PolicyMap[hashKey]
		if ok {
			continue
		}
		effected = append(effected, rule)
		model.AddPolicy(sec, ptype, rule)
	}
	return effected
}

// RemovePolicy removes a policy rule from the model.
// Deprecated: Using AddPoliciesWithAffected instead.
func (model Model) RemovePolicy(sec string, ptype string, rule []string) bool {
	index, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
	if !ok {
		return false
	}

	model[sec][ptype].Policy = append(model[sec][ptype].Policy[:index], model[sec][ptype].Policy[index+1:]...)
	delete(model[sec][ptype].PolicyMap, strings.Join(rule, DefaultSep))
	for i := index; i < len(model[sec][ptype].Policy); i++ {
		model[sec][ptype].PolicyMap[strings.Join(model[sec][ptype].Policy[i], DefaultSep)] = i
	}

	return true
}

// UpdatePolicy updates a policy rule from the model.
func (model Model) UpdatePolicy(sec string, ptype string, oldRule []string, newRule []string) bool {
	oldPolicy := strings.Join(oldRule, DefaultSep)
	index, ok := model[sec][ptype].PolicyMap[oldPolicy]
	if !ok {
		return false
	}

	model[sec][ptype].Policy[index] = newRule
	delete(model[sec][ptype].PolicyMap, oldPolicy)
	model[sec][ptype].PolicyMap[strings.Join(newRule, DefaultSep)] = index

	return true
}

// UpdatePolicies updates a policy rule from the model.
func (model Model) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) bool {
	rollbackFlag := false
	// index -> []{oldIndex, newIndex}
	modifiedRuleIndex := make(map[int][]int)
	// rollback
	defer func() {
		if rollbackFlag {
			for index, oldNewIndex := range modifiedRuleIndex {
				model[sec][ptype].Policy[index] = oldRules[oldNewIndex[0]]
				oldPolicy := strings.Join(oldRules[oldNewIndex[0]], DefaultSep)
				newPolicy := strings.Join(newRules[oldNewIndex[1]], DefaultSep)
				delete(model[sec][ptype].PolicyMap, newPolicy)
				model[sec][ptype].PolicyMap[oldPolicy] = index
			}
		}
	}()

	newIndex := 0
	for oldIndex, oldRule := range oldRules {
		oldPolicy := strings.Join(oldRule, DefaultSep)
		index, ok := model[sec][ptype].PolicyMap[oldPolicy]
		if !ok {
			rollbackFlag = true
			return false
		}

		model[sec][ptype].Policy[index] = newRules[newIndex]
		delete(model[sec][ptype].PolicyMap, oldPolicy)
		model[sec][ptype].PolicyMap[strings.Join(newRules[newIndex], DefaultSep)] = index
		modifiedRuleIndex[index] = []int{oldIndex, newIndex}
		newIndex++
	}

	return true
}

// RemovePolicies removes policy rules from the model.
func (model Model) RemovePolicies(sec string, ptype string, rules [][]string) bool {
	effected := model.RemovePoliciesWithEffected(sec, ptype, rules)
	return len(effected) != 0
}

// RemovePoliciesWithEffected removes policy rules from the model, and returns effected rules.
func (model Model) RemovePoliciesWithEffected(sec string, ptype string, rules [][]string) [][]string {
	var effected [][]string
	for _, rule := range rules {
		index, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
		if !ok {
			continue
		}

		effected = append(effected, rule)
		model[sec][ptype].Policy = append(model[sec][ptype].Policy[:index], model[sec][ptype].Policy[index+1:]...)
		delete(model[sec][ptype].PolicyMap, strings.Join(rule, DefaultSep))
		for i := index; i < len(model[sec][ptype].Policy); i++ {
			model[sec][ptype].PolicyMap[strings.Join(model[sec][ptype].Policy[i], DefaultSep)] = i
		}
	}
	return effected
}

// RemoveFilteredPolicy removes policy rules based on field filters from the model.
func (model Model) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, [][]string) {
	var tmp [][]string
	var effects [][]string
	res := false
	model[sec][ptype].PolicyMap = map[string]int{}

	for _, rule := range model[sec][ptype].Policy {
		matched := true
		for i, fieldValue := range fieldValues {
			if fieldValue != "" && rule[fieldIndex+i] != fieldValue {
				matched = false
				break
			}
		}

		if matched {
			effects = append(effects, rule)
		} else {
			tmp = append(tmp, rule)
			model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] = len(tmp) - 1
		}
	}

	if len(tmp) != len(model[sec][ptype].Policy) {
		model[sec][ptype].Policy = tmp
		res = true
	}

	return res, effects
}

// GetValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicy(sec string, ptype string, fieldIndex int) []string {
	values := []string{}

	for _, rule := range model[sec][ptype].Policy {
		values = append(values, rule[fieldIndex])
	}

	util.ArrayRemoveDuplicates(&values)

	return values
}

// GetValuesForFieldInPolicyAllTypes gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicyAllTypes(sec string, fieldIndex int) []string {
	values := []string{}

	for ptype := range model[sec] {
		values = append(values, model.GetValuesForFieldInPolicy(sec, ptype, fieldIndex)...)
	}

	util.ArrayRemoveDuplicates(&values)

	return values
}
<commit_msg>fix: model AddPolicy method can't set PolicyMap correctly when has priority<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// 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 model

import (
	"strconv"
	"strings"

	"github.com/casbin/casbin/v2/rbac"
	"github.com/casbin/casbin/v2/util"
)

type (
	PolicyOp int
)

const (
	PolicyAdd PolicyOp = iota
	PolicyRemove
)

const DefaultSep = ","

// BuildIncrementalRoleLinks provides incremental build the role inheritance relations.
func (model Model) BuildIncrementalRoleLinks(rmMap map[string]rbac.RoleManager, op PolicyOp, sec string, ptype string, rules [][]string) error {
	if sec == "g" {
		return model[sec][ptype].buildIncrementalRoleLinks(rmMap[ptype], op, rules)
	}
	return nil
}

// BuildRoleLinks initializes the roles in RBAC.
func (model Model) BuildRoleLinks(rmMap map[string]rbac.RoleManager) error {
	model.PrintPolicy()
	for ptype, ast := range model["g"] {
		rm := rmMap[ptype]
		err := ast.buildRoleLinks(rm)
		if err != nil {
			return err
		}
	}

	return nil
}

// PrintPolicy prints the policy to log.
func (model Model) PrintPolicy() {
	if !model.GetLogger().IsEnabled() {
		return
	}

	policy := make(map[string][][]string)

	for key, ast := range model["p"] {
		value, found := policy[key]
		if found {
			value = append(value, ast.Policy...)
			policy[key] = value
		} else {
			policy[key] = ast.Policy
		}
	}

	for key, ast := range model["g"] {
		value, found := policy[key]
		if found {
			value = append(value, ast.Policy...)
			policy[key] = value
		} else {
			policy[key] = ast.Policy
		}
	}

	model.GetLogger().LogPolicy(policy)
}

// ClearPolicy clears all current policy.
func (model Model) ClearPolicy() {
	for _, ast := range model["p"] {
		ast.Policy = nil
		ast.PolicyMap = map[string]int{}
	}

	for _, ast := range model["g"] {
		ast.Policy = nil
		ast.PolicyMap = map[string]int{}
	}
}

// GetPolicy gets all rules in a policy.
func (model Model) GetPolicy(sec string, ptype string) [][]string {
	return model[sec][ptype].Policy
}

// GetFilteredPolicy gets rules based on field filters from a policy.
func (model Model) GetFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) [][]string {
	res := [][]string{}

	for _, rule := range model[sec][ptype].Policy {
		matched := true
		for i, fieldValue := range fieldValues {
			if fieldValue != "" && rule[fieldIndex+i] != fieldValue {
				matched = false
				break
			}
		}

		if matched {
			res = append(res, rule)
		}
	}

	return res
}

// HasPolicy determines whether a model has the specified policy rule.
func (model Model) HasPolicy(sec string, ptype string, rule []string) bool {
	_, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
	return ok
}

// HasPolicies determines whether a model has any of the specified policies. If one is found we return true.
func (model Model) HasPolicies(sec string, ptype string, rules [][]string) bool {
	for i := 0; i < len(rules); i++ {
		if model.HasPolicy(sec, ptype, rules[i]) {
			return true
		}
	}

	return false
}

// AddPolicy adds a policy rule to the model.
func (model Model) AddPolicy(sec string, ptype string, rule []string) {
	assertion := model[sec][ptype]
	assertion.Policy = append(assertion.Policy, rule)
	assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1

	if sec == "p" && assertion.priorityIndex >= 0 {
		if idxInsert, err := strconv.Atoi(rule[assertion.priorityIndex]); err == nil {
			i := len(assertion.Policy) - 1
			for ; i > 0; i-- {
				idx, err := strconv.Atoi(assertion.Policy[i-1][assertion.priorityIndex])
				if err != nil {
					break
				}
				if idx > idxInsert {
					assertion.Policy[i] = assertion.Policy[i-1]
					assertion.PolicyMap[strings.Join(assertion.Policy[i-1], DefaultSep)]++
				} else {
					break
				}
			}
			assertion.Policy[i] = rule
			assertion.PolicyMap[strings.Join(rule, DefaultSep)] = i
		}
	}
}

// AddPolicies adds policy rules to the model.
func (model Model) AddPolicies(sec string, ptype string, rules [][]string) {
	_ = model.AddPoliciesWithAffected(sec, ptype, rules)
}

// AddPoliciesWithAffected adds policy rules to the model, and returns effected rules.
func (model Model) AddPoliciesWithAffected(sec string, ptype string, rules [][]string) [][]string {
	var effected [][]string
	for _, rule := range rules {
		hashKey := strings.Join(rule, DefaultSep)
		_, ok := model[sec][ptype].PolicyMap[hashKey]
		if ok {
			continue
		}
		effected = append(effected, rule)
		model.AddPolicy(sec, ptype, rule)
	}
	return effected
}

// RemovePolicy removes a policy rule from the model.
// Deprecated: Using AddPoliciesWithAffected instead.
func (model Model) RemovePolicy(sec string, ptype string, rule []string) bool {
	index, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
	if !ok {
		return false
	}

	model[sec][ptype].Policy = append(model[sec][ptype].Policy[:index], model[sec][ptype].Policy[index+1:]...)
	delete(model[sec][ptype].PolicyMap, strings.Join(rule, DefaultSep))
	for i := index; i < len(model[sec][ptype].Policy); i++ {
		model[sec][ptype].PolicyMap[strings.Join(model[sec][ptype].Policy[i], DefaultSep)] = i
	}

	return true
}

// UpdatePolicy updates a policy rule from the model.
func (model Model) UpdatePolicy(sec string, ptype string, oldRule []string, newRule []string) bool {
	oldPolicy := strings.Join(oldRule, DefaultSep)
	index, ok := model[sec][ptype].PolicyMap[oldPolicy]
	if !ok {
		return false
	}

	model[sec][ptype].Policy[index] = newRule
	delete(model[sec][ptype].PolicyMap, oldPolicy)
	model[sec][ptype].PolicyMap[strings.Join(newRule, DefaultSep)] = index

	return true
}

// UpdatePolicies updates a policy rule from the model.
func (model Model) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) bool {
	rollbackFlag := false
	// index -> []{oldIndex, newIndex}
	modifiedRuleIndex := make(map[int][]int)
	// rollback
	defer func() {
		if rollbackFlag {
			for index, oldNewIndex := range modifiedRuleIndex {
				model[sec][ptype].Policy[index] = oldRules[oldNewIndex[0]]
				oldPolicy := strings.Join(oldRules[oldNewIndex[0]], DefaultSep)
				newPolicy := strings.Join(newRules[oldNewIndex[1]], DefaultSep)
				delete(model[sec][ptype].PolicyMap, newPolicy)
				model[sec][ptype].PolicyMap[oldPolicy] = index
			}
		}
	}()

	newIndex := 0
	for oldIndex, oldRule := range oldRules {
		oldPolicy := strings.Join(oldRule, DefaultSep)
		index, ok := model[sec][ptype].PolicyMap[oldPolicy]
		if !ok {
			rollbackFlag = true
			return false
		}

		model[sec][ptype].Policy[index] = newRules[newIndex]
		delete(model[sec][ptype].PolicyMap, oldPolicy)
		model[sec][ptype].PolicyMap[strings.Join(newRules[newIndex], DefaultSep)] = index
		modifiedRuleIndex[index] = []int{oldIndex, newIndex}
		newIndex++
	}

	return true
}

// RemovePolicies removes policy rules from the model.
func (model Model) RemovePolicies(sec string, ptype string, rules [][]string) bool {
	effected := model.RemovePoliciesWithEffected(sec, ptype, rules)
	return len(effected) != 0
}

// RemovePoliciesWithEffected removes policy rules from the model, and returns effected rules.
func (model Model) RemovePoliciesWithEffected(sec string, ptype string, rules [][]string) [][]string {
	var effected [][]string
	for _, rule := range rules {
		index, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
		if !ok {
			continue
		}

		effected = append(effected, rule)
		model[sec][ptype].Policy = append(model[sec][ptype].Policy[:index], model[sec][ptype].Policy[index+1:]...)
		delete(model[sec][ptype].PolicyMap, strings.Join(rule, DefaultSep))
		for i := index; i < len(model[sec][ptype].Policy); i++ {
			model[sec][ptype].PolicyMap[strings.Join(model[sec][ptype].Policy[i], DefaultSep)] = i
		}
	}
	return effected
}

// RemoveFilteredPolicy removes policy rules based on field filters from the model.
func (model Model) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, [][]string) {
	var tmp [][]string
	var effects [][]string
	res := false
	model[sec][ptype].PolicyMap = map[string]int{}

	for _, rule := range model[sec][ptype].Policy {
		matched := true
		for i, fieldValue := range fieldValues {
			if fieldValue != "" && rule[fieldIndex+i] != fieldValue {
				matched = false
				break
			}
		}

		if matched {
			effects = append(effects, rule)
		} else {
			tmp = append(tmp, rule)
			model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] = len(tmp) - 1
		}
	}

	if len(tmp) != len(model[sec][ptype].Policy) {
		model[sec][ptype].Policy = tmp
		res = true
	}

	return res, effects
}

// GetValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicy(sec string, ptype string, fieldIndex int) []string {
	values := []string{}

	for _, rule := range model[sec][ptype].Policy {
		values = append(values, rule[fieldIndex])
	}

	util.ArrayRemoveDuplicates(&values)

	return values
}

// GetValuesForFieldInPolicyAllTypes gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicyAllTypes(sec string, fieldIndex int) []string {
	values := []string{}

	for ptype := range model[sec] {
		values = append(values, model.GetValuesForFieldInPolicy(sec, ptype, fieldIndex)...)
	}

	util.ArrayRemoveDuplicates(&values)

	return values
}
<|endoftext|>


<commit_before>package aero

import (
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"

	"crypto/sha256"

	"encoding/base64"
	"encoding/json"

	"io/ioutil"

	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
)

const (
	gzipCacheDuration = 5 * time.Minute
	gzipCacheCleanup  = 1 * time.Minute
)

// Middleware ...
type Middleware func(*Context, func())

// Application represents a single web service.
type Application struct {
	root string

	Config   *Configuration
	Layout   func(*Context, string) string
	Sessions SessionManager
	Security ApplicationSecurity

	router *httprouter.Router
	routes struct {
		GET  []string
		POST []string
	}
	routeTests map[string][]string
	gzipCache  *cache.Cache
	start      time.Time
	rewrite    func(*RewriteContext)

	middleware []Middleware

	css            string
	cssHash        string
	cssReplacement string

	contentSecurityPolicy string
}

// New creates a new application.
func New() *Application {
	app := new(Application)
	app.start = time.Now()
	app.router = httprouter.New()
	app.routeTests = make(map[string][]string)
	app.gzipCache = cache.New(gzipCacheDuration, gzipCacheCleanup)
	app.Layout = func(ctx *Context, content string) string {
		return content
	}
	app.Config = new(Configuration)
	app.Config.Reset()
	app.Load()

	app.Sessions.Store = NewMemoryStore()

	return app
}

// Get registers your function to be called when a certain GET path has been requested.
func (app *Application) Get(path string, handle Handle) {
	app.routes.GET = append(app.routes.GET, path)
	app.router.GET(path, app.createRouteHandler(path, handle))
}

// Post registers your function to be called when a certain POST path has been requested.
func (app *Application) Post(path string, handle Handle) {
	app.routes.POST = append(app.routes.POST, path)
	app.router.POST(path, app.createRouteHandler(path, handle))
}

// createRouteHandler creates a handler function for httprouter.
func (app *Application) createRouteHandler(path string, handle Handle) httprouter.Handle {
	return func(response http.ResponseWriter, request *http.Request, params httprouter.Params) {
		// Create context.
		ctx := Context{
			App:        app,
			StatusCode: http.StatusOK,
			request:    request,
			response:   response,
			params:     params,
			start:      time.Now(),
		}

		// The last part of the call chain will send the actual response.
		lastPartOfCallChain := func() {
			data := handle(&ctx)
			ctx.Respond(data)
		}

		// Declare the type of generateNext so that we can define it recursively in the next part.
		var generateNext func(index int) func()

		// Create a function that returns a bound function next()
		// which can be used as the 2nd parameter in the call chain.
		generateNext = func(index int) func() {
			if index == len(app.middleware) {
				return lastPartOfCallChain
			}

			return func() {
				app.middleware[index](&ctx, generateNext(index+1))
			}
		}

		generateNext(0)()
	}
}

// Ajax calls app.Get for both /route and /_/route
func (app *Application) Ajax(path string, handle Handle) {
	app.Get("/_"+path, handle)
	app.Get(path, func(ctx *Context) string {
		page := handle(ctx)
		html := app.Layout(ctx, page)
		html = strings.Replace(html, "<body", app.cssReplacement, 1)
		html = strings.Replace(html, "<html", "<!DOCTYPE html><html", 1)
		return html
	})
}

// Run starts your application.
func (app *Application) Run() {
	app.TestManifest()
	app.TestRoutes()
	app.Listen()
}

// Use adds middleware to your middleware chain.
func (app *Application) Use(middlewares ...Middleware) {
	app.middleware = append(app.middleware, middlewares...)
}

// Load loads the application data from the file system.
func (app *Application) Load() {
	config, readError := ioutil.ReadFile("config.json")

	if readError == nil {
		jsonDecodeError := json.Unmarshal(config, app.Config)

		if jsonDecodeError != nil {
			color.Red(jsonDecodeError.Error())
		}
	}

	if app.Config.Manifest.Name == "" {
		app.Config.Manifest.Name = app.Config.Title
	}

	if app.Config.Manifest.ShortName == "" {
		app.Config.Manifest.ShortName = app.Config.Title
	}

	if app.Config.Manifest.Lang == "" {
		app.Config.Manifest.Lang = "en"
	}

	if app.Config.Manifest.Display == "" {
		app.Config.Manifest.Display = "standalone"
	}

	if app.Config.Manifest.StartURL == "" {
		app.Config.Manifest.StartURL = "/"
	}
}

// Listen starts the server.
func (app *Application) Listen() {
	if app.Security.Key != "" && app.Security.Certificate != "" {
		go func() {
			app.serveHTTPS(":" + strconv.Itoa(app.Config.Ports.HTTPS))
		}()

		fmt.Println("Server running on:", color.GreenString("https://localhost:"+strconv.Itoa(app.Config.Ports.HTTPS)))
	} else {
		fmt.Println("Server running on:", color.GreenString("http://localhost:"+strconv.Itoa(app.Config.Ports.HTTP)))
	}

	app.serveHTTP(":" + strconv.Itoa(app.Config.Ports.HTTP))
}

// // listen listens on the specified host and port.
// func (app *Application) listen(port int) net.Listener {
// 	address := ":" + strconv.Itoa(port)

// 	listener, bindError := net.Listen("tcp", address)

// 	if bindError != nil {
// 		panic(bindError)
// 	}

// 	return listener
// }

// Rewrite sets the URL rewrite function.
func (app *Application) Rewrite(rewrite func(*RewriteContext)) {
	app.rewrite = rewrite
}

// SetStyle ...
func (app *Application) SetStyle(css string) {
	app.css = css

	hash := sha256.Sum256([]byte(css))
	app.cssHash = base64.StdEncoding.EncodeToString(hash[:])
	app.cssReplacement = "<body"
	app.contentSecurityPolicy = "default-src 'none'; img-src https:; script-src 'self'; style-src 'sha256-" + app.cssHash + "'; font-src https:; manifest-src 'self'; child-src https:; connect-src https: wss:"
}

// StartTime ...
func (app *Application) StartTime() time.Time {
	return app.start
}

// Handler returns the request handler.
func (app *Application) Handler() http.Handler {
	router := app.router
	rewrite := app.rewrite

	if rewrite != nil {
		return &rewriteHandler{
			rewrite: rewrite,
			router:  router,
		}
	}

	return router
}

// serveHTTP serves requests from the given listener.
func (app *Application) serveHTTP(address string) {
	serveError := http.ListenAndServe(address, app.Handler())

	if serveError != nil {
		panic(serveError)
	}
}

// serveHTTPS serves requests from the given listener.
func (app *Application) serveHTTPS(address string) {
	serveError := http.ListenAndServeTLS(address, app.Security.Certificate, app.Security.Key, app.Handler())

	if serveError != nil {
		panic(serveError)
	}
}

// Test tests the given URI paths when the application starts.
func (app *Application) Test(route string, paths []string) {
	app.routeTests[route] = paths
}

// TestManifest tests your application's manifest.
func (app *Application) TestManifest() {
	manifest := app.Config.Manifest

	if len(manifest.ShortName) >= 12 {
		color.Yellow("The short name of your application should have less than 12 characters")
	}
}

// TestRoutes tests your application's routes.
func (app *Application) TestRoutes() {
	fmt.Println(strings.Repeat("-", 80))

	go func() {
		sort.Strings(app.routes.GET)

		for _, route := range app.routes.GET {
			// Ajax routes
			if strings.HasPrefix(route, "/_") {
				continue
			}

			testRoutes, exists := app.routeTests[route]

			if exists {
				for _, testRoute := range testRoutes {
					app.TestRoute(testRoute)
				}

				continue
			}

			// Routes with parameters
			if strings.Contains(route, ":") {
				color.Yellow(route)
				continue
			}

			app.TestRoute(route)
		}

		// json, _ := Post("https://html5.validator.nu/?out=json").Header("Content-Type", "text/html; charset=utf-8").Header("Content-Encoding", "gzip").Body(body).Send()
		// fmt.Println(json)
	}()
}

// TestRoute tests the given route.
func (app *Application) TestRoute(route string) {
	start := time.Now()
	body, _ := Get("http://localhost:" + strconv.Itoa(app.Config.Ports.HTTP) + route).Send()
	responseTime := time.Since(start).Nanoseconds() / 1000000
	responseSize := float64(len(body)) / 1024

	faint := color.New(color.Faint).SprintFunc()

	// Response size color
	var responseSizeColor func(a ...interface{}) string

	switch {
	case responseSize < 15:
		responseSizeColor = color.New(color.FgGreen).SprintFunc()
	case responseSize < 100:
		responseSizeColor = color.New(color.FgYellow).SprintFunc()
	default:
		responseSizeColor = color.New(color.FgRed).SprintFunc()
	}

	// Response time color
	var responseTimeColor func(a ...interface{}) string

	switch {
	case responseTime < 10:
		responseTimeColor = color.New(color.FgGreen).SprintFunc()
	case responseTime < 100:
		responseTimeColor = color.New(color.FgYellow).SprintFunc()
	default:
		responseTimeColor = color.New(color.FgRed).SprintFunc()
	}

	fmt.Printf("%-67s %s %s %s %s\n", color.BlueString(route), responseSizeColor(fmt.Sprintf("%6.0f", responseSize)), faint("KB"), responseTimeColor(fmt.Sprintf("%7d", responseTime)), faint("ms"))
}
<commit_msg>Set default display to fullscreen<commit_after>package aero

import (
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"

	"crypto/sha256"

	"encoding/base64"
	"encoding/json"

	"io/ioutil"

	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
)

const (
	gzipCacheDuration = 5 * time.Minute
	gzipCacheCleanup  = 1 * time.Minute
)

// Middleware ...
type Middleware func(*Context, func())

// Application represents a single web service.
type Application struct {
	root string

	Config   *Configuration
	Layout   func(*Context, string) string
	Sessions SessionManager
	Security ApplicationSecurity

	router *httprouter.Router
	routes struct {
		GET  []string
		POST []string
	}
	routeTests map[string][]string
	gzipCache  *cache.Cache
	start      time.Time
	rewrite    func(*RewriteContext)

	middleware []Middleware

	css            string
	cssHash        string
	cssReplacement string

	contentSecurityPolicy string
}

// New creates a new application.
func New() *Application {
	app := new(Application)
	app.start = time.Now()
	app.router = httprouter.New()
	app.routeTests = make(map[string][]string)
	app.gzipCache = cache.New(gzipCacheDuration, gzipCacheCleanup)
	app.Layout = func(ctx *Context, content string) string {
		return content
	}
	app.Config = new(Configuration)
	app.Config.Reset()
	app.Load()

	app.Sessions.Store = NewMemoryStore()

	return app
}

// Get registers your function to be called when a certain GET path has been requested.
func (app *Application) Get(path string, handle Handle) {
	app.routes.GET = append(app.routes.GET, path)
	app.router.GET(path, app.createRouteHandler(path, handle))
}

// Post registers your function to be called when a certain POST path has been requested.
func (app *Application) Post(path string, handle Handle) {
	app.routes.POST = append(app.routes.POST, path)
	app.router.POST(path, app.createRouteHandler(path, handle))
}

// createRouteHandler creates a handler function for httprouter.
func (app *Application) createRouteHandler(path string, handle Handle) httprouter.Handle {
	return func(response http.ResponseWriter, request *http.Request, params httprouter.Params) {
		// Create context.
		ctx := Context{
			App:        app,
			StatusCode: http.StatusOK,
			request:    request,
			response:   response,
			params:     params,
			start:      time.Now(),
		}

		// The last part of the call chain will send the actual response.
		lastPartOfCallChain := func() {
			data := handle(&ctx)
			ctx.Respond(data)
		}

		// Declare the type of generateNext so that we can define it recursively in the next part.
		var generateNext func(index int) func()

		// Create a function that returns a bound function next()
		// which can be used as the 2nd parameter in the call chain.
		generateNext = func(index int) func() {
			if index == len(app.middleware) {
				return lastPartOfCallChain
			}

			return func() {
				app.middleware[index](&ctx, generateNext(index+1))
			}
		}

		generateNext(0)()
	}
}

// Ajax calls app.Get for both /route and /_/route
func (app *Application) Ajax(path string, handle Handle) {
	app.Get("/_"+path, handle)
	app.Get(path, func(ctx *Context) string {
		page := handle(ctx)
		html := app.Layout(ctx, page)
		html = strings.Replace(html, "<body", app.cssReplacement, 1)
		html = strings.Replace(html, "<html", "<!DOCTYPE html><html", 1)
		return html
	})
}

// Run starts your application.
func (app *Application) Run() {
	app.TestManifest()
	app.TestRoutes()
	app.Listen()
}

// Use adds middleware to your middleware chain.
func (app *Application) Use(middlewares ...Middleware) {
	app.middleware = append(app.middleware, middlewares...)
}

// Load loads the application data from the file system.
func (app *Application) Load() {
	config, readError := ioutil.ReadFile("config.json")

	if readError == nil {
		jsonDecodeError := json.Unmarshal(config, app.Config)

		if jsonDecodeError != nil {
			color.Red(jsonDecodeError.Error())
		}
	}

	if app.Config.Manifest.Name == "" {
		app.Config.Manifest.Name = app.Config.Title
	}

	if app.Config.Manifest.ShortName == "" {
		app.Config.Manifest.ShortName = app.Config.Title
	}

	if app.Config.Manifest.Lang == "" {
		app.Config.Manifest.Lang = "en"
	}

	if app.Config.Manifest.Display == "" {
		app.Config.Manifest.Display = "fullscreen"
	}

	if app.Config.Manifest.StartURL == "" {
		app.Config.Manifest.StartURL = "/"
	}
}

// Listen starts the server.
func (app *Application) Listen() {
	if app.Security.Key != "" && app.Security.Certificate != "" {
		go func() {
			app.serveHTTPS(":" + strconv.Itoa(app.Config.Ports.HTTPS))
		}()

		fmt.Println("Server running on:", color.GreenString("https://localhost:"+strconv.Itoa(app.Config.Ports.HTTPS)))
	} else {
		fmt.Println("Server running on:", color.GreenString("http://localhost:"+strconv.Itoa(app.Config.Ports.HTTP)))
	}

	app.serveHTTP(":" + strconv.Itoa(app.Config.Ports.HTTP))
}

// // listen listens on the specified host and port.
// func (app *Application) listen(port int) net.Listener {
// 	address := ":" + strconv.Itoa(port)

// 	listener, bindError := net.Listen("tcp", address)

// 	if bindError != nil {
// 		panic(bindError)
// 	}

// 	return listener
// }

// Rewrite sets the URL rewrite function.
func (app *Application) Rewrite(rewrite func(*RewriteContext)) {
	app.rewrite = rewrite
}

// SetStyle ...
func (app *Application) SetStyle(css string) {
	app.css = css

	hash := sha256.Sum256([]byte(css))
	app.cssHash = base64.StdEncoding.EncodeToString(hash[:])
	app.cssReplacement = "<body"
	app.contentSecurityPolicy = "default-src 'none'; img-src https:; script-src 'self'; style-src 'sha256-" + app.cssHash + "'; font-src https:; manifest-src 'self'; child-src https:; connect-src https: wss:"
}

// StartTime ...
func (app *Application) StartTime() time.Time {
	return app.start
}

// Handler returns the request handler.
func (app *Application) Handler() http.Handler {
	router := app.router
	rewrite := app.rewrite

	if rewrite != nil {
		return &rewriteHandler{
			rewrite: rewrite,
			router:  router,
		}
	}

	return router
}

// serveHTTP serves requests from the given listener.
func (app *Application) serveHTTP(address string) {
	serveError := http.ListenAndServe(address, app.Handler())

	if serveError != nil {
		panic(serveError)
	}
}

// serveHTTPS serves requests from the given listener.
func (app *Application) serveHTTPS(address string) {
	serveError := http.ListenAndServeTLS(address, app.Security.Certificate, app.Security.Key, app.Handler())

	if serveError != nil {
		panic(serveError)
	}
}

// Test tests the given URI paths when the application starts.
func (app *Application) Test(route string, paths []string) {
	app.routeTests[route] = paths
}

// TestManifest tests your application's manifest.
func (app *Application) TestManifest() {
	manifest := app.Config.Manifest

	if len(manifest.ShortName) >= 12 {
		color.Yellow("The short name of your application should have less than 12 characters")
	}
}

// TestRoutes tests your application's routes.
func (app *Application) TestRoutes() {
	fmt.Println(strings.Repeat("-", 80))

	go func() {
		sort.Strings(app.routes.GET)

		for _, route := range app.routes.GET {
			// Ajax routes
			if strings.HasPrefix(route, "/_") {
				continue
			}

			testRoutes, exists := app.routeTests[route]

			if exists {
				for _, testRoute := range testRoutes {
					app.TestRoute(testRoute)
				}

				continue
			}

			// Routes with parameters
			if strings.Contains(route, ":") {
				color.Yellow(route)
				continue
			}

			app.TestRoute(route)
		}

		// json, _ := Post("https://html5.validator.nu/?out=json").Header("Content-Type", "text/html; charset=utf-8").Header("Content-Encoding", "gzip").Body(body).Send()
		// fmt.Println(json)
	}()
}

// TestRoute tests the given route.
func (app *Application) TestRoute(route string) {
	start := time.Now()
	body, _ := Get("http://localhost:" + strconv.Itoa(app.Config.Ports.HTTP) + route).Send()
	responseTime := time.Since(start).Nanoseconds() / 1000000
	responseSize := float64(len(body)) / 1024

	faint := color.New(color.Faint).SprintFunc()

	// Response size color
	var responseSizeColor func(a ...interface{}) string

	switch {
	case responseSize < 15:
		responseSizeColor = color.New(color.FgGreen).SprintFunc()
	case responseSize < 100:
		responseSizeColor = color.New(color.FgYellow).SprintFunc()
	default:
		responseSizeColor = color.New(color.FgRed).SprintFunc()
	}

	// Response time color
	var responseTimeColor func(a ...interface{}) string

	switch {
	case responseTime < 10:
		responseTimeColor = color.New(color.FgGreen).SprintFunc()
	case responseTime < 100:
		responseTimeColor = color.New(color.FgYellow).SprintFunc()
	default:
		responseTimeColor = color.New(color.FgRed).SprintFunc()
	}

	fmt.Printf("%-67s %s %s %s %s\n", color.BlueString(route), responseSizeColor(fmt.Sprintf("%6.0f", responseSize)), faint("KB"), responseTimeColor(fmt.Sprintf("%7d", responseTime)), faint("ms"))
}
<|endoftext|>


<commit_before>package currency

import (
	"errors"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/thrasher-/gocryptotrader/common"
)

// Rate holds the current exchange rates for the currency pair.
type Rate struct {
	ID   string  `json:"id"`
	Name string  `json:"Name"`
	Rate float64 `json:",string"`
	Date string  `json:"Date"`
	Time string  `json:"Time"`
	Ask  float64 `json:",string"`
	Bid  float64 `json:",string"`
}

// YahooJSONResponseInfo is a sub type that holds JSON response info
type YahooJSONResponseInfo struct {
	Count   int       `json:"count"`
	Created time.Time `json:"created"`
	Lang    string    `json:"lang"`
}

// YahooJSONResponse holds Yahoo API responses
type YahooJSONResponse struct {
	Query struct {
		YahooJSONResponseInfo
		Results struct {
			Rate []Rate `json:"rate"`
		}
	}
}

// FixerResponse contains the data fields for the Fixer API response
type FixerResponse struct {
	Base  string             `json:"base"`
	Date  string             `json:"date"`
	Rates map[string]float64 `json:"rates"`
}

const (
	maxCurrencyPairsPerRequest = 350
	yahooYQLURL                = "https://query.yahooapis.com/v1/public/yql?"
	yahooDatabase              = "store://datatables.org/alltableswithkeys"
	fixerAPI                   = "http://api.fixer.io/latest"
	// DefaultCurrencies has the default minimum of FIAT values
	DefaultCurrencies = "USD,AUD,EUR,CNY"
	// DefaultCryptoCurrencies has the default minimum of crytpocurrency values
	DefaultCryptoCurrencies = "BTC,LTC,ETH,DOGE,DASH,XRP,XMR"
)

// Variables for package which includes base error strings & exportable
// queries
var (
	CurrencyStore             map[string]Rate
	CurrencyStoreFixer        map[string]float64
	BaseCurrencies            []string
	CryptoCurrencies          []string
	ErrCurrencyDataNotFetched = errors.New("yahoo currency data has not been fetched yet")
	ErrCurrencyNotFound       = errors.New("unable to find specified currency")
	ErrQueryingYahoo          = errors.New("unable to query Yahoo currency values")
	ErrQueryingYahooZeroCount = errors.New("yahoo returned zero currency data")
	YahooEnabled              = false
)

// SetProvider sets the currency exchange service used by the currency
// converter
func SetProvider(yahooEnabled bool) {
	if yahooEnabled {
		YahooEnabled = true
		return
	}
	YahooEnabled = false
}

// SwapProvider swaps the currency exchange service used by the curency
// converter
func SwapProvider() {
	if YahooEnabled {
		YahooEnabled = false
		return
	}
	YahooEnabled = true
}

// GetProvider returns the currency exchange service used by the currency
// converter
func GetProvider() string {
	if YahooEnabled {
		return "yahoo"
	}
	return "fixer"
}

// IsDefaultCurrency checks if the currency passed in matches the default
// FIAT currency
func IsDefaultCurrency(currency string) bool {
	defaultCurrencies := common.SplitStrings(DefaultCurrencies, ",")
	return common.DataContains(defaultCurrencies, common.StringToUpper(currency))
}

// IsDefaultCryptocurrency checks if the currency passed in matches the default
// CRYPTO currency
func IsDefaultCryptocurrency(currency string) bool {
	cryptoCurrencies := common.SplitStrings(DefaultCryptoCurrencies, ",")
	return common.DataContains(cryptoCurrencies, common.StringToUpper(currency))
}

// IsFiatCurrency checks if the currency passed is an enabled FIAT currency
func IsFiatCurrency(currency string) bool {
	if len(BaseCurrencies) == 0 {
		log.Println("IsFiatCurrency: BaseCurrencies string variable not populated")
		return false
	}
	return common.DataContains(BaseCurrencies, common.StringToUpper(currency))
}

// IsCryptocurrency checks if the currency passed is an enabled CRYPTO currency.
func IsCryptocurrency(currency string) bool {
	if len(CryptoCurrencies) == 0 {
		log.Println(
			"IsCryptocurrency: CryptoCurrencies string variable not populated",
		)
		return false
	}
	return common.DataContains(CryptoCurrencies, common.StringToUpper(currency))
}

// Update updates the local crypto currency or base currency store
func Update(input []string, cryptos bool) {
	for x := range input {
		if cryptos {
			if !common.DataContains(CryptoCurrencies, input[x]) {
				CryptoCurrencies = append(CryptoCurrencies, input[x])
			}
		} else {
			if !common.DataContains(BaseCurrencies, input[x]) {
				BaseCurrencies = append(BaseCurrencies, input[x])
			}
		}
	}
}

// SeedCurrencyData takes the desired FIAT currency string, if not defined the
// function will assign it the default values. The function will query
// yahoo for the currency values and will seed currency data.
func SeedCurrencyData(fiatCurrencies string) error {
	if fiatCurrencies == "" {
		fiatCurrencies = DefaultCurrencies
	}

	if YahooEnabled {
		return QueryYahooCurrencyValues(fiatCurrencies)
	}

	return FetchFixerCurrencyData()
}

// MakecurrencyPairs takes all supported currency and turns them into pairs.
func MakecurrencyPairs(supportedCurrencies string) string {
	currencies := common.SplitStrings(supportedCurrencies, ",")
	var pairs []string
	count := len(currencies)
	for i := 0; i < count; i++ {
		currency := currencies[i]
		for j := 0; j < count; j++ {
			if currency != currencies[j] {
				pairs = append(pairs, currency+currencies[j])
			}
		}
	}
	return common.JoinStrings(pairs, ",")
}

// ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen
// or vice versa.
func ConvertCurrency(amount float64, from, to string) (float64, error) {
	from = common.StringToUpper(from)
	to = common.StringToUpper(to)

	if from == to {
		return amount, nil
	}

	if YahooEnabled {
		currency := from + to
		_, ok := CurrencyStore[currency]
		if !ok {
			err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):])
			if err != nil {
				return 0, err
			}
		}

		result, ok := CurrencyStore[currency]
		if !ok {
			return 0, ErrCurrencyNotFound
		}
		return amount * result.Rate, nil
	}

	_, ok := CurrencyStoreFixer[from]
	if !ok {
		err := FetchFixerCurrencyData()
		if err != nil {
			return 0, err
		}
	}

	var resultFrom float64
	var resultTo float64

	// First check if we're converting to USD, USD doesn't exist in the rates map
	if to == "USD" {
		resultFrom, ok = CurrencyStoreFixer[from]
		if !ok {
			return 0, ErrCurrencyNotFound
		}
		return amount / resultFrom, nil
	}

	// Check to see if we're converting from USD
	if from == "USD" {
		resultTo, ok = CurrencyStoreFixer[to]
		if !ok {
			return 0, ErrCurrencyNotFound
		}
		return resultTo * amount, nil
	}

	// Otherwise convert to USD, then to the target currency
	resultFrom, ok = CurrencyStoreFixer[from]
	if !ok {
		return 0, ErrCurrencyNotFound
	}

	converted := amount / resultFrom
	resultTo, ok = CurrencyStoreFixer[to]
	if !ok {
		return 0, ErrCurrencyNotFound
	}

	return converted * resultTo, nil
}

// FetchFixerCurrencyData seeds the variable C
func FetchFixerCurrencyData() error {
	var result FixerResponse
	values := url.Values{}
	values.Set("base", "USD")
	url := common.EncodeURLValues(fixerAPI, values)

	CurrencyStoreFixer = make(map[string]float64)

	err := common.SendHTTPGetRequest(url, true, false, &result)
	if err != nil {
		return err
	}

	CurrencyStoreFixer = result.Rates
	return nil
}

// FetchYahooCurrencyData seeds the variable CurrencyStore; this is a
// map[string]Rate
func FetchYahooCurrencyData(currencyPairs []string) error {
	values := url.Values{}
	values.Set(
		"q", fmt.Sprintf("SELECT * from yahoo.finance.xchange WHERE pair in (\"%s\")",
			common.JoinStrings(currencyPairs, ",")),
	)
	values.Set("format", "json")
	values.Set("env", yahooDatabase)

	headers := make(map[string]string)
	headers["Content-Type"] = "application/x-www-form-urlencoded"

	resp, err := common.SendHTTPRequest(
		"POST", yahooYQLURL, headers, strings.NewReader(values.Encode()),
	)
	if err != nil {
		return err
	}

	log.Printf("Currency recv: %s", resp)

	yahooResp := YahooJSONResponse{}
	err = common.JSONDecode([]byte(resp), &yahooResp)
	if err != nil {
		return err
	}

	if yahooResp.Query.Count == 0 {
		return ErrQueryingYahooZeroCount
	}

	for i := 0; i < yahooResp.Query.YahooJSONResponseInfo.Count; i++ {
		CurrencyStore[yahooResp.Query.Results.Rate[i].ID] = yahooResp.Query.Results.Rate[i]
	}
	return nil
}

// QueryYahooCurrencyValues takes in desired currencies, creates pairs then
// uses FetchYahooCurrencyData to seed CurrencyStore
func QueryYahooCurrencyValues(currencies string) error {
	CurrencyStore = make(map[string]Rate)
	currencyPairs := common.SplitStrings(MakecurrencyPairs(currencies), ",")
	log.Printf(
		"%d fiat currency pairs generated. Fetching Yahoo currency data (this may take a minute)..\n",
		len(currencyPairs),
	)
	return FetchYahooCurrencyData(currencyPairs)
}
<commit_msg>Improve Fixer currency conversion<commit_after>package currency

import (
	"errors"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/thrasher-/gocryptotrader/common"
)

// Rate holds the current exchange rates for the currency pair.
type Rate struct {
	ID   string  `json:"id"`
	Name string  `json:"Name"`
	Rate float64 `json:",string"`
	Date string  `json:"Date"`
	Time string  `json:"Time"`
	Ask  float64 `json:",string"`
	Bid  float64 `json:",string"`
}

// YahooJSONResponseInfo is a sub type that holds JSON response info
type YahooJSONResponseInfo struct {
	Count   int       `json:"count"`
	Created time.Time `json:"created"`
	Lang    string    `json:"lang"`
}

// YahooJSONResponse holds Yahoo API responses
type YahooJSONResponse struct {
	Query struct {
		YahooJSONResponseInfo
		Results struct {
			Rate []Rate `json:"rate"`
		}
	}
}

// FixerResponse contains the data fields for the Fixer API response
type FixerResponse struct {
	Base  string             `json:"base"`
	Date  string             `json:"date"`
	Rates map[string]float64 `json:"rates"`
}

const (
	maxCurrencyPairsPerRequest = 350
	yahooYQLURL                = "https://query.yahooapis.com/v1/public/yql?"
	yahooDatabase              = "store://datatables.org/alltableswithkeys"
	fixerAPI                   = "http://api.fixer.io/latest"
	// DefaultCurrencies has the default minimum of FIAT values
	DefaultCurrencies = "USD,AUD,EUR,CNY"
	// DefaultCryptoCurrencies has the default minimum of crytpocurrency values
	DefaultCryptoCurrencies = "BTC,LTC,ETH,DOGE,DASH,XRP,XMR"
)

// Variables for package which includes base error strings & exportable
// queries
var (
	CurrencyStore             map[string]Rate
	CurrencyStoreFixer        map[string]float64
	BaseCurrencies            []string
	CryptoCurrencies          []string
	ErrCurrencyDataNotFetched = errors.New("yahoo currency data has not been fetched yet")
	ErrCurrencyNotFound       = errors.New("unable to find specified currency")
	ErrQueryingYahoo          = errors.New("unable to query Yahoo currency values")
	ErrQueryingYahooZeroCount = errors.New("yahoo returned zero currency data")
	YahooEnabled              = false
)

// SetProvider sets the currency exchange service used by the currency
// converter
func SetProvider(yahooEnabled bool) {
	if yahooEnabled {
		YahooEnabled = true
		return
	}
	YahooEnabled = false
}

// SwapProvider swaps the currency exchange service used by the curency
// converter
func SwapProvider() {
	if YahooEnabled {
		YahooEnabled = false
		return
	}
	YahooEnabled = true
}

// GetProvider returns the currency exchange service used by the currency
// converter
func GetProvider() string {
	if YahooEnabled {
		return "yahoo"
	}
	return "fixer"
}

// IsDefaultCurrency checks if the currency passed in matches the default
// FIAT currency
func IsDefaultCurrency(currency string) bool {
	defaultCurrencies := common.SplitStrings(DefaultCurrencies, ",")
	return common.DataContains(defaultCurrencies, common.StringToUpper(currency))
}

// IsDefaultCryptocurrency checks if the currency passed in matches the default
// CRYPTO currency
func IsDefaultCryptocurrency(currency string) bool {
	cryptoCurrencies := common.SplitStrings(DefaultCryptoCurrencies, ",")
	return common.DataContains(cryptoCurrencies, common.StringToUpper(currency))
}

// IsFiatCurrency checks if the currency passed is an enabled FIAT currency
func IsFiatCurrency(currency string) bool {
	if len(BaseCurrencies) == 0 {
		log.Println("IsFiatCurrency: BaseCurrencies string variable not populated")
		return false
	}
	return common.DataContains(BaseCurrencies, common.StringToUpper(currency))
}

// IsCryptocurrency checks if the currency passed is an enabled CRYPTO currency.
func IsCryptocurrency(currency string) bool {
	if len(CryptoCurrencies) == 0 {
		log.Println(
			"IsCryptocurrency: CryptoCurrencies string variable not populated",
		)
		return false
	}
	return common.DataContains(CryptoCurrencies, common.StringToUpper(currency))
}

// Update updates the local crypto currency or base currency store
func Update(input []string, cryptos bool) {
	for x := range input {
		if cryptos {
			if !common.DataContains(CryptoCurrencies, input[x]) {
				CryptoCurrencies = append(CryptoCurrencies, input[x])
			}
		} else {
			if !common.DataContains(BaseCurrencies, input[x]) {
				BaseCurrencies = append(BaseCurrencies, input[x])
			}
		}
	}
}

// SeedCurrencyData takes the desired FIAT currency string, if not defined the
// function will assign it the default values. The function will query
// yahoo for the currency values and will seed currency data.
func SeedCurrencyData(fiatCurrencies string) error {
	if fiatCurrencies == "" {
		fiatCurrencies = DefaultCurrencies
	}

	if YahooEnabled {
		return QueryYahooCurrencyValues(fiatCurrencies)
	}

	return FetchFixerCurrencyData()
}

// MakecurrencyPairs takes all supported currency and turns them into pairs.
func MakecurrencyPairs(supportedCurrencies string) string {
	currencies := common.SplitStrings(supportedCurrencies, ",")
	var pairs []string
	count := len(currencies)
	for i := 0; i < count; i++ {
		currency := currencies[i]
		for j := 0; j < count; j++ {
			if currency != currencies[j] {
				pairs = append(pairs, currency+currencies[j])
			}
		}
	}
	return common.JoinStrings(pairs, ",")
}

// ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen
// or vice versa.
func ConvertCurrency(amount float64, from, to string) (float64, error) {
	from = common.StringToUpper(from)
	to = common.StringToUpper(to)

	if from == to {
		return amount, nil
	}

	if YahooEnabled {
		currency := from + to
		_, ok := CurrencyStore[currency]
		if !ok {
			err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):])
			if err != nil {
				return 0, err
			}
		}

		result, ok := CurrencyStore[currency]
		if !ok {
			return 0, ErrCurrencyNotFound
		}
		return amount * result.Rate, nil
	}

	if len(CurrencyStoreFixer) == 0 {
		err := FetchFixerCurrencyData()
		if err != nil {
			return 0, err
		}
	}

	var resultFrom float64
	var resultTo float64

	// First check if we're converting to USD, USD doesn't exist in the rates map
	if to == "USD" {
		resultFrom, ok := CurrencyStoreFixer[from]
		if !ok {
			return 0, ErrCurrencyNotFound
		}
		return amount / resultFrom, nil
	}

	// Check to see if we're converting from USD
	if from == "USD" {
		resultTo, ok := CurrencyStoreFixer[to]
		if !ok {
			return 0, ErrCurrencyNotFound
		}
		return resultTo * amount, nil
	}

	// Otherwise convert to USD, then to the target currency
	resultFrom, ok := CurrencyStoreFixer[from]
	if !ok {
		return 0, ErrCurrencyNotFound
	}

	converted := amount / resultFrom
	resultTo, ok = CurrencyStoreFixer[to]
	if !ok {
		return 0, ErrCurrencyNotFound
	}

	return converted * resultTo, nil
}

// FetchFixerCurrencyData seeds the variable C
func FetchFixerCurrencyData() error {
	var result FixerResponse
	values := url.Values{}
	values.Set("base", "USD")
	url := common.EncodeURLValues(fixerAPI, values)

	CurrencyStoreFixer = make(map[string]float64)

	err := common.SendHTTPGetRequest(url, true, false, &result)
	if err != nil {
		return err
	}

	CurrencyStoreFixer = result.Rates
	return nil
}

// FetchYahooCurrencyData seeds the variable CurrencyStore; this is a
// map[string]Rate
func FetchYahooCurrencyData(currencyPairs []string) error {
	values := url.Values{}
	values.Set(
		"q", fmt.Sprintf("SELECT * from yahoo.finance.xchange WHERE pair in (\"%s\")",
			common.JoinStrings(currencyPairs, ",")),
	)
	values.Set("format", "json")
	values.Set("env", yahooDatabase)

	headers := make(map[string]string)
	headers["Content-Type"] = "application/x-www-form-urlencoded"

	resp, err := common.SendHTTPRequest(
		"POST", yahooYQLURL, headers, strings.NewReader(values.Encode()),
	)
	if err != nil {
		return err
	}

	log.Printf("Currency recv: %s", resp)

	yahooResp := YahooJSONResponse{}
	err = common.JSONDecode([]byte(resp), &yahooResp)
	if err != nil {
		return err
	}

	if yahooResp.Query.Count == 0 {
		return ErrQueryingYahooZeroCount
	}

	for i := 0; i < yahooResp.Query.YahooJSONResponseInfo.Count; i++ {
		CurrencyStore[yahooResp.Query.Results.Rate[i].ID] = yahooResp.Query.Results.Rate[i]
	}
	return nil
}

// QueryYahooCurrencyValues takes in desired currencies, creates pairs then
// uses FetchYahooCurrencyData to seed CurrencyStore
func QueryYahooCurrencyValues(currencies string) error {
	CurrencyStore = make(map[string]Rate)
	currencyPairs := common.SplitStrings(MakecurrencyPairs(currencies), ",")
	log.Printf(
		"%d fiat currency pairs generated. Fetching Yahoo currency data (this may take a minute)..\n",
		len(currencyPairs),
	)
	return FetchYahooCurrencyData(currencyPairs)
}
<|endoftext|>


<commit_before>package arel

type SqlStatement struct{}

func (s *SqlStatement) CreateTrue() *TrueNode {
	return NewTrueNode()
}

func (s *SqlStatement) CreateFalse() *FalseNode {
	return NewFalseNode()
}

func (s *SqlStatement) CreateTableAlias(relation *Table, name string) *TableAliasNode {
	return NewTableAliasNode(relation, name)
}

func (s *SqlStatement) CreateJoin(to string) *JoinNode {
	return &JoinNode{}
}

func (s *SqlStatement) CreateAnd(clauses []interface{}) *AndNode {
	return NewAndNode()
}

func (s *SqlStatement) CreateOn(expr string) *OnNode {
	return NewOnNode()
}

func (s *SqlStatement) Grouping(expr string) *GroupingNode {
	return NewGroupingNode()
}

func (s *SqlStatement) Lower() {

}
<commit_msg>Removed sql statement file<commit_after><|endoftext|>


<commit_before>/**
 * Provides a redis storage adapter for storing and retrieving Watches.
 */

package msWatchStorage

import (
	// Utilities
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	// Redis.
  "github.com/mediocregopher/radix.v2/redis"

	// Internal dependencies.
	common "github.com/krystalcode/go-mantis-shrimp/watches/common"
	health "github.com/krystalcode/go-mantis-shrimp/watches/health_check"
)

/**
 * Redis storage provider.
 */

// Implements the Storage interface.
type Redis struct {
	dsn     string
	client *redis.Client
}

// Get a Watch by its ID.
func (storage Redis) Get(_id int) common.Watch {
	if storage.client == nil {
		panic("The Redis client has not been initialized yet.")
	}

	key := redisKey(_id)

	r := storage.client.Cmd("GET", key)
	if r.Err != nil {
		panic(r.Err)
	}

	jsonWatch, err := r.Bytes()
	// If an error happens here, it should be because there is no value for this
	// key. It could be the case that the data is corrupted or the wrong data is
	// stored, we should see how to handle this later.
	// @I Handle edge cases when deserializing json in Redis
	if err != nil {
		return nil
	}

	// @I Dynamically detect the Watch type and convert json to struct
	//    accordingly
	watch := health.Watch{}
	json.Unmarshal(jsonWatch, &watch)

	return watch
}

// Set an Watch.
// @I Consider using hashmaps instead of json values
// @I Investigate risk of a Watch overriding another due to race conditions when
//    creating them
func (storage Redis) Set(watch common.Watch) int {
	if storage.client == nil {
		panic("The Redis client has not been initialized yet.")
	}

	jsonWatch, err := json.Marshal(watch)
	if err != nil {
		panic(err)
	}

	// Generate an ID, store the Watch, and update the Watches index set.
	_id := storage.generateID()
	key := redisKey(_id)
	err = storage.client.Cmd("SET", key, jsonWatch).Err
	if err != nil {
		panic(err)
	}
	err = storage.client.Cmd("ZADD", "watches", _id, key).Err
	if err != nil {
		panic(err)
	}

	return _id
}

func (storage Redis) generateID() int {
	// Get the last ID that exists on the Watches index set, so that we can generate
	// the next one.
	r, err := storage.client.Cmd("ZREVRANGE", "watches", 0, 0, "WITHSCORES").List()
	if err != nil {
		panic(err)
	}

	// If there are no watches yet, start with ID 1.
	if len(r) == 0 {
		return 1
	}

	_id, err := strconv.Atoi(r[1])
	if err != nil {
		panic(err)
	}

	return _id + 1
}

// Implements the StorageFactory interface.
// Connects to the Redis database defined in the given configuration and returns
// the storage.
func NewRedisStorage(config map[string]string) (Storage, error) {
	dsn, ok := config["STORAGE_REDIS_DSN"]
	if !ok {
		err := errors.New(
			fmt.Sprintf(
				"The \"%s\" configuration option is required for the Redis storage",
				"STORAGE_REDIS_DSN",
			),
		)
		return nil, err
	}

	client, err := redis.Dial("tcp", dsn)
	if err != nil {
		err := errors.New(fmt.Sprintf("Failed to connect to Redis: %s", err.Error()))
		return nil, err
	}

	storage := Redis {
		dsn    : dsn,
		client : client,
	}

	return storage, nil
}

/**
 * For internal use.
 */

// Generate a Redis key for the given Watch ID.
func redisKey(_id int) string {
	return "watch:" + string(_id)
}
<commit_msg>ms_watch_storage Fixed redis key not being properly constructed<commit_after>/**
 * Provides a redis storage adapter for storing and retrieving Watches.
 */

package msWatchStorage

import (
	// Utilities
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	// Redis.
  "github.com/mediocregopher/radix.v2/redis"

	// Internal dependencies.
	common "github.com/krystalcode/go-mantis-shrimp/watches/common"
	health "github.com/krystalcode/go-mantis-shrimp/watches/health_check"
)

/**
 * Redis storage provider.
 */

// Implements the Storage interface.
type Redis struct {
	dsn     string
	client *redis.Client
}

// Get a Watch by its ID.
func (storage Redis) Get(_id int) common.Watch {
	if storage.client == nil {
		panic("The Redis client has not been initialized yet.")
	}

	key := redisKey(_id)

	r := storage.client.Cmd("GET", key)
	if r.Err != nil {
		panic(r.Err)
	}

	jsonWatch, err := r.Bytes()
	// If an error happens here, it should be because there is no value for this
	// key. It could be the case that the data is corrupted or the wrong data is
	// stored, we should see how to handle this later.
	// @I Handle edge cases when deserializing json in Redis
	if err != nil {
		return nil
	}

	// @I Dynamically detect the Watch type and convert json to struct
	//    accordingly
	watch := health.Watch{}
	json.Unmarshal(jsonWatch, &watch)

	return watch
}

// Set an Watch.
// @I Consider using hashmaps instead of json values
// @I Investigate risk of a Watch overriding another due to race conditions when
//    creating them
func (storage Redis) Set(watch common.Watch) int {
	if storage.client == nil {
		panic("The Redis client has not been initialized yet.")
	}

	jsonWatch, err := json.Marshal(watch)
	if err != nil {
		panic(err)
	}

	// Generate an ID, store the Watch, and update the Watches index set.
	_id := storage.generateID()
	key := redisKey(_id)
	err = storage.client.Cmd("SET", key, jsonWatch).Err
	if err != nil {
		panic(err)
	}
	err = storage.client.Cmd("ZADD", "watches", _id, key).Err
	if err != nil {
		panic(err)
	}

	return _id
}

func (storage Redis) generateID() int {
	// Get the last ID that exists on the Watches index set, so that we can generate
	// the next one.
	r, err := storage.client.Cmd("ZREVRANGE", "watches", 0, 0, "WITHSCORES").List()
	if err != nil {
		panic(err)
	}

	// If there are no watches yet, start with ID 1.
	if len(r) == 0 {
		return 1
	}

	_id, err := strconv.Atoi(r[1])
	if err != nil {
		panic(err)
	}

	return _id + 1
}

// Implements the StorageFactory interface.
// Connects to the Redis database defined in the given configuration and returns
// the storage.
func NewRedisStorage(config map[string]string) (Storage, error) {
	dsn, ok := config["STORAGE_REDIS_DSN"]
	if !ok {
		err := errors.New(
			fmt.Sprintf(
				"The \"%s\" configuration option is required for the Redis storage",
				"STORAGE_REDIS_DSN",
			),
		)
		return nil, err
	}

	client, err := redis.Dial("tcp", dsn)
	if err != nil {
		err := errors.New(fmt.Sprintf("Failed to connect to Redis: %s", err.Error()))
		return nil, err
	}

	storage := Redis {
		dsn    : dsn,
		client : client,
	}

	return storage, nil
}

/**
 * For internal use.
 */

// Generate a Redis key for the given Watch ID.
func redisKey(_id int) string {
	return "watch:" + strconv.Itoa(_id)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"time"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/gocast"
	"github.com/stampzilla/gocast/events"
	"github.com/stampzilla/gocast/handlers"
	"github.com/stampzilla/gocast/responses"
)

type Chromecast struct {
	Id    string
	Name_ string `json:"Name"`

	PrimaryApp      string
	PrimaryEndpoint string
	Playing         bool
	//Paused          bool

	IsStandBy     bool
	IsActiveInput bool

	Volume float64
	Muted  bool

	Addr net.IP
	Port int

	publish func()

	mediaHandler           *handlers.Media
	mediaConnectionHandler *handlers.Connection

	appLaunch chan string

	*gocast.Device
}

func NewChromecast(d *gocast.Device) *Chromecast {
	c := &Chromecast{
		Device: d,
	}

	d.OnEvent(c.Event)

	c.mediaHandler = &handlers.Media{}
	c.mediaConnectionHandler = &handlers.Connection{}
	c.appLaunch = make(chan string)

	return c
}

func (c *Chromecast) Play() {
	c.mediaHandler.Play()
}
func (c *Chromecast) Pause() {
	c.mediaHandler.Pause()
}
func (c *Chromecast) Stop() {
	c.mediaHandler.Stop()
}

func (c *Chromecast) PlayUrl(url string, contentType string) {
	err := c.Device.ReceiverHandler.LaunchApp(gocast.AppMedia)
	if err != nil && err != handlers.ErrAppAlreadyLaunched {
		log.Error(err)
		return
	}

	if err != handlers.ErrAppAlreadyLaunched {
		//Wait for new media connection to launched app
		if err := c.waitForAppLaunch(gocast.AppMedia); err != nil {
			log.Error(err)
			return
		}
	}

	if contentType == "" {
		contentType = "audio/mpeg"
	}
	item := responses.MediaItem{
		ContentId:   url,
		StreamType:  "BUFFERED",
		ContentType: contentType,
	}
	err = c.mediaHandler.LoadMedia(item, 0, true, map[string]interface{}{})
	if err != nil {
		log.Error(err)
		return
	}
}

func (c *Chromecast) waitForAppLaunch(app string) error {
	select {
	case launchedApp := <-c.appLaunch:
		if app == launchedApp {
			return nil
		}
		return fmt.Errorf("Wrong app launched. Expected %s got %s", app, launchedApp)
	case <-time.After(time.Second * 20):
		return fmt.Errorf("timeout waiting for app launch after 20 seconds")

	}

}
func (c *Chromecast) appLaunched(app string) {
	select {
	case c.appLaunch <- app:
	default:
	}
}

func (c *Chromecast) Event(event events.Event) {
	switch data := event.(type) {
	case events.Connected:
		log.Info(c.Name(), "- Connected")

		c.Addr = c.Ip()
		c.Port = c.Device.Port()
		c.Id = c.Uuid()
		c.Name_ = c.Name()

		state.Add(c)
	case events.Disconnected:
		log.Warn(c.Name(), "- Disconnected")

		state.Remove(c)
	case events.AppStarted:
		log.Info(c.Name(), "- App started:", data.DisplayName, "(", data.AppID, ")")
		//spew.Dump("Data:", data)

		c.PrimaryApp = data.DisplayName
		c.PrimaryEndpoint = data.TransportId

		//If the app supports media controls lets subscribe to it
		if data.HasNamespace("urn:x-cast:com.google.cast.media") {
			c.Subscribe("urn:x-cast:com.google.cast.tp.connection", data.TransportId, c.mediaConnectionHandler)
			c.Subscribe("urn:x-cast:com.google.cast.media", data.TransportId, c.mediaHandler)
		}
		c.appLaunched(data.AppID)

	case events.AppStopped:
		log.Info(c.Name(), "- App stopped:", data.DisplayName, "(", data.AppID, ")")
		//spew.Dump("Data:", data)

		//unsubscribe from old channels
		for _, v := range data.Namespaces {
			c.UnsubscribeByUrnAndDestinationId(v.Name, data.TransportId)
		}
		c.PrimaryApp = ""
		c.PrimaryEndpoint = ""
		c.Playing = false

	case events.ReceiverStatus:
		c.IsStandBy = data.Status.IsStandBy
		c.IsActiveInput = data.Status.IsActiveInput
		c.Volume = data.Status.Volume.Level
		c.Muted = data.Status.Volume.Muted
	case events.Media:
		playing := c.Playing
		if data.PlayerState == "PLAYING" {
			c.Playing = true
		} else {
			c.Playing = false
		}

		//Only publish if playing state changed
		if playing != c.Playing {
			c.publish()
		}
		return

	//gocast.MediaEvent:
	default:
		log.Warn("unexpected event %T: %#v\n", data, data)
	}

	c.publish()
}
<commit_msg>Added more logging<commit_after>package main

import (
	"fmt"
	"net"
	"time"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/gocast"
	"github.com/stampzilla/gocast/events"
	"github.com/stampzilla/gocast/handlers"
	"github.com/stampzilla/gocast/responses"
)

type Chromecast struct {
	Id    string
	Name_ string `json:"Name"`

	PrimaryApp      string
	PrimaryEndpoint string
	Playing         bool
	//Paused          bool

	IsStandBy     bool
	IsActiveInput bool

	Volume float64
	Muted  bool

	Addr net.IP
	Port int

	publish func()

	mediaHandler           *handlers.Media
	mediaConnectionHandler *handlers.Connection

	appLaunch chan string

	*gocast.Device
}

func NewChromecast(d *gocast.Device) *Chromecast {
	c := &Chromecast{
		Device: d,
	}

	d.OnEvent(c.Event)

	c.mediaHandler = &handlers.Media{}
	c.mediaConnectionHandler = &handlers.Connection{}
	c.appLaunch = make(chan string)

	return c
}

func (c *Chromecast) Play() {
	c.mediaHandler.Play()
}
func (c *Chromecast) Pause() {
	c.mediaHandler.Pause()
}
func (c *Chromecast) Stop() {
	c.mediaHandler.Stop()
}

func (c *Chromecast) PlayUrl(url string, contentType string) {
	err := c.Device.ReceiverHandler.LaunchApp(gocast.AppMedia)
	if err != nil && err != handlers.ErrAppAlreadyLaunched {
		log.Error(err)
		return
	}

	if err != handlers.ErrAppAlreadyLaunched {
		//Wait for new media connection to launched app
		if err := c.waitForAppLaunch(gocast.AppMedia); err != nil {
			log.Error(err)
			return
		}
	}

	if contentType == "" {
		contentType = "audio/mpeg"
	}
	item := responses.MediaItem{
		ContentId:   url,
		StreamType:  "BUFFERED",
		ContentType: contentType,
	}
	err = c.mediaHandler.LoadMedia(item, 0, true, map[string]interface{}{})
	if err != nil {
		log.Error(err)
		return
	}
}

func (c *Chromecast) waitForAppLaunch(app string) error {
	select {
	case launchedApp := <-c.appLaunch:
		if app == launchedApp {
			return nil
		}
		return fmt.Errorf("Wrong app launched. Expected %s got %s", app, launchedApp)
	case <-time.After(time.Second * 20):
		return fmt.Errorf("timeout waiting for app launch after 20 seconds")

	}

}
func (c *Chromecast) appLaunched(app string) {
	select {
	case c.appLaunch <- app:
	default:
	}
}

func (c *Chromecast) Event(event events.Event) {
	switch data := event.(type) {
	case events.Connected:
		log.Info(c.Name(), "- Connected")

		c.Addr = c.Ip()
		c.Port = c.Device.Port()
		c.Id = c.Uuid()
		c.Name_ = c.Name()

		state.Add(c)
	case events.Disconnected:
		log.Warn(c.Name(), "- Disconnected")

		state.Remove(c)
	case events.AppStarted:
		log.Info(c.Name(), "- App started:", data.DisplayName, "(", data.AppID, ")")
		//spew.Dump("Data:", data)

		c.PrimaryApp = data.DisplayName
		c.PrimaryEndpoint = data.TransportId

		//If the app supports media controls lets subscribe to it
		if data.HasNamespace("urn:x-cast:com.google.cast.media") {
			log.Info(c.Name(), "- Subscribe cast.tp.connection:", data.DisplayName, "(", data.AppID, ")")
			c.Subscribe("urn:x-cast:com.google.cast.tp.connection", data.TransportId, c.mediaConnectionHandler)
			log.Info(c.Name(), "- Subscribe cast.media:", data.DisplayName, "(", data.AppID, ")")
			c.Subscribe("urn:x-cast:com.google.cast.media", data.TransportId, c.mediaHandler)
		}
		c.appLaunched(data.AppID)
		log.Info(c.Name(), "- Notifying appLanunched:", data.DisplayName, "(", data.AppID, ")")

	case events.AppStopped:
		log.Info(c.Name(), "- App stopped:", data.DisplayName, "(", data.AppID, ")")
		//spew.Dump("Data:", data)

		//unsubscribe from old channels
		for _, v := range data.Namespaces {
			c.UnsubscribeByUrnAndDestinationId(v.Name, data.TransportId)
		}
		c.PrimaryApp = ""
		c.PrimaryEndpoint = ""
		c.Playing = false

	case events.ReceiverStatus:
		c.IsStandBy = data.Status.IsStandBy
		c.IsActiveInput = data.Status.IsActiveInput
		c.Volume = data.Status.Volume.Level
		c.Muted = data.Status.Volume.Muted
	case events.Media:
		playing := c.Playing
		if data.PlayerState == "PLAYING" {
			c.Playing = true
		} else {
			c.Playing = false
		}

		//Only publish if playing state changed
		if playing != c.Playing {
			c.publish()
		}
		return

	//gocast.MediaEvent:
	default:
		log.Warn("unexpected event %T: %#v\n", data, data)
	}

	c.publish()
}
<|endoftext|>


<commit_before>package controller

import (
	"eaciit/gdrj/model"
	"eaciit/gdrj/web/helper"
	"eaciit/gdrj/web/model"
	"errors"
	// "fmt"
	"github.com/eaciit/knot/knot.v1"
	"github.com/eaciit/toolkit"
)

type ReportController struct {
	App
}

func CreateReportController(s *knot.Server) *ReportController {
	var controller = new(ReportController)
	controller.Server = s
	return controller
}

func (m *ReportController) GetDataBranch(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.BranchGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Branch{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataBrand(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.BrandGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Brand{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataHCostCenterGroup(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.HCostCenterGroupGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HCostCenterGroup{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataEntity(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.EntityGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HCostCenterGroup{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataChannel(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.ChannelGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Channel{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataHGeographi(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.HGeographiGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HGeographi{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataCustomer(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	param := struct {
		Keyword string `json:"keyword"`
	}{}

	if err := r.GetForms(&param); err != nil {
		return helper.CreateResult(false, []*gdrj.Customer{}, err.Error())
	}

	res, err := gdrj.CustomerGetContains(param.Keyword)
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Customer{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataCustomerGroup(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.CustomerGroupGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.CustomerGroup{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataHBrandCategory(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.HBrandCategoryGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HBrandCategory{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataProduct(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.ProductGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Product{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataLedgerAccount(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.LedgerAccountGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.LedgerAccount{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataKeyAccount(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.KeyAccountGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.KeyAccount{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataAnalysisIdea(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gocore.AnalysisIdeaGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gocore.AnalysisIdea{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDecreasedQty(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	result, err := gdrj.GetDecreasedQty()
	if err != nil {
		return helper.CreateResult(false, nil, err.Error())
	}

	return result
}

func (m *ReportController) GetPLModel(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	result, err := gdrj.PLModelGetAll()
	if err != nil {
		return helper.CreateResult(false, nil, err.Error())
	}

	return result
}

func (m *ReportController) GetPNLData(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.SalesPLParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	data, err := payload.GetData()
	if err != nil {
		res.SetError(err)
		return res
	}

	plmodels, err := gdrj.PLModelGetAll()
	if err != nil {
		res.SetError(err)
		return res
	}

	res.SetData(toolkit.M{
		"Data":     data,
		"PLModels": plmodels,
	})

	return res
}

func (m *ReportController) GetPNLDataDetail(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.SalesPLDetailParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	data, err := payload.GetData()
	if err != nil {
		res.SetError(err)
		return res
	}

	res.SetData(data)

	return res
}

func (m *ReportController) CheckPNLDataNew(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.PLFinderParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	tableName := payload.GetTableName()
	if gocore.GetConfig(tableName) == "" {
		res.SetError(errors.New("still processing, might take a while"))
		return res
	}

	return res
}

func (m *ReportController) GetPNLDataNew(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.PLFinderParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	ok, err := payload.CountPLData()
	if err != nil {
		res.SetError(err)
		return res
	}

	tableName := payload.GetTableName()

	if ok {
		data, err := payload.GetPLData()
		if err != nil {
			res.SetError(err)
			return res
		}

		res.SetData(data)
		return res
	}

	if gocore.GetConfig(tableName) != nil {
		res.SetError(errors.New("still processing, might take a while"))
		return res
	}

	go func() {
		gocore.SetConfig(tableName, "MANGSTABS!")
		err = payload.GeneratePLData()
		if err != nil {
			gocore.SetConfig(tableName, nil)
		}

		gocore.SetConfig(tableName, nil)
	}()

	return res
}

func (m *ReportController) GenerateRandomLedgerSummary(r *knot.WebContext) interface{} {
	return "ok"

	gocore.GenerateDummySalesLinePL()
	gocore.GenerateDummyLedgerSummary()

	return "ok"
}
<commit_msg>no message<commit_after>package controller

import (
	"eaciit/gdrj/model"
	"eaciit/gdrj/web/helper"
	"eaciit/gdrj/web/model"
	"errors"
	// "fmt"
	"github.com/eaciit/knot/knot.v1"
	"github.com/eaciit/toolkit"
)

type ReportController struct {
	App
}

func CreateReportController(s *knot.Server) *ReportController {
	var controller = new(ReportController)
	controller.Server = s
	return controller
}

func (m *ReportController) GetDataBranch(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.BranchGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Branch{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataBrand(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.BrandGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Brand{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataHCostCenterGroup(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.HCostCenterGroupGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HCostCenterGroup{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataEntity(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.EntityGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HCostCenterGroup{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataChannel(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.ChannelGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Channel{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataHGeographi(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.HGeographiGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HGeographi{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataCustomer(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	param := struct {
		Keyword string `json:"keyword"`
	}{}

	if err := r.GetForms(&param); err != nil {
		return helper.CreateResult(false, []*gdrj.Customer{}, err.Error())
	}

	res, err := gdrj.CustomerGetContains(param.Keyword)
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Customer{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataCustomerGroup(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.CustomerGroupGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.CustomerGroup{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataHBrandCategory(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.HBrandCategoryGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.HBrandCategory{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataProduct(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.ProductGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.Product{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataLedgerAccount(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.LedgerAccountGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.LedgerAccount{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataKeyAccount(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gdrj.KeyAccountGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gdrj.KeyAccount{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDataAnalysisIdea(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	res, err := gocore.AnalysisIdeaGetAll()
	if err != nil {
		return helper.CreateResult(false, []*gocore.AnalysisIdea{}, err.Error())
	}

	return helper.CreateResult(true, res, "")
}

func (m *ReportController) GetDecreasedQty(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	result, err := gdrj.GetDecreasedQty()
	if err != nil {
		return helper.CreateResult(false, nil, err.Error())
	}

	return result
}

func (m *ReportController) GetPLModel(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson

	result, err := gdrj.PLModelGetAll()
	if err != nil {
		return helper.CreateResult(false, nil, err.Error())
	}

	return result
}

func (m *ReportController) GetPNLData(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.SalesPLParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	data, err := payload.GetData()
	if err != nil {
		res.SetError(err)
		return res
	}

	plmodels, err := gdrj.PLModelGetAll()
	if err != nil {
		res.SetError(err)
		return res
	}

	res.SetData(toolkit.M{
		"Data":     data,
		"PLModels": plmodels,
	})

	return res
}

func (m *ReportController) GetPNLDataDetail(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.SalesPLDetailParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	data, err := payload.GetData()
	if err != nil {
		res.SetError(err)
		return res
	}

	res.SetData(data)

	return res
}

func (m *ReportController) CheckPNLDataNew(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.PLFinderParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	tableName := payload.GetTableName()
	if gocore.GetConfig(tableName) == "" {
		res.SetError(errors.New("still processing, might take a while"))
		return res
	}

	return res
}

func (m *ReportController) GetPNLDataNew(r *knot.WebContext) interface{} {
	r.Config.OutputType = knot.OutputJson
	res := new(toolkit.Result)

	payload := new(gdrj.PLFinderParam)
	if err := r.GetPayload(payload); err != nil {
		res.SetError(err)
		return res
	}

	ok, err := payload.CountPLData()
	if err != nil {
		res.SetError(err)
		return res
	}

	tableName := payload.GetTableName()

	if ok {
		data, err := payload.GetPLData()
		if err != nil {
			res.SetError(err)
			return res
		}

		res.SetData(data)
		return res
	}

	if knot.SharedObject().Get(tableName, "") != "" {
		res.SetError(errors.New("still processing, might take a while"))
		return res
	}

	go func() {
		knot.SharedObject().Set(tableName, "MANGSTABS!")
		err = payload.GeneratePLData()
		if err != nil {
			knot.SharedObject().Unset(tableName)
		}

		knot.SharedObject().Unset(tableName)
	}()

	return res
}

func (m *ReportController) GenerateRandomLedgerSummary(r *knot.WebContext) interface{} {
	return "ok"

	gocore.GenerateDummySalesLinePL()
	gocore.GenerateDummyLedgerSummary()

	return "ok"
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 blob

import (
	"fmt"

	"github.com/jacobsa/gcloud/gcs"
)

// Return a blob store that stores blobs in the supplied GCS bucket. GCS object
// names look like:
//
//     <prefix><score>
//
// where <score> is the result of calling Score.Hex.
//
// The blob store trusts that it has full ownership of this portion of the
// bucket's key space -- if a score name exists, then it points to the correct
// data.
func NewGCSStore(
	bucket gcs.Bucket,
	prefix string) (store Store)

type gcsStore struct {
	bucket    gcs.Bucket
	keyPrefix string
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (s *kvBasedBlobStore) makeKey(score Score) (key string) {
	key = s.keyPrefix + score.Hex()
	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (s *kvBasedBlobStore) Store(blob []byte) (score Score, err error) {
	// Will this blob ever fit?
	if len(blob) > s.maxBytesBuffered {
		err = fmt.Errorf(
			"%v-byte blob is larger than buffer size of %v",
			len(blob),
			s.maxBytesBuffered)

		return
	}

	// Compute a score and a key for use under the lock below.
	score = ComputeScore(blob)
	key := s.makeKey(score)

	s.mu.Lock()
	defer s.mu.Unlock()

	// Wait until there is room for this request.
	for !s.hasRoom(len(blob)) {
		s.inFlightChanged.Wait()
	}

	// Have we already decided on a final error?
	if s.writeErr != nil {
		err = fmt.Errorf("Error from previous write: %v", s.writeErr)
		return
	}

	// If we already have a request for this score in flight, we can avoid
	// spawning another one. (If the existing one fails, the user will see an
	// error from Flush.)
	if _, ok := s.inFlight[score]; ok {
		return
	}

	// If the KV store already contains the key, we need not do anything further.
	if s.kvStore.Contains(key) {
		return
	}

	// Mark this blob as in flight.
	s.inFlight[score] = len(blob)
	s.bytesBuffered += len(blob)
	s.inFlightChanged.Broadcast()

	// Spawn a goroutine that will make the blob durable in the background.
	go s.makeDurable(score, key, blob)

	return
}

func (s *kvBasedBlobStore) Flush() (err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Wait until there are no requests in flight.
	for len(s.inFlight) != 0 {
		s.inFlightChanged.Wait()
	}

	err = s.writeErr
	return
}

func (s *kvBasedBlobStore) Contains(score Score) (b bool) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Do we have an in-flight request?
	if _, ok := s.inFlight[score]; ok {
		b = true
		return
	}

	// Does the key exist in the KV store?
	key := s.makeKey(score)
	if s.kvStore.Contains(key) {
		b = true
		return
	}

	return
}

func (s *kvBasedBlobStore) Load(score Score) (blob []byte, err error) {
	// Choose the appropriate key.
	key := s.makeKey(score)

	// Call the key/value store.
	if blob, err = s.kvStore.Get(key); err != nil {
		err = fmt.Errorf("Get: %v", err)
		return
	}

	return
}
<commit_msg>Tightened the contract.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 blob

import (
	"fmt"

	"github.com/jacobsa/gcloud/gcs"
)

// Return a blob store that stores blobs in the supplied GCS bucket. GCS object
// names look like:
//
//     <prefix><score>
//
// where <score> is the result of calling Score.Hex.
//
// The blob store trusts that it has full ownership of this portion of the
// bucket's key space -- if a score name exists, then it points to the correct
// data.
//
// The returned store does not support Flush or Contains; these methods must
// not be called.
func NewGCSStore(
	bucket gcs.Bucket,
	prefix string) (store Store)

type gcsStore struct {
	bucket    gcs.Bucket
	keyPrefix string
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (s *kvBasedBlobStore) makeKey(score Score) (key string) {
	key = s.keyPrefix + score.Hex()
	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (s *kvBasedBlobStore) Store(blob []byte) (score Score, err error) {
	// Will this blob ever fit?
	if len(blob) > s.maxBytesBuffered {
		err = fmt.Errorf(
			"%v-byte blob is larger than buffer size of %v",
			len(blob),
			s.maxBytesBuffered)

		return
	}

	// Compute a score and a key for use under the lock below.
	score = ComputeScore(blob)
	key := s.makeKey(score)

	s.mu.Lock()
	defer s.mu.Unlock()

	// Wait until there is room for this request.
	for !s.hasRoom(len(blob)) {
		s.inFlightChanged.Wait()
	}

	// Have we already decided on a final error?
	if s.writeErr != nil {
		err = fmt.Errorf("Error from previous write: %v", s.writeErr)
		return
	}

	// If we already have a request for this score in flight, we can avoid
	// spawning another one. (If the existing one fails, the user will see an
	// error from Flush.)
	if _, ok := s.inFlight[score]; ok {
		return
	}

	// If the KV store already contains the key, we need not do anything further.
	if s.kvStore.Contains(key) {
		return
	}

	// Mark this blob as in flight.
	s.inFlight[score] = len(blob)
	s.bytesBuffered += len(blob)
	s.inFlightChanged.Broadcast()

	// Spawn a goroutine that will make the blob durable in the background.
	go s.makeDurable(score, key, blob)

	return
}

func (s *kvBasedBlobStore) Flush() (err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Wait until there are no requests in flight.
	for len(s.inFlight) != 0 {
		s.inFlightChanged.Wait()
	}

	err = s.writeErr
	return
}

func (s *kvBasedBlobStore) Contains(score Score) (b bool) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Do we have an in-flight request?
	if _, ok := s.inFlight[score]; ok {
		b = true
		return
	}

	// Does the key exist in the KV store?
	key := s.makeKey(score)
	if s.kvStore.Contains(key) {
		b = true
		return
	}

	return
}

func (s *kvBasedBlobStore) Load(score Score) (blob []byte, err error) {
	// Choose the appropriate key.
	key := s.makeKey(score)

	// Call the key/value store.
	if blob, err = s.kvStore.Get(key); err != nil {
		err = fmt.Errorf("Get: %v", err)
		return
	}

	return
}
<|endoftext|>


<commit_before>package models

type FileStoreInterface interface {
  CreateFile(path string, content []byte) (written bool, err error)
  UpdateFile(path string, content []byte) (written bool, err error)
  CreateDir(path string) (created bool, err error)
  ReadFile(path string) (content []byte, err error)
  ReadDir(path string) (paths []string, err error)
  Move(path string, newPath string) (moved bool, err error)
  Delete(path string) (deleted bool, err error)
}
<commit_msg>Comment file store interface.<commit_after>package models

type FileStoreInterface interface {
  // create new file at path
  CreateFile(path string, content []byte) (written bool, err error)

  // update existing file at path
  UpdateFile(path string, content []byte) (written bool, err error)

  // create dir at path
  CreateDir(path string) (created bool, err error)

  // read content of file at path
  ReadFile(path string) (content []byte, err error)

  // list direct child paths within dir at path
  ReadDir(path string) (paths []string, err error)

  // move file or dir at path
  Move(path string, newPath string) (moved bool, err error)

  // delete file or dir at path
  Delete(path string) (deleted bool, err error)
}
<|endoftext|>