<commit_before>package okex

import (
	"fmt"
	"sync"
	"time"

	"github.com/thrasher-corp/gocryptotrader/common"
	"github.com/thrasher-corp/gocryptotrader/config"
	"github.com/thrasher-corp/gocryptotrader/currency"
	exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
	"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
	"github.com/thrasher-corp/gocryptotrader/exchanges/request"
	"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
	"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
	log "github.com/thrasher-corp/gocryptotrader/logger"
)

// GetDefaultConfig returns a default exchange config
func (o *OKEX) GetDefaultConfig() (*config.ExchangeConfig, error) {
	o.SetDefaults()
	exchCfg := new(config.ExchangeConfig)
	exchCfg.Name = o.Name
	exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
	exchCfg.BaseCurrencies = o.BaseCurrencies

	err := o.SetupDefaults(exchCfg)
	if err != nil {
		return nil, err
	}

	if o.Features.Supports.RESTCapabilities.AutoPairUpdates {
		err = o.UpdateTradablePairs(true)
		if err != nil {
			return nil, err
		}
	}

	return exchCfg, nil
}

// SetDefaults method assignes the default values for OKEX
func (o *OKEX) SetDefaults() {
	o.SetErrorDefaults()
	o.SetCheckVarDefaults()
	o.Name = okExExchangeName
	o.Enabled = true
	o.Verbose = true
	o.API.CredentialsValidator.RequiresKey = true
	o.API.CredentialsValidator.RequiresSecret = true
	o.API.CredentialsValidator.RequiresClientID = true

	o.CurrencyPairs = currency.PairsManager{
		AssetTypes: asset.Items{
			asset.Spot,
			asset.Futures,
			asset.PerpetualSwap,
			asset.Index,
		},
		UseGlobalFormat: false,
	}
	// Same format used for perpetual swap and futures
	fmt1 := currency.PairStore{
		RequestFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "-",
		},
		ConfigFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "_",
		},
	}
	o.CurrencyPairs.Store(asset.PerpetualSwap, fmt1)
	o.CurrencyPairs.Store(asset.Futures, fmt1)

	fmt2 := currency.PairStore{
		RequestFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "-",
		},
		ConfigFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "-",
		},
	}
	o.CurrencyPairs.Store(asset.Spot, fmt2)
	o.CurrencyPairs.Store(asset.Index, fmt2)

	o.Features = exchange.Features{
		Supports: exchange.FeaturesSupported{
			REST:      true,
			Websocket: true,
			RESTCapabilities: exchange.ProtocolFeatures{
				AutoPairUpdates: true,
				TickerBatching:  true,
			},
			WithdrawPermissions: exchange.AutoWithdrawCrypto |
				exchange.NoFiatWithdrawals,
		},
		Enabled: exchange.FeaturesEnabled{
			AutoPairUpdates: true,
		},
	}

	o.Requester = request.New(o.Name,
		request.NewRateLimit(time.Second, okExAuthRate),
		request.NewRateLimit(time.Second, okExUnauthRate),
		common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
	)

	o.API.Endpoints.URLDefault = okExAPIURL
	o.API.Endpoints.URL = okExAPIURL
	o.API.Endpoints.WebsocketURL = OkExWebsocketURL
	o.Websocket = wshandler.New()
	o.APIVersion = okExAPIVersion
	o.Websocket.Functionality = wshandler.WebsocketTickerSupported |
		wshandler.WebsocketTradeDataSupported |
		wshandler.WebsocketKlineSupported |
		wshandler.WebsocketOrderbookSupported |
		wshandler.WebsocketSubscribeSupported |
		wshandler.WebsocketUnsubscribeSupported |
		wshandler.WebsocketAuthenticatedEndpointsSupported |
		wshandler.WebsocketMessageCorrelationSupported
	o.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
	o.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
	o.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}

// Start starts the OKGroup go routine
func (o *OKEX) Start(wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		o.Run()
		wg.Done()
	}()
}

// Run implements the OKEX wrapper
func (o *OKEX) Run() {
	if o.Verbose {
		log.Debugf(log.ExchangeSys, "%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.API.Endpoints.WebsocketURL)
	}
	if o.Config.CurrencyPairs.Pairs[asset.Spot].ConfigFormat == nil || o.Config.CurrencyPairs.Pairs[asset.Spot].RequestFormat == nil {
		fmt := currency.PairStore{
			RequestFormat: &currency.PairFormat{
				Uppercase: true,
				Delimiter: "-",
			},
			ConfigFormat: &currency.PairFormat{
				Uppercase: true,
				Delimiter: "-",
			},
		}
		o.CurrencyPairs.Store(asset.Spot, fmt)
		o.Config.CurrencyPairs.Store(asset.Spot, fmt)
	}

	if !common.StringDataContains(o.Config.CurrencyPairs.Pairs[asset.Spot].Enabled.Strings(), o.CurrencyPairs.Pairs[asset.Spot].RequestFormat.Delimiter) {
		enabledPairs := currency.NewPairsFromStrings([]string{"EOS-USDT"})
		log.Warnf(log.ExchangeSys,
			"Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", o.Name)

		err := o.UpdatePairs(enabledPairs, asset.Spot, true, true)
		if err != nil {
			log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.GetName())
			return
		}
	}

	if !o.GetEnabledFeatures().AutoPairUpdates {
		return
	}

	err := o.UpdateTradablePairs(false)
	if err != nil {
		log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", o.Name, err)
	}
}

// FetchTradablePairs returns a list of the exchanges tradable pairs
func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) {
	var pairs []string
	switch i {
	case asset.Spot:
		prods, err := o.GetSpotTokenPairDetails()
		if err != nil {
			return nil, err
		}

		for x := range prods {
			pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].BaseCurrency, o.GetPairFormat(i, false).Delimiter, prods[x].QuoteCurrency))
		}
		return pairs, nil
	case asset.Futures:
		prods, err := o.GetFuturesContractInformation()
		if err != nil {
			return nil, err
		}

		var pairs []string
		for x := range prods {
			pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].UnderlyingIndex+prods[x].QuoteCurrency, o.GetPairFormat(i, false).Delimiter, prods[x].Delivery))
		}
		return pairs, nil

	case asset.PerpetualSwap:
		prods, err := o.GetSwapContractInformation()
		if err != nil {
			return nil, err
		}

		var pairs []string
		for x := range prods {
			pairs = append(pairs, fmt.Sprintf("%v%v%v%vSWAP", prods[x].UnderlyingIndex, o.GetPairFormat(i, false).Delimiter, prods[x].QuoteCurrency, o.GetPairFormat(i, false).Delimiter))
		}
		return pairs, nil
	case asset.Index:
		return []string{fmt.Sprintf("BTC%vUSD", o.GetPairFormat(i, false).Delimiter)}, nil
	}

	return nil, fmt.Errorf("%s invalid asset type", o.Name)
}

// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (o *OKEX) UpdateTradablePairs(forceUpdate bool) error {
	for x := range o.CurrencyPairs.AssetTypes {
		a := o.CurrencyPairs.AssetTypes[x]
		pairs, err := o.FetchTradablePairs(a)
		if err != nil {
			return err
		}

		err = o.UpdatePairs(currency.NewPairsFromStrings(pairs), a, false, forceUpdate)
		if err != nil {
			return err
		}
	}
	return nil
}

// UpdateTicker updates and returns the ticker for a currency pair
func (o *OKEX) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
	var tickerData ticker.Price
	switch assetType {
	case asset.Spot:
		resp, err := o.GetSpotAllTokenPairsInformation()
		if err != nil {
			return tickerData, err
		}
		pairs := o.GetEnabledPairs(assetType)
		for i := range pairs {
			for j := range resp {
				if !pairs[i].Equal(resp[j].InstrumentID) {
					continue
				}
				tickerData = ticker.Price{
					Last:        resp[j].Last,
					High:        resp[j].High24h,
					Low:         resp[j].Low24h,
					Bid:         resp[j].BestBid,
					Ask:         resp[j].BestAsk,
					Volume:      resp[j].BaseVolume24h,
					QuoteVolume: resp[j].QuoteVolume24h,
					Open:        resp[j].Open24h,
					Pair:        pairs[i],
					LastUpdated: resp[j].Timestamp,
				}
				err = ticker.ProcessTicker(o.Name, &tickerData, assetType)
				if err != nil {
					log.Error(log.Ticker, err)
				}
			}
		}
	case asset.PerpetualSwap:
		resp, err := o.GetAllSwapTokensInformation()
		if err != nil {
			return tickerData, err
		}
		pairs := o.GetEnabledPairs(assetType)
		for i := range pairs {
			for j := range resp {
				if !pairs[i].Equal(resp[j].InstrumentID) {
					continue
				}
				tickerData = ticker.Price{
					Last:        resp[j].Last,
					High:        resp[j].High24H,
					Low:         resp[j].Low24H,
					Bid:         resp[j].BestBid,
					Ask:         resp[j].BestAsk,
					Volume:      resp[j].Volume24H,
					Pair:        resp[j].InstrumentID,
					LastUpdated: resp[j].Timestamp,
				}
				err = ticker.ProcessTicker(o.Name, &tickerData, assetType)
				if err != nil {
					log.Error(log.Ticker, err)
				}
			}
		}
	case asset.Futures:
		resp, err := o.GetAllFuturesTokenInfo()
		if err != nil {
			return tickerData, err
		}
		pairs := o.GetEnabledPairs(assetType)
		for i := range pairs {
			for j := range resp {
				if !pairs[i].Equal(resp[j].InstrumentID) {
					continue
				}
				tickerData = ticker.Price{
					Last:        resp[j].Last,
					High:        resp[j].High24h,
					Low:         resp[j].Low24h,
					Bid:         resp[j].BestBid,
					Ask:         resp[j].BestAsk,
					Volume:      resp[j].Volume24h,
					Pair:        resp[j].InstrumentID,
					LastUpdated: resp[j].Timestamp,
				}
				err = ticker.ProcessTicker(o.Name, &tickerData, assetType)
				if err != nil {
					log.Error(log.Ticker, err)
				}
			}
		}
	}

	return ticker.GetTicker(o.GetName(), p, assetType)
}

// FetchTicker returns the ticker for a currency pair
func (o *OKEX) FetchTicker(p currency.Pair, assetType asset.Item) (tickerData ticker.Price, err error) {
	tickerData, err = ticker.GetTicker(o.GetName(), p, assetType)
	if err != nil {
		return o.UpdateTicker(p, assetType)
	}
	return
}
<commit_msg>Engine: bugfix: OKEX Future swap panic (#350)<commit_after>package okex

import (
	"fmt"
	"sync"
	"time"

	"github.com/thrasher-corp/gocryptotrader/common"
	"github.com/thrasher-corp/gocryptotrader/config"
	"github.com/thrasher-corp/gocryptotrader/currency"
	exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
	"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
	"github.com/thrasher-corp/gocryptotrader/exchanges/request"
	"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
	"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
	log "github.com/thrasher-corp/gocryptotrader/logger"
)

// GetDefaultConfig returns a default exchange config
func (o *OKEX) GetDefaultConfig() (*config.ExchangeConfig, error) {
	o.SetDefaults()
	exchCfg := new(config.ExchangeConfig)
	exchCfg.Name = o.Name
	exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
	exchCfg.BaseCurrencies = o.BaseCurrencies

	err := o.SetupDefaults(exchCfg)
	if err != nil {
		return nil, err
	}

	if o.Features.Supports.RESTCapabilities.AutoPairUpdates {
		err = o.UpdateTradablePairs(true)
		if err != nil {
			return nil, err
		}
	}

	return exchCfg, nil
}

// SetDefaults method assignes the default values for OKEX
func (o *OKEX) SetDefaults() {
	o.SetErrorDefaults()
	o.SetCheckVarDefaults()
	o.Name = okExExchangeName
	o.Enabled = true
	o.Verbose = true
	o.API.CredentialsValidator.RequiresKey = true
	o.API.CredentialsValidator.RequiresSecret = true
	o.API.CredentialsValidator.RequiresClientID = true

	o.CurrencyPairs = currency.PairsManager{
		AssetTypes: asset.Items{
			asset.Spot,
			asset.Futures,
			asset.PerpetualSwap,
			asset.Index,
		},
		UseGlobalFormat: false,
	}
	// Same format used for perpetual swap and futures
	fmt1 := currency.PairStore{
		RequestFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "-",
		},
		ConfigFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "_",
		},
	}
	o.CurrencyPairs.Store(asset.PerpetualSwap, fmt1)
	o.CurrencyPairs.Store(asset.Futures, fmt1)

	fmt2 := currency.PairStore{
		RequestFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "-",
		},
		ConfigFormat: &currency.PairFormat{
			Uppercase: true,
			Delimiter: "-",
		},
	}
	o.CurrencyPairs.Store(asset.Spot, fmt2)
	o.CurrencyPairs.Store(asset.Index, fmt2)

	o.Features = exchange.Features{
		Supports: exchange.FeaturesSupported{
			REST:      true,
			Websocket: true,
			RESTCapabilities: exchange.ProtocolFeatures{
				AutoPairUpdates: true,
				TickerBatching:  true,
			},
			WithdrawPermissions: exchange.AutoWithdrawCrypto |
				exchange.NoFiatWithdrawals,
		},
		Enabled: exchange.FeaturesEnabled{
			AutoPairUpdates: true,
		},
	}

	o.Requester = request.New(o.Name,
		request.NewRateLimit(time.Second, okExAuthRate),
		request.NewRateLimit(time.Second, okExUnauthRate),
		common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
	)

	o.API.Endpoints.URLDefault = okExAPIURL
	o.API.Endpoints.URL = okExAPIURL
	o.API.Endpoints.WebsocketURL = OkExWebsocketURL
	o.Websocket = wshandler.New()
	o.APIVersion = okExAPIVersion
	o.Websocket.Functionality = wshandler.WebsocketTickerSupported |
		wshandler.WebsocketTradeDataSupported |
		wshandler.WebsocketKlineSupported |
		wshandler.WebsocketOrderbookSupported |
		wshandler.WebsocketSubscribeSupported |
		wshandler.WebsocketUnsubscribeSupported |
		wshandler.WebsocketAuthenticatedEndpointsSupported |
		wshandler.WebsocketMessageCorrelationSupported
	o.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
	o.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
	o.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}

// Start starts the OKGroup go routine
func (o *OKEX) Start(wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		o.Run()
		wg.Done()
	}()
}

// Run implements the OKEX wrapper
func (o *OKEX) Run() {
	if o.Verbose {
		log.Debugf(log.ExchangeSys, "%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.API.Endpoints.WebsocketURL)
	}

	if o.Config.CurrencyPairs.Pairs[asset.Spot].ConfigFormat == nil || o.Config.CurrencyPairs.Pairs[asset.Spot].RequestFormat == nil ||
		o.Config.CurrencyPairs.Pairs[asset.Index].ConfigFormat == nil || o.Config.CurrencyPairs.Pairs[asset.Index].RequestFormat == nil {
		currFmt := currency.PairStore{
			RequestFormat: &currency.PairFormat{
				Uppercase: true,
				Delimiter: "-",
			},
			ConfigFormat: &currency.PairFormat{
				Uppercase: true,
				Delimiter: "-",
			},
		}
		o.CurrencyPairs.Store(asset.Spot, currFmt)
		o.Config.CurrencyPairs.Store(asset.Spot, currFmt)
		o.CurrencyPairs.Store(asset.Index, currFmt)
		o.Config.CurrencyPairs.Store(asset.Index, currFmt)
	}

	if o.Config.CurrencyPairs.Pairs[asset.Futures].ConfigFormat == nil || o.Config.CurrencyPairs.Pairs[asset.Futures].RequestFormat == nil ||
		o.Config.CurrencyPairs.Pairs[asset.PerpetualSwap].ConfigFormat == nil || o.Config.CurrencyPairs.Pairs[asset.PerpetualSwap].RequestFormat == nil {
		currFmt := currency.PairStore{
			RequestFormat: &currency.PairFormat{
				Uppercase: true,
				Delimiter: "-",
			},
			ConfigFormat: &currency.PairFormat{
				Uppercase: true,
				Delimiter: "_",
			},
		}
		o.CurrencyPairs.Store(asset.Futures, currFmt)
		o.Config.CurrencyPairs.Store(asset.Futures, currFmt)
		o.CurrencyPairs.Store(asset.PerpetualSwap, currFmt)
		o.Config.CurrencyPairs.Store(asset.PerpetualSwap, currFmt)
	}

	if !common.StringDataContains(o.Config.CurrencyPairs.Pairs[asset.Spot].Enabled.Strings(), o.CurrencyPairs.Pairs[asset.Spot].RequestFormat.Delimiter) {
		enabledPairs := currency.NewPairsFromStrings([]string{"EOS-USDT"})
		log.Warnf(log.ExchangeSys,
			"Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", o.Name)

		err := o.UpdatePairs(enabledPairs, asset.Spot, true, true)
		if err != nil {
			log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.GetName())
			return
		}
	}

	if !o.GetEnabledFeatures().AutoPairUpdates {
		return
	}

	err := o.UpdateTradablePairs(false)
	if err != nil {
		log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", o.Name, err)
	}
}

// FetchTradablePairs returns a list of the exchanges tradable pairs
func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) {
	var pairs []string
	switch i {
	case asset.Spot:
		prods, err := o.GetSpotTokenPairDetails()
		if err != nil {
			return nil, err
		}

		for x := range prods {
			pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].BaseCurrency, o.GetPairFormat(i, false).Delimiter, prods[x].QuoteCurrency))
		}
		return pairs, nil
	case asset.Futures:
		prods, err := o.GetFuturesContractInformation()
		if err != nil {
			return nil, err
		}

		var pairs []string
		for x := range prods {
			pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].UnderlyingIndex+prods[x].QuoteCurrency, o.GetPairFormat(i, false).Delimiter, prods[x].Delivery))
		}
		return pairs, nil

	case asset.PerpetualSwap:
		prods, err := o.GetSwapContractInformation()
		if err != nil {
			return nil, err
		}

		var pairs []string
		for x := range prods {
			pairs = append(pairs, fmt.Sprintf("%v%v%v%vSWAP", prods[x].UnderlyingIndex, o.GetPairFormat(i, false).Delimiter, prods[x].QuoteCurrency, o.GetPairFormat(i, false).Delimiter))
		}
		return pairs, nil
	case asset.Index:
		return []string{fmt.Sprintf("BTC%vUSD", o.GetPairFormat(i, false).Delimiter)}, nil
	}

	return nil, fmt.Errorf("%s invalid asset type", o.Name)
}

// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (o *OKEX) UpdateTradablePairs(forceUpdate bool) error {
	for x := range o.CurrencyPairs.AssetTypes {
		a := o.CurrencyPairs.AssetTypes[x]
		pairs, err := o.FetchTradablePairs(a)
		if err != nil {
			return err
		}

		err = o.UpdatePairs(currency.NewPairsFromStrings(pairs), a, false, forceUpdate)
		if err != nil {
			return err
		}
	}
	return nil
}

// UpdateTicker updates and returns the ticker for a currency pair
func (o *OKEX) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
	var tickerData ticker.Price
	switch assetType {
	case asset.Spot:
		resp, err := o.GetSpotAllTokenPairsInformation()
		if err != nil {
			return tickerData, err
		}
		pairs := o.GetEnabledPairs(assetType)
		for i := range pairs {
			for j := range resp {
				if !pairs[i].Equal(resp[j].InstrumentID) {
					continue
				}
				tickerData = ticker.Price{
					Last:        resp[j].Last,
					High:        resp[j].High24h,
					Low:         resp[j].Low24h,
					Bid:         resp[j].BestBid,
					Ask:         resp[j].BestAsk,
					Volume:      resp[j].BaseVolume24h,
					QuoteVolume: resp[j].QuoteVolume24h,
					Open:        resp[j].Open24h,
					Pair:        pairs[i],
					LastUpdated: resp[j].Timestamp,
				}
				err = ticker.ProcessTicker(o.Name, &tickerData, assetType)
				if err != nil {
					log.Error(log.Ticker, err)
				}
			}
		}
	case asset.PerpetualSwap:
		resp, err := o.GetAllSwapTokensInformation()
		if err != nil {
			return tickerData, err
		}
		pairs := o.GetEnabledPairs(assetType)
		for i := range pairs {
			for j := range resp {
				if !pairs[i].Equal(resp[j].InstrumentID) {
					continue
				}
				tickerData = ticker.Price{
					Last:        resp[j].Last,
					High:        resp[j].High24H,
					Low:         resp[j].Low24H,
					Bid:         resp[j].BestBid,
					Ask:         resp[j].BestAsk,
					Volume:      resp[j].Volume24H,
					Pair:        resp[j].InstrumentID,
					LastUpdated: resp[j].Timestamp,
				}
				err = ticker.ProcessTicker(o.Name, &tickerData, assetType)
				if err != nil {
					log.Error(log.Ticker, err)
				}
			}
		}
	case asset.Futures:
		resp, err := o.GetAllFuturesTokenInfo()
		if err != nil {
			return tickerData, err
		}
		pairs := o.GetEnabledPairs(assetType)
		for i := range pairs {
			for j := range resp {
				if !pairs[i].Equal(resp[j].InstrumentID) {
					continue
				}
				tickerData = ticker.Price{
					Last:        resp[j].Last,
					High:        resp[j].High24h,
					Low:         resp[j].Low24h,
					Bid:         resp[j].BestBid,
					Ask:         resp[j].BestAsk,
					Volume:      resp[j].Volume24h,
					Pair:        resp[j].InstrumentID,
					LastUpdated: resp[j].Timestamp,
				}
				err = ticker.ProcessTicker(o.Name, &tickerData, assetType)
				if err != nil {
					log.Error(log.Ticker, err)
				}
			}
		}
	}

	return ticker.GetTicker(o.GetName(), p, assetType)
}

// FetchTicker returns the ticker for a currency pair
func (o *OKEX) FetchTicker(p currency.Pair, assetType asset.Item) (tickerData ticker.Price, err error) {
	tickerData, err = ticker.GetTicker(o.GetName(), p, assetType)
	if err != nil {
		return o.UpdateTicker(p, assetType)
	}
	return
}
<|endoftext|>


<commit_before>/**
 * Copyright 2016 IBM Corp.
 *
 * 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 filter

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
)

/**
 * Example:
 * service.Options.Filter = filter.New(
 *		filter.Path("id", "134").Eq(),
 *		filter.Path("datacenter.locationName", "Dallas").Eq(),
 *		filter.Path("something.creationDate").Date("01/01/01")
 * ).Build()
 */

type Filter struct {
	Path string
	Op   string
	Opts map[string]interface{}
	Val  interface{}
}

type Filters []Filter

func New(args ...Filter) Filters {
	return args
}

func Build(args ...Filter) string {
	filters := Filters{}

	for _, arg := range args {
		filters = append(filters, arg)
	}

	return filters.Build()
}

func Path(path string, vals ...interface{}) Filter {
	if len(vals) > 0 {
		return Filter{Path: path, Val: vals[0]}
	}

	return Filter{Path: path}
}

// Loops around filters,
// splitting path on '.' and looping around path pieces.
// Idea is to create a map/tree like map[string]interface{}.
// Every component in the path is a node to create in the tree.
// Once we get to the leaf, we set the operation.
// map[string]interface{}{"operation": op+" "+value}
// If Op is "", then just map[string]interface{}{"operation": value}.
// Afterwards, the Opts are traversed; []map[string]interface{}{}
// For every entry in Opts, we create one map, and append it to an array of maps.
// At the end, json.Marshal the whole thing.
func (fs Filters) Build() string {
	result := map[string]interface{}{}
	for _, filter := range fs {
		if filter.Path == "" {
			continue
		}

		cursor := result
		nodes := strings.Split(filter.Path, ".")
		for len(nodes) > 1 {
			branch := nodes[0]
			if cursor[branch] == nil {
				cursor[branch] = map[string]interface{}{}
			}
			cursor = cursor[branch].(map[string]interface{})
			nodes = nodes[1:len(nodes)]
		}

		leaf := nodes[0]
		if filter.Val != nil {
			operation := filter.Val
			if filter.Op != "" {
				format := "%s"
				if reflect.TypeOf(filter.Val).String() != "string" {
					format = "%d"
				}
				operation = filter.Op + " " + fmt.Sprintf(format, filter.Val)
			}

			cursor[leaf] = map[string]interface{}{
				"operation": operation,
			}
		}

		if filter.Opts == nil {
			continue
		}

		options := []map[string]interface{}{}
		for name, value := range filter.Opts {
			options = append(options, map[string]interface{}{
				"name":  name,
				"value": value,
			})
		}

		cursor[leaf] = map[string]interface{}{
			"operation": filter.Op,
			"options":   options,
		}
	}

	jsonStr, _ := json.Marshal(result)
	return string(jsonStr)
}

func (f Filter) Opt(name string, value interface{}) Filter {
	if f.Opts == nil {
		f.Opts = map[string]interface{}{}
	}

	f.Opts[name] = value
	return f
}

func (f Filter) Eq() Filter {
	f.Op = ""
	return f
}

func (f Filter) NotEq() Filter {
	f.Op = "!="
	return f
}

func (f Filter) Like() Filter {
	f.Op = "~"
	return f
}

func (f Filter) NotLike() Filter {
	f.Op = "!~"
	return f
}

func (f Filter) LessThan() Filter {
	f.Op = "<"
	return f
}

func (f Filter) LessThanOrEqual() Filter {
	f.Op = "<="
	return f
}

func (f Filter) GreaterThan() Filter {
	f.Op = ">"
	return f
}

func (f Filter) GreaterThanOrEqual() Filter {
	f.Op = ">="
	return f
}

func (f Filter) IsNull() Filter {
	f.Op = "is null"
	f.Val = nil
	return f
}

func (f Filter) NotNull() Filter {
	f.Op = "not null"
	f.Val = nil
	return f
}

func (f Filter) Contains() Filter {
	f.Op = "*="
	return f
}

func (f Filter) NotContains() Filter {
	f.Op = "!*="
	return f
}

func (f Filter) StartsWith() Filter {
	f.Op = "^="
	return f
}

func (f Filter) NotStartsWith() Filter {
	f.Op = "!^="
	return f
}

func (f Filter) EndsWith() Filter {
	f.Op = "$="
	return f
}

func (f Filter) NotEndsWith() Filter {
	f.Op = "!$="
	return f
}

func (f Filter) In(args ...interface{}) Filter {
	f.Op = "in"
	values := []interface{}{}
	for _, arg := range args {
		values = append(values, arg)
	}

	return f.Opt("data", values)
}

func (f Filter) DaysPast() Filter {
	f.Op = ">= currentDate -"
	return f
}

func (f Filter) Date(date string) Filter {
	f.Op = "isDate"
	f.Val = nil
	return f.Opt("date", date)
}

func (f Filter) DateBefore(date string) Filter {
	f.Op = "lessThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

func (f Filter) DateAfter(date string) Filter {
	f.Op = "greaterThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

func (f Filter) DateBetween(start string, end string) Filter {
	f.Op = "betweenDate"
	f.Val = nil
	return f.Opt("startDate", start).Opt("endDate", end)
}
<commit_msg>Add function comments<commit_after>/**
 * Copyright 2016 IBM Corp.
 *
 * 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.
 */

// See reference at https://sldn.softlayer.com/article/object-filters.
// Examples in the README.md file and in the examples directory.
package filter

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
)

type Filter struct {
	Path string
	Op   string
	Opts map[string]interface{}
	Val  interface{}
}

type Filters []Filter

// Returns an array of Filters that you can later call .Build() on.
func New(args ...Filter) Filters {
	return args
}

// This is like calling New().Build().
// Returns a JSON string that can be used as the object filter.
func Build(args ...Filter) string {
	filters := Filters{}

	for _, arg := range args {
		filters = append(filters, arg)
	}

	return filters.Build()
}

// This creates a new Filter. The first argument (path) is required.
// The second argument (val) is optional.
func Path(path string, val ...interface{}) Filter {
	if len(val) > 0 {
		return Filter{Path: path, Val: val[0]}
	}

	return Filter{Path: path}
}

// Builds the filter string in JSON format
func (fs Filters) Build() string {
	// Loops around filters,
	// splitting path on '.' and looping around path pieces.
	// Idea is to create a map/tree like map[string]interface{}.
	// Every component in the path is a node to create in the tree.
	// Once we get to the leaf, we set the operation.
	// map[string]interface{}{"operation": op+" "+value}
	// If Op is "", then just map[string]interface{}{"operation": value}.
	// Afterwards, the Opts are traversed; []map[string]interface{}{}
	// For every entry in Opts, we create one map, and append it to an array of maps.
	// At the end, json.Marshal the whole thing.
	result := map[string]interface{}{}
	for _, filter := range fs {
		if filter.Path == "" {
			continue
		}

		cursor := result
		nodes := strings.Split(filter.Path, ".")
		for len(nodes) > 1 {
			branch := nodes[0]
			if cursor[branch] == nil {
				cursor[branch] = map[string]interface{}{}
			}
			cursor = cursor[branch].(map[string]interface{})
			nodes = nodes[1:len(nodes)]
		}

		leaf := nodes[0]
		if filter.Val != nil {
			operation := filter.Val
			if filter.Op != "" {
				format := "%s"
				if reflect.TypeOf(filter.Val).String() != "string" {
					format = "%d"
				}
				operation = filter.Op + " " + fmt.Sprintf(format, filter.Val)
			}

			cursor[leaf] = map[string]interface{}{
				"operation": operation,
			}
		}

		if filter.Opts == nil {
			continue
		}

		options := []map[string]interface{}{}
		for name, value := range filter.Opts {
			options = append(options, map[string]interface{}{
				"name":  name,
				"value": value,
			})
		}

		cursor[leaf] = map[string]interface{}{
			"operation": filter.Op,
			"options":   options,
		}
	}

	jsonStr, _ := json.Marshal(result)
	return string(jsonStr)
}

// Add options to the filter. Can be chained for multiple options.
func (f Filter) Opt(name string, value interface{}) Filter {
	if f.Opts == nil {
		f.Opts = map[string]interface{}{}
	}

	f.Opts[name] = value
	return f
}

// Set this filter to test if property is equal to the value
func (f Filter) Eq() Filter {
	f.Op = ""
	return f
}

// Set this filter to test if property is not equal to the value
func (f Filter) NotEq() Filter {
	f.Op = "!="
	return f
}

// Set this filter to test if property is like the value
func (f Filter) Like() Filter {
	f.Op = "~"
	return f
}

// Set this filter to test if property is unlike value
func (f Filter) NotLike() Filter {
	f.Op = "!~"
	return f
}

// Set this filter to test if property is less than value
func (f Filter) LessThan() Filter {
	f.Op = "<"
	return f
}

// Set this filter to test if property is less than or equal to the value
func (f Filter) LessThanOrEqual() Filter {
	f.Op = "<="
	return f
}

// Set this filter to test if property is greater than value
func (f Filter) GreaterThan() Filter {
	f.Op = ">"
	return f
}

// Set this filter to test if property is greater than or equal to value
func (f Filter) GreaterThanOrEqual() Filter {
	f.Op = ">="
	return f
}

// Set this filter to test if property is null
func (f Filter) IsNull() Filter {
	f.Op = "is null"
	f.Val = nil
	return f
}

// Set this filter to test if property is not null
func (f Filter) NotNull() Filter {
	f.Op = "not null"
	f.Val = nil
	return f
}

// Set this filter to test if property contains the value
func (f Filter) Contains() Filter {
	f.Op = "*="
	return f
}

// Set this filter to test if property does not contain the value
func (f Filter) NotContains() Filter {
	f.Op = "!*="
	return f
}

// Set this filter to test if property starts with the value
func (f Filter) StartsWith() Filter {
	f.Op = "^="
	return f
}

// Set this filter to test if property does not start with the value
func (f Filter) NotStartsWith() Filter {
	f.Op = "!^="
	return f
}

// Set this filter to test if property ends with the value
func (f Filter) EndsWith() Filter {
	f.Op = "$="
	return f
}

// Set this filter to test if property does not end with the value
func (f Filter) NotEndsWith() Filter {
	f.Op = "!$="
	return f
}

// Set this filter to test if property is one of the values in args.
func (f Filter) In(args ...interface{}) Filter {
	f.Op = "in"
	values := []interface{}{}
	for _, arg := range args {
		values = append(values, arg)
	}

	return f.Opt("data", values)
}

// Set this filter to test if property has a date older than the value in days.
func (f Filter) DaysPast() Filter {
	f.Op = ">= currentDate -"
	return f
}

// Set this filter to test if property has the exact date as the value.
func (f Filter) Date(date string) Filter {
	f.Op = "isDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date before the value.
func (f Filter) DateBefore(date string) Filter {
	f.Op = "lessThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date after the value.
func (f Filter) DateAfter(date string) Filter {
	f.Op = "greaterThanDate"
	f.Val = nil
	return f.Opt("date", date)
}

// Set this filter to test if property has a date between the values.
func (f Filter) DateBetween(start string, end string) Filter {
	f.Op = "betweenDate"
	f.Val = nil
	return f.Opt("startDate", start).Opt("endDate", end)
}
<|endoftext|>


<commit_before>package packager

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

import (
	// data "butler/data"
	fileutil "butler/fileutil"
	proto "code.google.com/p/goprotobuf/proto"
	tp "tritium/proto"
	yaml "goyaml"
)

type Packager struct {
	MixerDir      string
	IncludePaths  []string
	IsTransformer bool
	Dependencies  map[string]string
	TypeMap       map[string]int
	ExtensionMap  map[string]string
	TypeList      []string
	*tp.Mixer
}

const (
	PACKAGER_VERSION = 1
	LIB_DIR          = "lib"
	SCRIPTS_DIR      = "scripts"
	ENTRY_FILE       = "main.ts"
	DEPS_FILE        = "dependencies.yml"
	TYPES_FILE       = "types.yml"
	MIXER_DIR        = "mixers"
)

func New(relSrcDir string) *Packager {
	pkgr := new(Packager)

	wd, wdErr := os.Getwd()
	if wdErr != nil {
		panic("unable to determine current directory for mixer creation")
	}

	absSrcDir, absErr := filepath.Abs(relSrcDir)
	if absErr != nil {
		panic("unable to absolutize mixer source directory for mixer creation")
	}

	pkgr.MixerDir        = absSrcDir
	pkgr.IncludePaths    = make([]string, 1)
	pkgr.IncludePaths[0] = wd

	pkgr.Mixer           = tp.NewMixer(absSrcDir)
	pkgr.PackagerVersion = proto.Int32(1)
	pkgr.readDependenciesFile()

	pkgr.Mixer.Package      = new(tp.Package)
	pkgr.Mixer.Package.Name = proto.String(pkgr.Mixer.GetName())
	pkgr.Mixer.Package.Path = proto.String(pkgr.MixerDir)

	return pkgr
}

func (pkgr *Packager) readDependenciesFile() {
	depPath := filepath.Join(pkgr.MixerDir, DEPS_FILE)
	depPathExists, existsErr := fileutil.Exists(depPath)
	if existsErr != nil {
		panic(fmt.Sprintf("error reading dependencies file for `%s`", pkgr.Mixer.GetName()))
	}
	if !depPathExists {
		return
	}
	data, readErr := ioutil.ReadFile(filepath.Join(pkgr.MixerDir, DEPS_FILE))
	if readErr != nil {
		panic(fmt.Sprintf("error reading dependencies file for `%s`", pkgr.Mixer.GetName()))
	}
	pkgr.Dependencies = make(map[string]string)
	yaml.Unmarshal(data, &pkgr.Dependencies)
}

func (pkgr *Packager) Build() {
	pkgr.resolveDependencies()
	pkgr.resolveTypes()
	pkgr.populateTypeList()
	pkgr.buildLib()
}

func (pkgr *Packager) resolveDependencies() {
	for name, version := range pkgr.Dependencies {
		pkgr.loadDependency(name, version)
	}
}

func (pkgr *Packager) loadDependency(name, specifiedVersion string) {
	foundMixerSrc := false
	foundCompiledMixer := false
	for _, incPath := range pkgr.IncludePaths {
		depPath := filepath.Join(incPath, name)
		there, err := fileutil.Exists(depPath)
		if !there || err != nil {
			continue
		}
		foundMixerSrc = true
		needed := New(depPath)
		if needed.GetVersion() != specifiedVersion {
			continue
		}
		needed.Build()
		pkgr.mergeWith(needed)
		return
	}

	// TODO: check if the dependency is among the compiled mixers

	if foundMixerSrc || foundCompiledMixer {
		panic(fmt.Sprintf("version %s needed for dependency `%s` of `%s`",
		      specifiedVersion, name, pkgr.GetName()))
	}
	panic(fmt.Sprintf("unable to find dependency `%s` of `%s`", name, pkgr.GetName()))
}

func (pkgr *Packager) resolveTypes() {
	var typeDecs []string

	// see whether a type declarations file exists; if so, read it
	typeFilePath := filepath.Join(pkgr.MixerDir, LIB_DIR, TYPES_FILE)
	there, existsErr := fileutil.Exists(typeFilePath)
	if there && existsErr == nil {
		data, readErr := ioutil.ReadFile(typeFilePath)
		if readErr != nil {
			panic(fmt.Sprintf("error reading type declarations file for `%s`", pkgr.Mixer.GetName()))
		}
		typeDecs = make([]string, 0)
		yaml.Unmarshal(data, &typeDecs)
		if pkgr.TypeMap == nil {
			pkgr.TypeMap = make(map[string]int)
		}
		if pkgr.ExtensionMap == nil {
			pkgr.ExtensionMap = make(map[string]string)
		}

		// now resolve the type declarations
		for _, typeDec := range typeDecs {

			// if the type doesn't extend anything ...
			if !strings.Contains(typeDec, "<") {
				_, isThere := pkgr.TypeMap[typeDec]
				if !isThere {
					pkgr.TypeMap[typeDec] = len(pkgr.TypeMap)
					pkgr.ExtensionMap[typeDec] = ""
					// if the type has already been declared, check for a semantic conflict
				} else if extendee := pkgr.ExtensionMap[typeDec]; extendee != "" {
					panic(fmt.Sprintf("type declaration %s conflicts with previous declaration %s < %s", typeDec, typeDec, extendee))
				}
				// if we get to this point, the declaration is a duplicate, so do nothing

			// else it's an extension
			} else {
				// parse the supertype and subtype
				splitted := strings.Split(typeDec, "<")
				if len(splitted) != 2 {
					panic(fmt.Sprintf("invalid syntax in type declaration `%s`; only one extension is permitted per declaration", typeDec))
				}
				sub, super := strings.TrimSpace(splitted[0]), strings.TrimSpace(splitted[1])
				// make sure that the supertype in a declaration actually exists
				_, there := pkgr.TypeMap[super]
				if !there {
					panic(fmt.Sprintf("cannot extend type `%s` before it has been declared", super))
				}
				// check for conflicts with previous declarations
				// (and incidentally ensure that subtypes always have a higher id than their supertypes
				extendee, subHasAlreadyExtended := pkgr.ExtensionMap[sub]
				if subHasAlreadyExtended && extendee != super {
					var previousDec string
					if extendee == "" {
						previousDec = sub + " (extends nothing)"
					} else {
						previousDec = fmt.Sprintf("%s < %s", sub, extendee)
					}
					panic(fmt.Sprintf("type declaration `%s` conflicts with previous declaration `%s`", typeDec, previousDec))
				}
				// if we get this far, we can proceed with the type extension
				pkgr.TypeMap[sub] = len(pkgr.TypeMap)
				pkgr.ExtensionMap[sub] = super
			}
		}

		println()
		println("TYPES:")
		for name, id := range pkgr.TypeMap {
			println(name, id)
		}
		println()
		println("EXTENSIONS:")
		for sub, super := range pkgr.ExtensionMap {
			if super != "" {
				println(sub, "<", super)
			} else {
				println(sub)
			}
		}
	}
}

func (pkgr *Packager) populateTypeList() {
	numTypes := len(pkgr.TypeMap)
	pkgr.TypeList = make([]string, numTypes)
	pkgr.Package.Types = make([]*tp.Type, numTypes)
	for name, id := range pkgr.TypeMap {
		pkgr.TypeList[id] = name
		pkgr.Package.Types[id] = &tp.Type{
			Name: proto.String(name),
			Implements: proto.Int32(int32(pkgr.TypeMap[pkgr.ExtensionMap[name]])),
		}
	}
	println()
	println("TYPES IN ORDER:")
	for _, name := range pkgr.TypeList {
		println(name)
	}
}

func (pkgr *Packager) buildLib() {
	return
}

func (pkgr *Packager) mergeWith(dep *Packager) {
	// should be safe to share maps -- dependencies won't be used after they're resolved

	if pkgr.TypeMap == nil {
		pkgr.TypeMap = dep.TypeMap
	} else {
		for name, _ := range dep.TypeMap {
			_, alreadyDeclared := pkgr.TypeMap[name]
			if alreadyDeclared {
				panic(fmt.Sprintf("redeclaration of type `%s` in different packages", name))
			} else {
				// should be safe to append, since inheritance requires supertypes to
				// be declared either in the same mixer, or in a dependency, so the
				// invariant that subtypes come after their supertypes is maintained
				pkgr.TypeMap[name] = len(pkgr.TypeMap)
			}
		}
	}

	if pkgr.ExtensionMap == nil {
		pkgr.ExtensionMap = dep.ExtensionMap
	} else {
		// the previous step ensured that there are no conflicts, so just blaze ahead
		for sub, super := range dep.ExtensionMap {
			pkgr.ExtensionMap[sub] = super
		}
	}
}<commit_msg>More function resolution overhauling.<commit_after>package packager

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

import (
	// data "butler/data"
	fileutil "butler/fileutil"
	"tritium/parser"
	proto "code.google.com/p/goprotobuf/proto"
	tp "tritium/proto"
	yaml "goyaml"
)

type Packager struct {
	MixerDir      string
	LibDir        string
	IncludePaths  []string
	IsTransformer bool
	Dependencies  map[string]string
	TypeMap       map[string]int
	ExtensionMap  map[string]string
	TypeList      []string
	*tp.Mixer
}

const (
	PACKAGER_VERSION = 1
	SCRIPTS_DIR      = "scripts"
	ENTRY_FILE       = "main.ts"
	DEPS_FILE        = "dependencies.yml"
	TYPES_FILE       = "types.yml"
)

func New(relSrcDir, libDir string) *Packager {
	pkgr := new(Packager)

	wd, wdErr := os.Getwd()
	if wdErr != nil {
		panic("unable to determine current directory for mixer creation")
	}

	absSrcDir, absErr := filepath.Abs(relSrcDir)
	if absErr != nil {
		panic("unable to absolutize mixer source directory for mixer creation")
	}

	pkgr.MixerDir        = absSrcDir
	pkgr.LibDir          = libDir
	pkgr.IncludePaths    = make([]string, 1)
	pkgr.IncludePaths[0] = wd

	pkgr.Mixer           = tp.NewMixer(absSrcDir)
	pkgr.PackagerVersion = proto.Int32(1)
	pkgr.readDependenciesFile()

	pkgr.Mixer.Package      = new(tp.Package)
	pkgr.Mixer.Package.Name = proto.String(pkgr.Mixer.GetName())
	pkgr.Mixer.Package.Path = proto.String(pkgr.MixerDir)

	return pkgr
}

func (pkgr *Packager) readDependenciesFile() {
	depPath := filepath.Join(pkgr.MixerDir, DEPS_FILE)
	depPathExists, _ := fileutil.Exists(depPath)
	if !depPathExists {
		return
	}
	data, readErr := ioutil.ReadFile(filepath.Join(pkgr.MixerDir, DEPS_FILE))
	if readErr != nil {
		panic(fmt.Sprintf("error reading dependencies file for `%s`", pkgr.Mixer.GetName()))
	}
	pkgr.Dependencies = make(map[string]string)
	yaml.Unmarshal(data, &pkgr.Dependencies)
}

func (pkgr *Packager) Build() {
	pkgr.resolveDependencies()
	pkgr.resolveTypes()
	pkgr.populateTypeList()
	pkgr.buildLib()
}

func (pkgr *Packager) resolveDependencies() {
	for name, version := range pkgr.Dependencies {
		pkgr.loadDependency(name, version)
	}
}

func (pkgr *Packager) loadDependency(name, specifiedVersion string) {
	foundMixerSrc := false
	foundCompiledMixer := false
	for _, incPath := range pkgr.IncludePaths {
		depPath := filepath.Join(incPath, name)
		there, err := fileutil.Exists(depPath)
		if !there || err != nil {
			continue
		}
		foundMixerSrc = true
		needed := New(depPath, "lib")
		if needed.GetVersion() != specifiedVersion {
			continue
		}
		needed.Build()
		pkgr.mergeWith(needed)
		return
	}

	// TODO: check if the dependency is among the compiled mixers

	if foundMixerSrc || foundCompiledMixer {
		panic(fmt.Sprintf("version %s needed for dependency `%s` of `%s`",
		      specifiedVersion, name, pkgr.GetName()))
	}
	panic(fmt.Sprintf("unable to find dependency `%s` of `%s`", name, pkgr.GetName()))
}

func (pkgr *Packager) resolveTypes() {
	var typeDecs []string

	// see whether a type declarations file exists; if so, read it
	typeFilePath := filepath.Join(pkgr.MixerDir, pkgr.LibDir, TYPES_FILE)
	there, _ := fileutil.Exists(typeFilePath)
	if !there {
		return
	}
	data, readErr := ioutil.ReadFile(typeFilePath)
	if readErr != nil {
		panic(fmt.Sprintf("error reading type declarations file for `%s`", pkgr.Mixer.GetName()))
	}
	typeDecs = make([]string, 0)
	yaml.Unmarshal(data, &typeDecs)
	if pkgr.TypeMap == nil {
		pkgr.TypeMap = make(map[string]int)
	}
	if pkgr.ExtensionMap == nil {
		pkgr.ExtensionMap = make(map[string]string)
	}

	// now resolve the type declarations
	for _, typeDec := range typeDecs {

		// if the type doesn't extend anything ...
		if !strings.Contains(typeDec, "<") {
			_, isThere := pkgr.TypeMap[typeDec]
			if !isThere {
				pkgr.TypeMap[typeDec] = len(pkgr.TypeMap)
				pkgr.ExtensionMap[typeDec] = ""
				// if the type has already been declared, check for a semantic conflict
			} else if extendee := pkgr.ExtensionMap[typeDec]; extendee != "" {
				panic(fmt.Sprintf("type declaration %s conflicts with previous declaration %s < %s", typeDec, typeDec, extendee))
			}
			// if we get to this point, the declaration is a duplicate, so do nothing

		// else it's an extension
		} else {
			// parse the supertype and subtype
			splitted := strings.Split(typeDec, "<")
			if len(splitted) != 2 {
				panic(fmt.Sprintf("invalid syntax in type declaration `%s`; only one extension is permitted per declaration", typeDec))
			}
			sub, super := strings.TrimSpace(splitted[0]), strings.TrimSpace(splitted[1])
			// make sure that the supertype in a declaration actually exists
			_, there := pkgr.TypeMap[super]
			if !there {
				panic(fmt.Sprintf("cannot extend type `%s` before it has been declared", super))
			}
			// check for conflicts with previous declarations
			// (and incidentally ensure that subtypes always have a higher id than their supertypes
			extendee, subHasAlreadyExtended := pkgr.ExtensionMap[sub]
			if subHasAlreadyExtended && extendee != super {
				var previousDec string
				if extendee == "" {
					previousDec = sub + " (extends nothing)"
				} else {
					previousDec = fmt.Sprintf("%s < %s", sub, extendee)
				}
				panic(fmt.Sprintf("type declaration `%s` conflicts with previous declaration `%s`", typeDec, previousDec))
			}
			// if we get this far, we can proceed with the type extension
			pkgr.TypeMap[sub] = len(pkgr.TypeMap)
			pkgr.ExtensionMap[sub] = super
		}
	}

	println()
	println("TYPES:")
	for name, id := range pkgr.TypeMap {
		println(name, id)
	}
	println()
	println("EXTENSIONS:")
	for sub, super := range pkgr.ExtensionMap {
		if super != "" {
			println(sub, "<", super)
		} else {
			println(sub)
		}
	}
}

func (pkgr *Packager) populateTypeList() {
	numTypes := len(pkgr.TypeMap)
	pkgr.TypeList = make([]string, numTypes)
	pkgr.Package.Types = make([]*tp.Type, numTypes)
	for name, id := range pkgr.TypeMap {
		pkgr.TypeList[id] = name
		pkgr.Package.Types[id] = &tp.Type{
			Name: proto.String(name),
			Implements: proto.Int32(int32(pkgr.TypeMap[pkgr.ExtensionMap[name]])),
		}
	}
	println()
	println("TYPES IN ORDER:")
	for _, typeObj := range pkgr.Package.Types {
		println(typeObj.GetName())
	}
}

func (pkgr *Packager) buildLib() {
	// tritium libraries are optional -- compile them if they're there, otherwise do nothing
	libPath := filepath.Join(pkgr.MixerDir, pkgr.LibDir, ENTRY_FILE)
	there, _ := fileutil.Exists(libPath)
	if !there {
		return
	}

	pkgr.resolveDefinitions(pkgr.LibDir, ENTRY_FILE)
}

func (pkgr *Packager) mergeWith(dep *Packager) {
	// should be safe to share maps -- dependencies won't be used after they're resolved

	if pkgr.TypeMap == nil {
		pkgr.TypeMap = dep.TypeMap
	} else {
		for name, _ := range dep.TypeMap {
			_, alreadyDeclared := pkgr.TypeMap[name]
			if alreadyDeclared {
				panic(fmt.Sprintf("redeclaration of type `%s` in different packages", name))
			} else {
				// should be safe to append, since inheritance requires supertypes to
				// be declared either in the same mixer or in a dependency, so the
				// invariant that subtypes come after their supertypes is maintained
				pkgr.TypeMap[name] = len(pkgr.TypeMap)
			}
		}
	}

	if pkgr.ExtensionMap == nil {
		pkgr.ExtensionMap = dep.ExtensionMap
	} else {
		// the previous step ensured that there are no conflicts, so just add the maps
		for sub, super := range dep.ExtensionMap {
			pkgr.ExtensionMap[sub] = super
		}
	}
}

func (pkgr *Packager) resolveDefinitions(dirName, fileName string) {
	// the existence of `fileName` should be verified by the caller
	defs := parser.ParseFile(pkgr.MixerDir, dirName, fileName)

	for _, f := range defs.Functions {
		// if it's an import
		if f.GetName() == "@import" {
			importPath := f.GetDescription()
			importExists, _ := fileutil.Exists(filepath.Join(pkgr.MixerDir, importPath))
			if !importExists {
				errURL := "http://help.moovweb.com/entries/22335641-importing-non-existent-files-in-functions-main-ts"
				msg := fmt.Sprintf("\n********\nin file %s:\nattempting to import nonexistent file %s\nPlease consult %s for more information about this error.\n********\n", filepath.Join(pkgr.LibDir, fileName), importPath, errURL)
				panic(msg)
			}
			println("importing", importPath)
			pkgr.resolveDefinitions(filepath.Dir(importPath), filepath.Base(importPath))

		// otherwise it's not an import
		} else {
			println("resolving def for", f.Stub(pkgr.Package))
		}
	}

}<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"melange/app/framework"
	"melange/app/models"
	"net/http"

	gdb "github.com/huntaub/go-db"
)

// ListIdentity will list all identities on file.
type ListIdentity struct {
	Tables map[string]gdb.Table
	Store  *models.Store
}

// Handle will get all identities then return a JSONView with them.
func (i *ListIdentity) Handle(req *http.Request) framework.View {
	results := make([]*models.Identity, 0)
	err := i.Tables["identity"].Get().All(i.Store, &results)
	if err != nil {
		fmt.Println("Error getting Identities:", err)
		return framework.Error500
	}

	return &framework.JSONView{
		Content: results,
	}
}
<commit_msg>Added Current Flag to Identity List<commit_after>package controllers

import (
	"fmt"
	"melange/app/framework"
	"melange/app/models"
	"net/http"

	gdb "github.com/huntaub/go-db"
)

// ListIdentity will list all identities on file.
type ListIdentity struct {
	Tables map[string]gdb.Table
	Store  *models.Store
}

// Handle will get all identities then return a JSONView with them.
func (i *ListIdentity) Handle(req *http.Request) framework.View {
	results := make([]*models.Identity, 0)
	err := i.Tables["identity"].Get().All(i.Store, &results)
	if err != nil {
		fmt.Println("Error getting Identities:", err)
		return framework.Error500
	}

	fingerprint, err := i.Store.Get("current_identity")
	if err != nil {
		fmt.Println("Error getting current identity.", err)
		return framework.Error500
	}

	for _, v := range results {
		if v.Fingerprint == fingerprint {
			v.Current = true
		}
	}

	return &framework.JSONView{
		Content: results,
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Jon Arild Torresdal <jon@torresdal.net>
//
// 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 cmd

import (
  "log"
  "fmt"
	"github.com/spf13/cobra"
  "os"
  "path/filepath"
)

var autocompleteTarget string
var autocompleteType string

// applicationsCmd represents the applications command
var genCmd = &cobra.Command{
	Use:   "gen",
	Short: "Generate files",
	Long: "",
}

var genAutocompleteCmd = &cobra.Command {
  Use: "autocomplete",
  Short: "Generate file for bash autocompletion. Probably needs sudo: sudo spinex gen autocomplete",
  Long: "",
  Run: func(cmd *cobra.Command, args []string) {
    err := os.MkdirAll(autocompleteTarget, os.ModePerm)
    if err != nil {
			log.Fatal(err)
		}

    path := filepath.Join(autocompleteTarget, "spinex.sh")
    err = cmd.Root().GenBashCompletionFile(path)
    if err != nil {
			log.Fatal(err)
		}

    fmt.Println("Bash completion file for Hugo saved to", path)
  },
}

func init() {
	RootCmd.AddCommand(genCmd)
  genCmd.AddCommand(genAutocompleteCmd)

  genAutocompleteCmd.PersistentFlags().StringVarP(&autocompleteTarget, "completiondir", "", "/etc/bash_completion.d", "Autocompletion target dir")
}
<commit_msg>Added more description to autogen<commit_after>// Copyright © 2016 Jon Arild Torresdal <jon@torresdal.net>
//
// 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 cmd

import (
  "log"
  "fmt"
	"github.com/spf13/cobra"
  "os"
  "path/filepath"
)

var autocompleteTarget string
var autocompleteType string

// applicationsCmd represents the applications command
var genCmd = &cobra.Command{
	Use:   "gen",
	Short: "Generate files",
	Long: "",
}

var genAutocompleteCmd = &cobra.Command {
  Use: "autocomplete",
  Short: "Generate file for bash autocompletion.",
  Long: "Generate file for bash autocompletion. Probably needs sudo: sudo spinex gen autocomplete",
  Run: func(cmd *cobra.Command, args []string) {
    err := os.MkdirAll(autocompleteTarget, os.ModePerm)
    if err != nil {
			log.Fatal(err)
		}

    path := filepath.Join(autocompleteTarget, "spinex.sh")
    err = cmd.Root().GenBashCompletionFile(path)
    if err != nil {
			log.Fatal(err)
		}

    fmt.Println("Bash completion file for Hugo saved to", path)
    fmt.Println("")
    fmt.Println("Restart terminal or run ")
    fmt.Println("")
    fmt.Printf("  . %s\n", path)
    fmt.Println("")
    fmt.Println("to enable autocompletion now.")
  },
}

var genSpinexConfig = &cobra.Command {
  Use: "config",
  Short: "Generate spinex configuration file",
  Long: "",
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("Not implemented")
  },
}

func init() {
	RootCmd.AddCommand(genCmd)
  genCmd.AddCommand(genAutocompleteCmd)
  genCmd.AddCommand(genSpinexConfig)

  genAutocompleteCmd.PersistentFlags().StringVarP(&autocompleteTarget, "completiondir", "", "/etc/bash_completion.d", "Autocompletion target dir")
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/gin-gonic/gin"
)

// ClientRequest TODO
type ClientRequest struct {
	Timestamp    time.Time
	IP           string
	URL          string
	UserAgent    string
	Referer      string
	HTTPCode     int
	ResponseTime time.Duration
}

// Analytics TODO
func Analytics(c *gin.Context) {
	// Get the current time
	then := time.Now()

	// Let the rest of the request happen
	c.Next()

	// Build our client request record
	cr := ClientRequest{
		Timestamp:    time.Now(),
		UserAgent:    c.Request.UserAgent(),
		IP:           c.Request.RemoteAddr,
		URL:          c.Request.RequestURI,
		Referer:      c.Request.Referer(),
		HTTPCode:     c.Writer.Status(),
		ResponseTime: time.Since(then),
	}
	// log this to the database
	orm.Insert(cr)
}
<commit_msg>Renaming database tables to make slightly more sense<commit_after>package main

import (
	"time"

	"github.com/gin-gonic/gin"
)

// ClientRequest TODO
type ClientRequest struct {
	Timestamp    time.Time `xorm:"'timestamp' pk notnull"`
	IP           string    `xorm:"'ip' pk notnull"`
	URL          string    `xorm:"'url' notnull"`
	UserAgent    string    `xorm:"'user-agent'"`
	Referer      string    `xorm:"'referer'"`
	HTTPCode     int       `xorm:"'httpcode'"`
	ResponseTime time.Duration
}

// Analytics TODO
func Analytics(c *gin.Context) {
	// Get the current time
	then := time.Now()

	// Let the rest of the request happen
	c.Next()

	// Build our client request record
	cr := ClientRequest{
		Timestamp:    time.Now(),
		UserAgent:    c.Request.UserAgent(),
		IP:           c.Request.RemoteAddr,
		URL:          c.Request.RequestURI,
		Referer:      c.Request.Referer(),
		HTTPCode:     c.Writer.Status(),
		ResponseTime: time.Since(then),
	}
	// log this to the database
	orm.Insert(cr)
}
<|endoftext|>


<commit_before>package cli

const Version = "0.0.5"
<commit_msg>array types release<commit_after>package cli

const Version = "0.0.6"
<|endoftext|>


<commit_before>package comparer

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
)

const (
	pathSep = string(os.PathSeparator)
)

var (
	// DocType is required If you want to use the package, so don't
	// forget to instantiate It before using the Compare function
	DocType string
	// Docs , Lines and InNeed are `metrics` of how the program is running
	Docs   int
	Lines  int
	InNeed int
)

// Compare is the function that takes two comparable paths to
// directories and writes It's differences into the translation's
// directories files
func Compare(original, translation string) (err error) {
	originalDir, err := ReadDir(original)
	if err != nil {
		return
	}
	for _, f := range originalDir {
		if f.IsDir() {
			err = checkTransDirExists(f.Name(), translation)
			if err != nil {
				return
			}
			err = Compare(filepath.Join(original, f.Name()), filepath.Join(translation, f.Name()))
			if err != nil {
				return
			}
		} else {
			Docs += 2
			err = readFiles(filepath.Join(original, f.Name()), filepath.Join(translation, f.Name()))
			if err != nil {
				return
			}
		}
	}
	return
}

func ReadDir(path string) ([]os.FileInfo, error) {
	err := os.Chdir(path)
	if err != nil {
		return nil, err
	}
	fi, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	file, err := fi.Readdir(0)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	return file, nil
}

func readFiles(originalFile, translationFile string) (err error) {
	if (len(originalFile) == 0) || (len(translationFile) == 0) {
		return errEmptyFileOrPathName
	}
	err = os.Chdir(filepath.Dir(originalFile))
	if err != nil {
		return
	}
	fName := strings.Split(originalFile, pathSep)
	fileName := fName[len(fName)-1]
	orgTags, err := readFile(fileName, filepath.Dir(originalFile))
	if err != nil {
		return
	}
	fName = strings.Split(translationFile, pathSep)
	fileName = fName[len(fName)-1]
	trltTags, err := readFile(fileName, filepath.Dir(translationFile))
	if err != nil {
		err = os.Chdir(filepath.Dir(translationFile))
		if err != nil {
			return
		}
		file, errCreate := os.Create(fileName)
		if errCreate != nil {
			return errCreate
		}
		defer file.Close()
	}
	missingTags := findMissing(orgTags, trltTags)
	if missingTags == nil {
		return
	}
	outdatedTags := findMissing(trltTags, orgTags)
	err = writeToFileMissingTags(translationFile, outdatedTags, true)
	if err != nil {
		return
	}
	err = writeToFileMissingTags(translationFile, missingTags, false)
	if err != nil {
		return
	}
	return
}

func writeToFileMissingTags(translationFilePath string, missingTags map[string]string, outdated bool) (err error) {
	f, err := os.OpenFile(translationFilePath, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return
	}
	defer f.Close()
	for missingKey, missingValue := range missingTags {
		if len(missingKey) == 0 {
			continue
		}
		if string(missingKey[1]) == pathSep {
			continue
		}
		InNeed++
		if isCommentaryOrDocType(missingKey) {
			_, err = f.WriteString(fmt.Sprintf("\n%s", missingKey))
			if err != nil {
				return
			}
			continue
		}
		if outdated {
			_, err = f.WriteString(fmt.Sprintf("\n[OUTDATED]%s", missingKey))
			if err != nil {
				return
			}
			continue
		}
		_, err = f.WriteString(fmt.Sprintf("\n%s%s</%s", missingKey, missingValue, missingKey[1:]))
		if err != nil {
			return
		}
	}
	return
}

func isCommentaryOrDocType(key string) bool {
	if (hasSubstring(key, "<!-")) || (hasSubstring(key, "<--")) || hasSubstring(key, "<?"+DocType) {
		return true
	}
	return false
}

func hasSubstring(str, s string) bool {
	return strings.Contains(s, str)
}

func readFile(fileName, filePath string) (map[string]string, error) {
	if (len(fileName) == 0) || (len(filePath) == 0) {
		return nil, errEmptyFileOrPathName
	}
	splittedFileName := strings.Split(fileName, ".")
	if splittedFileName[len(splittedFileName)-1] != DocType {
		return nil, nil
	}
	inFile, err := os.Open(filepath.Join(filePath, fileName))
	if err != nil {
		return nil, err
	}
	defer inFile.Close()
	tags := map[string]string{}
	scanner := bufio.NewScanner(inFile)
	for scanner.Scan() {
		Lines++
		line := scanner.Text()
		indexStart := strings.Index(line, "<")
		indexEnd := strings.Index(line, ">")
		if (len(line) == 0) || indexStart < 0 || indexEnd < 0 {
			continue
		}
		tag := line[indexStart : indexEnd+1]
		if string(tag[0]) == pathSep {
			continue
		}
		markers := strings.Split(tag, " ")
		tag = markers[0]
		valEnd := strings.LastIndex(line, "<")
		if valEnd < indexEnd {
			continue
		}
		translationValue := line[indexEnd+1 : valEnd]
		if (indexStart != -1) && (indexEnd != -1) {
			tags[tag] = translationValue
		}
	}
	return tags, nil
}

func findMissing(original, translation map[string]string) map[string]string {
	missing := make(map[string]string)
	if reflect.DeepEqual(original, translation) {
		return nil
	}
	for k, v := range original {
		if _, ok := translation[k]; !ok {
			missing[k] = v
		}
	}
	return missing
}

// TODO: refactor
// this should return a bool and an error,
// this way the upper func can handle dir creation
func checkTransDirExists(dir, translation string) (err error) {
	splitDir := strings.Split(dir, pathSep)
	dir = filepath.Join(translation, splitDir[len(splitDir)-1])
	_, err = os.Open(dir)
	if err != nil {
		splitedDirectory := strings.Split(dir, pathSep)
		parentDirFromSplit := dir[:len(dir)-len(splitedDirectory[len(splitedDirectory)-1])-1]
		os.Chdir(parentDirFromSplit)
		err = os.Mkdir(splitedDirectory[len(splitedDirectory)-1], 0700)
		if err != nil {
			return
		}
	}
	return
}
<commit_msg>comparer: rewrite ReadDir<commit_after>package comparer

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
)

const (
	pathSep = string(os.PathSeparator)
)

var (
	// DocType is required If you want to use the package, so don't
	// forget to instantiate It before using the Compare function
	DocType string
	// Docs , Lines and InNeed are `metrics` of how the program is running
	Docs   int
	Lines  int
	InNeed int
)

// Compare is the function that takes two comparable paths to
// directories and writes It's differences into the translation's
// directories files
func Compare(original, translation string) (err error) {
	originalDir, err := ReadDir(original)
	if err != nil {
		return
	}
	for _, f := range originalDir {
		if f.IsDir() {
			err = checkTransDirExists(f.Name(), translation)
			if err != nil {
				return
			}
			err = Compare(filepath.Join(original, f.Name()), filepath.Join(translation, f.Name()))
			if err != nil {
				return
			}
		} else {
			Docs += 2
			err = readFiles(filepath.Join(original, f.Name()), filepath.Join(translation, f.Name()))
			if err != nil {
				return
			}
		}
	}
	return
}

func ReadDir(path string) (file []os.FileInfo, err error) {
	err = os.Chdir(path)
	if err != nil {
		return
	}
	fi, err := os.Open(path)
	if err != nil {
		return
	}
	defer fi.Close()
	file, err = fi.Readdir(0)
	if err != nil {
		return
	}
	return
}

func readFiles(originalFile, translationFile string) (err error) {
	if (len(originalFile) == 0) || (len(translationFile) == 0) {
		return errEmptyFileOrPathName
	}
	err = os.Chdir(filepath.Dir(originalFile))
	if err != nil {
		return
	}
	fName := strings.Split(originalFile, pathSep)
	fileName := fName[len(fName)-1]
	orgTags, err := readFile(fileName, filepath.Dir(originalFile))
	if err != nil {
		return
	}
	fName = strings.Split(translationFile, pathSep)
	fileName = fName[len(fName)-1]
	trltTags, err := readFile(fileName, filepath.Dir(translationFile))
	if err != nil {
		err = os.Chdir(filepath.Dir(translationFile))
		if err != nil {
			return
		}
		file, errCreate := os.Create(fileName)
		if errCreate != nil {
			return errCreate
		}
		defer file.Close()
	}
	missingTags := findMissing(orgTags, trltTags)
	if missingTags == nil {
		return
	}
	outdatedTags := findMissing(trltTags, orgTags)
	err = writeToFileMissingTags(translationFile, outdatedTags, true)
	if err != nil {
		return
	}
	err = writeToFileMissingTags(translationFile, missingTags, false)
	if err != nil {
		return
	}
	return
}

func writeToFileMissingTags(translationFilePath string, missingTags map[string]string, outdated bool) (err error) {
	f, err := os.OpenFile(translationFilePath, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return
	}
	defer f.Close()
	for missingKey, missingValue := range missingTags {
		if len(missingKey) == 0 {
			continue
		}
		if string(missingKey[1]) == pathSep {
			continue
		}
		InNeed++
		if isCommentaryOrDocType(missingKey) {
			_, err = f.WriteString(fmt.Sprintf("\n%s", missingKey))
			if err != nil {
				return
			}
			continue
		}
		if outdated {
			_, err = f.WriteString(fmt.Sprintf("\n[OUTDATED]%s", missingKey))
			if err != nil {
				return
			}
			continue
		}
		_, err = f.WriteString(fmt.Sprintf("\n%s%s</%s", missingKey, missingValue, missingKey[1:]))
		if err != nil {
			return
		}
	}
	return
}

func isCommentaryOrDocType(key string) bool {
	if (hasSubstring(key, "<!-")) || (hasSubstring(key, "<--")) || hasSubstring(key, "<?"+DocType) {
		return true
	}
	return false
}

func hasSubstring(str, s string) bool {
	return strings.Contains(s, str)
}

func readFile(fileName, filePath string) (map[string]string, error) {
	if (len(fileName) == 0) || (len(filePath) == 0) {
		return nil, errEmptyFileOrPathName
	}
	splittedFileName := strings.Split(fileName, ".")
	if splittedFileName[len(splittedFileName)-1] != DocType {
		return nil, nil
	}
	inFile, err := os.Open(filepath.Join(filePath, fileName))
	if err != nil {
		return nil, err
	}
	defer inFile.Close()
	tags := map[string]string{}
	scanner := bufio.NewScanner(inFile)
	for scanner.Scan() {
		Lines++
		line := scanner.Text()
		indexStart := strings.Index(line, "<")
		indexEnd := strings.Index(line, ">")
		if (len(line) == 0) || indexStart < 0 || indexEnd < 0 {
			continue
		}
		tag := line[indexStart : indexEnd+1]
		if string(tag[0]) == pathSep {
			continue
		}
		markers := strings.Split(tag, " ")
		tag = markers[0]
		valEnd := strings.LastIndex(line, "<")
		if valEnd < indexEnd {
			continue
		}
		translationValue := line[indexEnd+1 : valEnd]
		if (indexStart != -1) && (indexEnd != -1) {
			tags[tag] = translationValue
		}
	}
	return tags, nil
}

func findMissing(original, translation map[string]string) map[string]string {
	missing := make(map[string]string)
	if reflect.DeepEqual(original, translation) {
		return nil
	}
	for k, v := range original {
		if _, ok := translation[k]; !ok {
			missing[k] = v
		}
	}
	return missing
}

// TODO: refactor
// this should return a bool and an error,
// this way the upper func can handle dir creation
func checkTransDirExists(dir, translation string) (err error) {
	splitDir := strings.Split(dir, pathSep)
	dir = filepath.Join(translation, splitDir[len(splitDir)-1])
	_, err = os.Open(dir)
	if err != nil {
		splitedDirectory := strings.Split(dir, pathSep)
		parentDirFromSplit := dir[:len(dir)-len(splitedDirectory[len(splitedDirectory)-1])-1]
		os.Chdir(parentDirFromSplit)
		err = os.Mkdir(splitedDirectory[len(splitedDirectory)-1], 0700)
		if err != nil {
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
)

// AgentCheck represents a check known to the agent
type AgentCheck struct {
	Node        string
	CheckID     string
	Name        string
	Status      string
	Notes       string
	Output      string
	ServiceID   string
	ServiceName string
}

// AgentService represents a service known to the agent
type AgentService struct {
	ID                string
	Service           string
	Tags              []string
	Port              int
	Address           string
	EnableTagOverride bool
}

// AgentMember represents a cluster member known to the agent
type AgentMember struct {
	Name        string
	Addr        string
	Port        uint16
	Tags        map[string]string
	Status      int
	ProtocolMin uint8
	ProtocolMax uint8
	ProtocolCur uint8
	DelegateMin uint8
	DelegateMax uint8
	DelegateCur uint8
}

// AgentServiceRegistration is used to register a new service
type AgentServiceRegistration struct {
	ID                string   `json:",omitempty"`
	Name              string   `json:",omitempty"`
	Tags              []string `json:",omitempty"`
	Port              int      `json:",omitempty"`
	Address           string   `json:",omitempty"`
	EnableTagOverride bool     `json:",omitempty"`
	Check             *AgentServiceCheck
	Checks            AgentServiceChecks
}

// AgentCheckRegistration is used to register a new check
type AgentCheckRegistration struct {
	ID        string `json:",omitempty"`
	Name      string `json:",omitempty"`
	Notes     string `json:",omitempty"`
	ServiceID string `json:",omitempty"`
	AgentServiceCheck
}

// AgentServiceCheck is used to create an associated
// check for a service
type AgentServiceCheck struct {
	Script            string `json:",omitempty"`
	DockerContainerID string `json:",omitempty"`
	Shell             string `json:",omitempty"` // Only supported for Docker.
	Interval          string `json:",omitempty"`
	Timeout           string `json:",omitempty"`
	TTL               string `json:",omitempty"`
	HTTP              string `json:",omitempty"`
	TCP               string `json:",omitempty"`
	Status            string `json:",omitempty"`
}
type AgentServiceChecks []*AgentServiceCheck

// Agent can be used to query the Agent endpoints
type Agent struct {
	c *Client

	// cache the node name
	nodeName string
}

// Agent returns a handle to the agent endpoints
func (c *Client) Agent() *Agent {
	return &Agent{c: c}
}

// Self is used to query the agent we are speaking to for
// information about itself
func (a *Agent) Self() (map[string]map[string]interface{}, error) {
	r := a.c.newRequest("GET", "/v1/agent/self")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]map[string]interface{}
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// NodeName is used to get the node name of the agent
func (a *Agent) NodeName() (string, error) {
	if a.nodeName != "" {
		return a.nodeName, nil
	}
	info, err := a.Self()
	if err != nil {
		return "", err
	}
	name := info["Config"]["NodeName"].(string)
	a.nodeName = name
	return name, nil
}

// Checks returns the locally registered checks
func (a *Agent) Checks() (map[string]*AgentCheck, error) {
	r := a.c.newRequest("GET", "/v1/agent/checks")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Services returns the locally registered services
func (a *Agent) Services() (map[string]*AgentService, error) {
	r := a.c.newRequest("GET", "/v1/agent/services")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentService
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Members returns the known gossip members. The WAN
// flag can be used to query a server for WAN members.
func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
	r := a.c.newRequest("GET", "/v1/agent/members")
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out []*AgentMember
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// ServiceRegister is used to register a new service with
// the local agent
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/register")
	r.obj = service
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ServiceDeregister is used to deregister a service with
// the local agent
func (a *Agent) ServiceDeregister(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// PassTTL is used to set a TTL check to the passing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) PassTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "pass")
}

// WarnTTL is used to set a TTL check to the warning state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) WarnTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "warn")
}

// FailTTL is used to set a TTL check to the failing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) FailTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "fail")
}

// updateTTL is used to update the TTL of a check. This is the internal
// method that uses the old API that's present in Consul versions prior to
// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
// but keep the old Pass/Warn/Fail methods using the old API under the hood.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 and the server endpoints will
// be removed in 0.9.
func (a *Agent) updateTTL(checkID, note, status string) error {
	switch status {
	case "pass":
	case "warn":
	case "fail":
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}
	endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.params.Set("note", note)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// checkUpdate is the payload for a PUT for a check update.
type checkUpdate struct {
	// Status us one of the api.Health* states: HealthPassing
	// ("passing"), HealthWarning ("warning"), or HealthCritical
	// ("critical").
	Status string

	// Output is the information to post to the UI for operators as the
	// output of the process that decided to hit the TTL check. This is
	// different from the note field that's associated with the check
	// itself.
	Output string
}

// UpdateTTL is used to update the TTL of a check. This uses the newer API
// that was introduced in Consul 0.6.4 and later. We translate the old status
// strings for compatibility (though a newer version of Consul will still be
// required to use this API).
func (a *Agent) UpdateTTL(checkID, output, status string) error {
	switch status {
	case "pass", HealthPassing:
		status = HealthPassing
	case "warn", HealthWarning:
		status = HealthWarning
	case "fail", HealthCritical:
		status = HealthCritical
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}

	endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.obj = &checkUpdate{
		Status: status,
		Output: output,
	}

	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckRegister is used to register a new check with
// the local agent
func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/register")
	r.obj = check
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckDeregister is used to deregister a check with
// the local agent
func (a *Agent) CheckDeregister(checkID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// Join is used to instruct the agent to attempt a join to
// another cluster member
func (a *Agent) Join(addr string, wan bool) error {
	r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ForceLeave is used to have the agent eject a failed node
func (a *Agent) ForceLeave(node string) error {
	r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableServiceMaintenance toggles service maintenance mode on
// for the given service ID.
func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableServiceMaintenance toggles service maintenance mode off
// for the given service ID.
func (a *Agent) DisableServiceMaintenance(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableNodeMaintenance toggles node maintenance mode on for the
// agent we are connected to.
func (a *Agent) EnableNodeMaintenance(reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableNodeMaintenance toggles node maintenance mode off for the
// agent we are connected to.
func (a *Agent) DisableNodeMaintenance() error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<commit_msg>Correct a small typo<commit_after>package api

import (
	"fmt"
)

// AgentCheck represents a check known to the agent
type AgentCheck struct {
	Node        string
	CheckID     string
	Name        string
	Status      string
	Notes       string
	Output      string
	ServiceID   string
	ServiceName string
}

// AgentService represents a service known to the agent
type AgentService struct {
	ID                string
	Service           string
	Tags              []string
	Port              int
	Address           string
	EnableTagOverride bool
}

// AgentMember represents a cluster member known to the agent
type AgentMember struct {
	Name        string
	Addr        string
	Port        uint16
	Tags        map[string]string
	Status      int
	ProtocolMin uint8
	ProtocolMax uint8
	ProtocolCur uint8
	DelegateMin uint8
	DelegateMax uint8
	DelegateCur uint8
}

// AgentServiceRegistration is used to register a new service
type AgentServiceRegistration struct {
	ID                string   `json:",omitempty"`
	Name              string   `json:",omitempty"`
	Tags              []string `json:",omitempty"`
	Port              int      `json:",omitempty"`
	Address           string   `json:",omitempty"`
	EnableTagOverride bool     `json:",omitempty"`
	Check             *AgentServiceCheck
	Checks            AgentServiceChecks
}

// AgentCheckRegistration is used to register a new check
type AgentCheckRegistration struct {
	ID        string `json:",omitempty"`
	Name      string `json:",omitempty"`
	Notes     string `json:",omitempty"`
	ServiceID string `json:",omitempty"`
	AgentServiceCheck
}

// AgentServiceCheck is used to create an associated
// check for a service
type AgentServiceCheck struct {
	Script            string `json:",omitempty"`
	DockerContainerID string `json:",omitempty"`
	Shell             string `json:",omitempty"` // Only supported for Docker.
	Interval          string `json:",omitempty"`
	Timeout           string `json:",omitempty"`
	TTL               string `json:",omitempty"`
	HTTP              string `json:",omitempty"`
	TCP               string `json:",omitempty"`
	Status            string `json:",omitempty"`
}
type AgentServiceChecks []*AgentServiceCheck

// Agent can be used to query the Agent endpoints
type Agent struct {
	c *Client

	// cache the node name
	nodeName string
}

// Agent returns a handle to the agent endpoints
func (c *Client) Agent() *Agent {
	return &Agent{c: c}
}

// Self is used to query the agent we are speaking to for
// information about itself
func (a *Agent) Self() (map[string]map[string]interface{}, error) {
	r := a.c.newRequest("GET", "/v1/agent/self")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]map[string]interface{}
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// NodeName is used to get the node name of the agent
func (a *Agent) NodeName() (string, error) {
	if a.nodeName != "" {
		return a.nodeName, nil
	}
	info, err := a.Self()
	if err != nil {
		return "", err
	}
	name := info["Config"]["NodeName"].(string)
	a.nodeName = name
	return name, nil
}

// Checks returns the locally registered checks
func (a *Agent) Checks() (map[string]*AgentCheck, error) {
	r := a.c.newRequest("GET", "/v1/agent/checks")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Services returns the locally registered services
func (a *Agent) Services() (map[string]*AgentService, error) {
	r := a.c.newRequest("GET", "/v1/agent/services")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out map[string]*AgentService
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// Members returns the known gossip members. The WAN
// flag can be used to query a server for WAN members.
func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
	r := a.c.newRequest("GET", "/v1/agent/members")
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var out []*AgentMember
	if err := decodeBody(resp, &out); err != nil {
		return nil, err
	}
	return out, nil
}

// ServiceRegister is used to register a new service with
// the local agent
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/register")
	r.obj = service
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ServiceDeregister is used to deregister a service with
// the local agent
func (a *Agent) ServiceDeregister(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// PassTTL is used to set a TTL check to the passing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) PassTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "pass")
}

// WarnTTL is used to set a TTL check to the warning state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) WarnTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "warn")
}

// FailTTL is used to set a TTL check to the failing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 or changed to use
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
func (a *Agent) FailTTL(checkID, note string) error {
	return a.updateTTL(checkID, note, "fail")
}

// updateTTL is used to update the TTL of a check. This is the internal
// method that uses the old API that's present in Consul versions prior to
// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
// but keep the old Pass/Warn/Fail methods using the old API under the hood.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
// The client interface will be removed in 0.8 and the server endpoints will
// be removed in 0.9.
func (a *Agent) updateTTL(checkID, note, status string) error {
	switch status {
	case "pass":
	case "warn":
	case "fail":
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}
	endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.params.Set("note", note)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// checkUpdate is the payload for a PUT for a check update.
type checkUpdate struct {
	// Status is one of the api.Health* states: HealthPassing
	// ("passing"), HealthWarning ("warning"), or HealthCritical
	// ("critical").
	Status string

	// Output is the information to post to the UI for operators as the
	// output of the process that decided to hit the TTL check. This is
	// different from the note field that's associated with the check
	// itself.
	Output string
}

// UpdateTTL is used to update the TTL of a check. This uses the newer API
// that was introduced in Consul 0.6.4 and later. We translate the old status
// strings for compatibility (though a newer version of Consul will still be
// required to use this API).
func (a *Agent) UpdateTTL(checkID, output, status string) error {
	switch status {
	case "pass", HealthPassing:
		status = HealthPassing
	case "warn", HealthWarning:
		status = HealthWarning
	case "fail", HealthCritical:
		status = HealthCritical
	default:
		return fmt.Errorf("Invalid status: %s", status)
	}

	endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
	r := a.c.newRequest("PUT", endpoint)
	r.obj = &checkUpdate{
		Status: status,
		Output: output,
	}

	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckRegister is used to register a new check with
// the local agent
func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/register")
	r.obj = check
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// CheckDeregister is used to deregister a check with
// the local agent
func (a *Agent) CheckDeregister(checkID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// Join is used to instruct the agent to attempt a join to
// another cluster member
func (a *Agent) Join(addr string, wan bool) error {
	r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
	if wan {
		r.params.Set("wan", "1")
	}
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// ForceLeave is used to have the agent eject a failed node
func (a *Agent) ForceLeave(node string) error {
	r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableServiceMaintenance toggles service maintenance mode on
// for the given service ID.
func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableServiceMaintenance toggles service maintenance mode off
// for the given service ID.
func (a *Agent) DisableServiceMaintenance(serviceID string) error {
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// EnableNodeMaintenance toggles node maintenance mode on for the
// agent we are connected to.
func (a *Agent) EnableNodeMaintenance(reason string) error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "true")
	r.params.Set("reason", reason)
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

// DisableNodeMaintenance toggles node maintenance mode off for the
// agent we are connected to.
func (a *Agent) DisableNodeMaintenance() error {
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
	r.params.Set("enable", "false")
	_, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<|endoftext|>


<commit_before>package api

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

	"code.cloudfoundry.org/bytefmt"

	"golang.org/x/net/websocket"

	"net/url"

	sigar "github.com/cloudfoundry/gosigar"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/labstack/echo"
)

func (a *Api) CreateBox(c echo.Context) error {
	body, err := ioutil.ReadAll(c.Request().Body)
	if err != nil {
		return err
	}

	var req CreateBoxRequest
	err = json.Unmarshal(body, &req)
	if err != nil {
		return err
	}

	// validate captcha if secret is configured
	if a.Config.RCSecret != "" {
		data := url.Values{}
		data.Set("secret", a.Config.RCSecret)
		data.Set("response", req.Captcha)
		data.Set("remoteip", c.RealIP())

		res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", data)
		if err != nil {
			return err
		}

		defer res.Body.Close()

		var verify CaptchaVerifyResponse
		err = json.NewDecoder(res.Body).Decode(&verify)
		if err != nil {
			return err
		}
		if !verify.Success {
			return errors.New("Captcha verification failed")
		}
	} else {
		a.Log.Warn("Creating box without captcha verfication")
	}

	// verify image is whitelisted
	imageAllowed := false
	for _, image := range *a.Images {
		if req.Image == image.Image {
			for _, version := range image.Versions {
				if req.Version == version {
					imageAllowed = true
				}
			}
		}
	}
	if !imageAllowed {
		return errors.New("Image not allowed")
	}

	// make sure we are not running out of memory
	mem := sigar.Mem{}
	mem.Get()
	if mem.ActualFree < bytefmt.GIGABYTE {
		return errors.New("Resource limit reached, try again later")
	}

	container := pod.UserContainer{
		Image: fmt.Sprintf("%s:%s", req.Image, req.Version),
	}

	pod := pod.UserPod{
		Name:       "termbox",
		Containers: []pod.UserContainer{container},
		Resource:   pod.UserResource{Vcpu: 1, Memory: 512},
	}

	podID, statusCode, err := a.Hyper.CreatePod(pod)
	if err != nil {
		if statusCode == http.StatusNotFound {
			err = a.HyperClient.PullImages(&pod)
			if err != nil {
				return err
			}
			podID, statusCode, err = a.Hyper.CreatePod(pod)
		}
		if err != nil {
			return err
		}
	}

	return c.JSON(http.StatusOK, CreateBoxResponse{PodID: podID})
}

type execMessage struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

func (a *Api) ExecBox(c echo.Context) error {

	websocket.Handler(func(ws *websocket.Conn) {
		defer ws.Close()

		podID := c.Param("id")
		podInfo, err := a.Hyper.GetPodInfo(podID)
		if err != nil {
			a.Log.Debug(err)
			websocket.Message.Send(ws, "box does not exist, closing connection")
			return
		}
		if podInfo.Status.Phase != "Running" {
			_, err := a.Hyper.StartPod(podID, "", false, false, nil, nil, nil)
			if err != nil {
				a.Log.Warn(err)
				return
			}
		}
		containerID, _ := a.Hyper.GetContainerByPod(podID)

		command, err := json.Marshal([]string{"bash"})
		if err != nil {
			a.Log.Error(err)
			return
		}

		execID, err := a.Hyper.CreateExec(containerID, command, true)
		if err != nil {
			a.Log.Warn(err)
			return
		}

		if err != nil {
			a.Log.Warn(err)
			return
		}

		dec := json.NewDecoder(ws)

		r, w := io.Pipe()

		go func() {
			err = a.Hyper.StartExec(containerID, execID, true, r, ws, ws)
			if err != nil {
				a.Log.Warn(err)
				return
			}
		}()

		for {
			var message ExecBoxMessage
			err := dec.Decode(&message)
			if err != nil {
				a.Log.Warning(err)
				break
			}

			if message.Width != 0 && message.Height != 0 {
				err = a.Hyper.WinResize(containerID, execID, message.Height, message.Width)
				continue
			}
			if message.Data != "" {
				io.WriteString(w, message.Data)
				if err != nil {
					a.Log.Warn(err)
					break
				}
			}
		}

	}).ServeHTTP(c.Response(), c.Request())
	return nil

}

type ExecReader struct {
	data      []byte
	readIndex int64
}
<commit_msg>refactor error handling<commit_after>package api

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

	"code.cloudfoundry.org/bytefmt"

	"golang.org/x/net/websocket"

	"net/url"

	sigar "github.com/cloudfoundry/gosigar"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/labstack/echo"
)

func (a *Api) CreateBox(c echo.Context) error {
	body, err := ioutil.ReadAll(c.Request().Body)
	if err != nil {
		return err
	}

	var req CreateBoxRequest
	if err = json.Unmarshal(body, &req); err != nil {
		return err
	}

	// validate captcha if secret is configured
	if a.Config.RCSecret != "" {
		data := url.Values{}
		data.Set("secret", a.Config.RCSecret)
		data.Set("response", req.Captcha)
		data.Set("remoteip", c.RealIP())

		res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", data)
		if err != nil {
			return err
		}

		defer res.Body.Close()

		var verify CaptchaVerifyResponse
		if err := json.NewDecoder(res.Body).Decode(&verify); err != nil {
			return err
		}
		if !verify.Success {
			return errors.New("Captcha verification failed")
		}
	} else {
		a.Log.Warn("Creating box without captcha verfication")
	}

	// verify image is whitelisted
	imageAllowed := false
	for _, image := range *a.Images {
		if req.Image == image.Image {
			for _, version := range image.Versions {
				if req.Version == version {
					imageAllowed = true
				}
			}
		}
	}
	if !imageAllowed {
		return errors.New("Image not allowed")
	}

	// make sure we are not running out of memory
	mem := sigar.Mem{}
	mem.Get()
	if mem.ActualFree < bytefmt.GIGABYTE {
		return errors.New("Resource limit reached, try again later")
	}

	container := pod.UserContainer{
		Image: fmt.Sprintf("%s:%s", req.Image, req.Version),
	}

	pod := pod.UserPod{
		Name:       "termbox",
		Containers: []pod.UserContainer{container},
		Resource:   pod.UserResource{Vcpu: 1, Memory: 512},
	}

	podID, statusCode, err := a.Hyper.CreatePod(pod)
	if err != nil {
		if statusCode == http.StatusNotFound {
			if err := a.HyperClient.PullImages(&pod); err != nil {
				return err
			}
			podID, statusCode, err = a.Hyper.CreatePod(pod)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return c.JSON(http.StatusOK, CreateBoxResponse{PodID: podID})
}

type execMessage struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

func (a *Api) ExecBox(c echo.Context) error {

	websocket.Handler(func(ws *websocket.Conn) {
		defer ws.Close()

		podID := c.Param("id")
		podInfo, err := a.Hyper.GetPodInfo(podID)
		if err != nil {
			a.Log.Debug(err)
			websocket.Message.Send(ws, "box does not exist, closing connection")
			return
		}
		if podInfo.Status.Phase != "Running" {
			_, err := a.Hyper.StartPod(podID, "", false, false, nil, nil, nil)
			if err != nil {
				a.Log.Warn(err)
				return
			}
		}
		containerID, _ := a.Hyper.GetContainerByPod(podID)

		command, err := json.Marshal([]string{"bash"})
		if err != nil {
			a.Log.Error(err)
			return
		}

		execID, err := a.Hyper.CreateExec(containerID, command, true)
		if err != nil {
			a.Log.Warn(err)
			return
		}

		dec := json.NewDecoder(ws)

		r, w := io.Pipe()

		go func() {
			if err := a.Hyper.StartExec(containerID, execID, true, r, ws, ws); err != nil {
				a.Log.Warn(err)
				return
			}
		}()

		for {
			var message ExecBoxMessage
			if err := dec.Decode(&message); err != nil {
				a.Log.Warn(err)
				break
			}

			if message.Width != 0 && message.Height != 0 {
				if err := a.Hyper.WinResize(containerID, execID, message.Height, message.Width); err != nil {
					a.Log.Warn(err)
				}
				continue
			}
			if message.Data != "" {
				_, err = io.WriteString(w, message.Data)
				if err != nil {
					a.Log.Warn(err)
					break
				}
			}
		}

	}).ServeHTTP(c.Response(), c.Request())
	return nil

}

type ExecReader struct {
	data      []byte
	readIndex int64
}
<|endoftext|>