<commit_before>package stripe

import (
	"encoding/json"
)

// ChargeFraudUserReport is the list of allowed values for reporting fraud.
type ChargeFraudUserReport string

// List of values that ChargeFraudUserReport can take.
const (
	ChargeFraudUserReportFraudulent ChargeFraudUserReport = "fraudulent"
	ChargeFraudUserReportSafe       ChargeFraudUserReport = "safe"
)

// ChargeFraudStripeReport is the list of allowed values for reporting fraud.
type ChargeFraudStripeReport string

// List of values that ChargeFraudStripeReport can take.
const (
	ChargeFraudStripeReportFraudulent ChargeFraudStripeReport = "fraudulent"
)

// ChargeLevel3LineItemsParams is the set of parameters that represent a line item on level III data.
type ChargeLevel3LineItemsParams struct {
	DiscountAmount     *int64  `form:"discount_amount"`
	ProductCode        *string `form:"product_code"`
	ProductDescription *string `form:"product_description"`
	Quantity           *int64  `form:"quantity"`
	TaxAmount          *int64  `form:"tax_amount"`
	UnitCost           *int64  `form:"unit_cost"`
}

// ChargeLevel3Params is the set of parameters that can be used for the Level III data.
type ChargeLevel3Params struct {
	CustomerReference  *string                        `form:"customer_reference"`
	LineItems          []*ChargeLevel3LineItemsParams `form:"line_items"`
	MerchantReference  *string                        `form:"merchant_reference"`
	ShippingAddressZip *string                        `form:"shipping_address_zip"`
	ShippingFromZip    *string                        `form:"shipping_from_zip"`
	ShippingAmount     *int64                         `form:"shipping_amount"`
}

// ChargeTransferDataParams is the set of parameters allowed for the transfer_data hash.
type ChargeTransferDataParams struct {
	Destination *string `form:"destination"`
}

// ChargeParams is the set of parameters that can be used when creating or updating a charge.
type ChargeParams struct {
	Params              `form:"*"`
	Amount              *int64                    `form:"amount"`
	ApplicationFee      *int64                    `form:"application_fee"`
	Capture             *bool                     `form:"capture"`
	Currency            *string                   `form:"currency"`
	Customer            *string                   `form:"customer"`
	Description         *string                   `form:"description"`
	Destination         *DestinationParams        `form:"destination"`
	ExchangeRate        *float64                  `form:"exchange_rate"`
	FraudDetails        *FraudDetailsParams       `form:"fraud_details"`
	Level3              *ChargeLevel3Params       `form:"level3"`
	OnBehalfOf          *string                   `form:"on_behalf_of"`
	ReceiptEmail        *string                   `form:"receipt_email"`
	Shipping            *ShippingDetailsParams    `form:"shipping"`
	Source              *SourceParams             `form:"*"` // SourceParams has custom encoding so brought to top level with "*"
	StatementDescriptor *string                   `form:"statement_descriptor"`
	TransferData        *ChargeTransferDataParams `form:"transfer_data"`
	TransferGroup       *string                   `form:"transfer_group"`
}

// ShippingDetailsParams is the structure containing shipping information as parameters
type ShippingDetailsParams struct {
	Address        *AddressParams `form:"address"`
	Carrier        *string        `form:"carrier"`
	Name           *string        `form:"name"`
	Phone          *string        `form:"phone"`
	TrackingNumber *string        `form:"tracking_number"`
}

// SetSource adds valid sources to a ChargeParams object,
// returning an error for unsupported sources.
func (p *ChargeParams) SetSource(sp interface{}) error {
	source, err := SourceParamsFor(sp)
	p.Source = source
	return err
}

// DestinationParams describes the parameters available for the destination hash when creating a charge.
type DestinationParams struct {
	Account *string `form:"account"`
	Amount  *int64  `form:"amount"`
}

// FraudDetailsParams provides information on the fraud details for a charge.
type FraudDetailsParams struct {
	UserReport *string `form:"user_report"`
}

// ChargeListParams is the set of parameters that can be used when listing charges.
type ChargeListParams struct {
	ListParams    `form:"*"`
	Created       *int64            `form:"created"`
	CreatedRange  *RangeQueryParams `form:"created"`
	Customer      *string           `form:"customer"`
	TransferGroup *string           `form:"transfer_group"`
}

// CaptureParams is the set of parameters that can be used when capturing a charge.
type CaptureParams struct {
	Params              `form:"*"`
	Amount              *int64   `form:"amount"`
	ApplicationFee      *int64   `form:"application_fee"`
	ExchangeRate        *float64 `form:"exchange_rate"`
	ReceiptEmail        *string  `form:"receipt_email"`
	StatementDescriptor *string  `form:"statement_descriptor"`
}

// ChargeLevel3LineItem represents a line item on level III data.
// This is in private beta and would be empty for most integrations
type ChargeLevel3LineItem struct {
	DiscountAmount     int64  `json:"discount_amount"`
	ProductCode        string `json:"product_code"`
	ProductDescription string `json:"product_description"`
	Quantity           int64  `json:"quantity"`
	TaxAmount          int64  `json:"tax_amount"`
	UnitCost           int64  `json:"unit_cost"`
}

// ChargeLevel3 represents the Level III data.
// This is in private beta and would be empty for most integrations
type ChargeLevel3 struct {
	CustomerReference  string                  `json:"customer_reference"`
	LineItems          []*ChargeLevel3LineItem `json:"line_items"`
	MerchantReference  string                  `json:"merchant_reference"`
	ShippingAddressZip string                  `json:"shipping_address_zip"`
	ShippingFromZip    string                  `json:"shipping_from_zip"`
	ShippingAmount     int64                   `json:"shipping_amount"`
}

// ChargeTransferData represents the information for the transfer_data associated with a charge.
type ChargeTransferData struct {
	Destination *Account `json:"destination"`
}

// Charge is the resource representing a Stripe charge.
// For more details see https://stripe.com/docs/api#charges.
type Charge struct {
	Amount              int64               `json:"amount"`
	AmountRefunded      int64               `json:"amount_refunded"`
	Application         *Application        `json:"application"`
	ApplicationFee      *ApplicationFee     `json:"application_fee"`
	AuthorizationCode   string              `json:"authorization_code"`
	BalanceTransaction  *BalanceTransaction `json:"balance_transaction"`
	Captured            bool                `json:"captured"`
	Created             int64               `json:"created"`
	Currency            Currency            `json:"currency"`
	Customer            *Customer           `json:"customer"`
	Description         string              `json:"description"`
	Destination         *Account            `json:"destination"`
	Dispute             *Dispute            `json:"dispute"`
	FailureCode         string              `json:"failure_code"`
	FailureMessage      string              `json:"failure_message"`
	FraudDetails        *FraudDetails       `json:"fraud_details"`
	ID                  string              `json:"id"`
	Invoice             *Invoice            `json:"invoice"`
	Level3              ChargeLevel3        `json:"level3"`
	Livemode            bool                `json:"livemode"`
	Metadata            map[string]string   `json:"metadata"`
	OnBehalfOf          *Account            `json:"on_behalf_of"`
	Outcome             *ChargeOutcome      `json:"outcome"`
	Paid                bool                `json:"paid"`
	ReceiptEmail        string              `json:"receipt_email"`
	ReceiptNumber       string              `json:"receipt_number"`
	Refunded            bool                `json:"refunded"`
	Refunds             *RefundList         `json:"refunds"`
	Review              *Review             `json:"review"`
	Shipping            *ShippingDetails    `json:"shipping"`
	Source              *PaymentSource      `json:"source"`
	SourceTransfer      *Transfer           `json:"source_transfer"`
	StatementDescriptor string              `json:"statement_descriptor"`
	Status              string              `json:"status"`
	Transfer            *Transfer           `json:"transfer"`
	TransferData        *ChargeTransferData `json:"transfer_data"`
	TransferGroup       string              `json:"transfer_group"`
}

// UnmarshalJSON handles deserialization of a charge.
// This custom unmarshaling is needed because the resulting
// property may be an ID or the full struct if it was expanded.
func (c *Charge) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type charge Charge
	var v charge
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = Charge(v)
	return nil
}

// ChargeList is a list of charges as retrieved from a list endpoint.
type ChargeList struct {
	ListMeta
	Data []*Charge `json:"data"`
}

// FraudDetails is the structure detailing fraud status.
type FraudDetails struct {
	UserReport   ChargeFraudUserReport   `json:"user_report"`
	StripeReport ChargeFraudStripeReport `json:"stripe_report"`
}

// ChargeOutcomeRule tells you the Radar rule that blocked the charge, if any.
type ChargeOutcomeRule struct {
	Action    string `json:"action"`
	ID        string `json:"id"`
	Predicate string `json:"predicate"`
}

// ChargeOutcome is the charge's outcome that details whether a payment
// was accepted and why.
type ChargeOutcome struct {
	NetworkStatus string             `json:"network_status"`
	Reason        string             `json:"reason"`
	RiskLevel     string             `json:"risk_level"`
	RiskScore     int64              `json:"risk_score"`
	Rule          *ChargeOutcomeRule `json:"rule"`
	SellerMessage string             `json:"seller_message"`
	Type          string             `json:"type"`
}

// ShippingDetails is the structure containing shipping information.
type ShippingDetails struct {
	Address        *Address `json:"address"`
	Carrier        string   `json:"carrier"`
	Name           string   `json:"name"`
	Phone          string   `json:"phone"`
	TrackingNumber string   `json:"tracking_number"`
}

var depth = -1

// UnmarshalJSON handles deserialization of a ChargeOutcomeRule.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *ChargeOutcomeRule) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type chargeOutcomeRule ChargeOutcomeRule
	var v chargeOutcomeRule
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = ChargeOutcomeRule(v)
	return nil
}
<commit_msg>Add support for `application_fee_amount` on Charge<commit_after>package stripe

import (
	"encoding/json"
)

// ChargeFraudUserReport is the list of allowed values for reporting fraud.
type ChargeFraudUserReport string

// List of values that ChargeFraudUserReport can take.
const (
	ChargeFraudUserReportFraudulent ChargeFraudUserReport = "fraudulent"
	ChargeFraudUserReportSafe       ChargeFraudUserReport = "safe"
)

// ChargeFraudStripeReport is the list of allowed values for reporting fraud.
type ChargeFraudStripeReport string

// List of values that ChargeFraudStripeReport can take.
const (
	ChargeFraudStripeReportFraudulent ChargeFraudStripeReport = "fraudulent"
)

// ChargeLevel3LineItemsParams is the set of parameters that represent a line item on level III data.
type ChargeLevel3LineItemsParams struct {
	DiscountAmount     *int64  `form:"discount_amount"`
	ProductCode        *string `form:"product_code"`
	ProductDescription *string `form:"product_description"`
	Quantity           *int64  `form:"quantity"`
	TaxAmount          *int64  `form:"tax_amount"`
	UnitCost           *int64  `form:"unit_cost"`
}

// ChargeLevel3Params is the set of parameters that can be used for the Level III data.
type ChargeLevel3Params struct {
	CustomerReference  *string                        `form:"customer_reference"`
	LineItems          []*ChargeLevel3LineItemsParams `form:"line_items"`
	MerchantReference  *string                        `form:"merchant_reference"`
	ShippingAddressZip *string                        `form:"shipping_address_zip"`
	ShippingFromZip    *string                        `form:"shipping_from_zip"`
	ShippingAmount     *int64                         `form:"shipping_amount"`
}

// ChargeTransferDataParams is the set of parameters allowed for the transfer_data hash.
type ChargeTransferDataParams struct {
	Destination *string `form:"destination"`
}

// ChargeParams is the set of parameters that can be used when creating or updating a charge.
type ChargeParams struct {
	Params               `form:"*"`
	Amount               *int64                    `form:"amount"`
	ApplicationFeeAmount *int64                    `form:"application_fee_amount"`
	Capture              *bool                     `form:"capture"`
	Currency             *string                   `form:"currency"`
	Customer             *string                   `form:"customer"`
	Description          *string                   `form:"description"`
	Destination          *DestinationParams        `form:"destination"`
	ExchangeRate         *float64                  `form:"exchange_rate"`
	FraudDetails         *FraudDetailsParams       `form:"fraud_details"`
	Level3               *ChargeLevel3Params       `form:"level3"`
	OnBehalfOf           *string                   `form:"on_behalf_of"`
	ReceiptEmail         *string                   `form:"receipt_email"`
	Shipping             *ShippingDetailsParams    `form:"shipping"`
	Source               *SourceParams             `form:"*"` // SourceParams has custom encoding so brought to top level with "*"
	StatementDescriptor  *string                   `form:"statement_descriptor"`
	TransferData         *ChargeTransferDataParams `form:"transfer_data"`
	TransferGroup        *string                   `form:"transfer_group"`

	// This property is considered deprecated. Prefer using ApplicationFeeAmount
	ApplicationFee *int64 `form:"application_fee"`
}

// ShippingDetailsParams is the structure containing shipping information as parameters
type ShippingDetailsParams struct {
	Address        *AddressParams `form:"address"`
	Carrier        *string        `form:"carrier"`
	Name           *string        `form:"name"`
	Phone          *string        `form:"phone"`
	TrackingNumber *string        `form:"tracking_number"`
}

// SetSource adds valid sources to a ChargeParams object,
// returning an error for unsupported sources.
func (p *ChargeParams) SetSource(sp interface{}) error {
	source, err := SourceParamsFor(sp)
	p.Source = source
	return err
}

// DestinationParams describes the parameters available for the destination hash when creating a charge.
type DestinationParams struct {
	Account *string `form:"account"`
	Amount  *int64  `form:"amount"`
}

// FraudDetailsParams provides information on the fraud details for a charge.
type FraudDetailsParams struct {
	UserReport *string `form:"user_report"`
}

// ChargeListParams is the set of parameters that can be used when listing charges.
type ChargeListParams struct {
	ListParams    `form:"*"`
	Created       *int64            `form:"created"`
	CreatedRange  *RangeQueryParams `form:"created"`
	Customer      *string           `form:"customer"`
	TransferGroup *string           `form:"transfer_group"`
}

// CaptureParams is the set of parameters that can be used when capturing a charge.
type CaptureParams struct {
	Params               `form:"*"`
	Amount               *int64   `form:"amount"`
	ApplicationFeeAmount *int64   `form:"application_fee_amount"`
	ExchangeRate         *float64 `form:"exchange_rate"`
	ReceiptEmail         *string  `form:"receipt_email"`
	StatementDescriptor  *string  `form:"statement_descriptor"`

	// This property is considered deprecated. Prefer using ApplicationFeeAmount
	ApplicationFee *int64 `form:"application_fee"`
}

// ChargeLevel3LineItem represents a line item on level III data.
// This is in private beta and would be empty for most integrations
type ChargeLevel3LineItem struct {
	DiscountAmount     int64  `json:"discount_amount"`
	ProductCode        string `json:"product_code"`
	ProductDescription string `json:"product_description"`
	Quantity           int64  `json:"quantity"`
	TaxAmount          int64  `json:"tax_amount"`
	UnitCost           int64  `json:"unit_cost"`
}

// ChargeLevel3 represents the Level III data.
// This is in private beta and would be empty for most integrations
type ChargeLevel3 struct {
	CustomerReference  string                  `json:"customer_reference"`
	LineItems          []*ChargeLevel3LineItem `json:"line_items"`
	MerchantReference  string                  `json:"merchant_reference"`
	ShippingAddressZip string                  `json:"shipping_address_zip"`
	ShippingFromZip    string                  `json:"shipping_from_zip"`
	ShippingAmount     int64                   `json:"shipping_amount"`
}

// ChargeTransferData represents the information for the transfer_data associated with a charge.
type ChargeTransferData struct {
	Destination *Account `json:"destination"`
}

// Charge is the resource representing a Stripe charge.
// For more details see https://stripe.com/docs/api#charges.
type Charge struct {
	Amount               int64               `json:"amount"`
	AmountRefunded       int64               `json:"amount_refunded"`
	Application          *Application        `json:"application"`
	ApplicationFee       *ApplicationFee     `json:"application_fee"`
	ApplicationFeeAmount int64               `json:"application_fee_amount"`
	AuthorizationCode    string              `json:"authorization_code"`
	BalanceTransaction   *BalanceTransaction `json:"balance_transaction"`
	Captured             bool                `json:"captured"`
	Created              int64               `json:"created"`
	Currency             Currency            `json:"currency"`
	Customer             *Customer           `json:"customer"`
	Description          string              `json:"description"`
	Destination          *Account            `json:"destination"`
	Dispute              *Dispute            `json:"dispute"`
	FailureCode          string              `json:"failure_code"`
	FailureMessage       string              `json:"failure_message"`
	FraudDetails         *FraudDetails       `json:"fraud_details"`
	ID                   string              `json:"id"`
	Invoice              *Invoice            `json:"invoice"`
	Level3               ChargeLevel3        `json:"level3"`
	Livemode             bool                `json:"livemode"`
	Metadata             map[string]string   `json:"metadata"`
	OnBehalfOf           *Account            `json:"on_behalf_of"`
	Outcome              *ChargeOutcome      `json:"outcome"`
	Paid                 bool                `json:"paid"`
	ReceiptEmail         string              `json:"receipt_email"`
	ReceiptNumber        string              `json:"receipt_number"`
	Refunded             bool                `json:"refunded"`
	Refunds              *RefundList         `json:"refunds"`
	Review               *Review             `json:"review"`
	Shipping             *ShippingDetails    `json:"shipping"`
	Source               *PaymentSource      `json:"source"`
	SourceTransfer       *Transfer           `json:"source_transfer"`
	StatementDescriptor  string              `json:"statement_descriptor"`
	Status               string              `json:"status"`
	Transfer             *Transfer           `json:"transfer"`
	TransferData         *ChargeTransferData `json:"transfer_data"`
	TransferGroup        string              `json:"transfer_group"`
}

// UnmarshalJSON handles deserialization of a charge.
// This custom unmarshaling is needed because the resulting
// property may be an ID or the full struct if it was expanded.
func (c *Charge) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type charge Charge
	var v charge
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = Charge(v)
	return nil
}

// ChargeList is a list of charges as retrieved from a list endpoint.
type ChargeList struct {
	ListMeta
	Data []*Charge `json:"data"`
}

// FraudDetails is the structure detailing fraud status.
type FraudDetails struct {
	UserReport   ChargeFraudUserReport   `json:"user_report"`
	StripeReport ChargeFraudStripeReport `json:"stripe_report"`
}

// ChargeOutcomeRule tells you the Radar rule that blocked the charge, if any.
type ChargeOutcomeRule struct {
	Action    string `json:"action"`
	ID        string `json:"id"`
	Predicate string `json:"predicate"`
}

// ChargeOutcome is the charge's outcome that details whether a payment
// was accepted and why.
type ChargeOutcome struct {
	NetworkStatus string             `json:"network_status"`
	Reason        string             `json:"reason"`
	RiskLevel     string             `json:"risk_level"`
	RiskScore     int64              `json:"risk_score"`
	Rule          *ChargeOutcomeRule `json:"rule"`
	SellerMessage string             `json:"seller_message"`
	Type          string             `json:"type"`
}

// ShippingDetails is the structure containing shipping information.
type ShippingDetails struct {
	Address        *Address `json:"address"`
	Carrier        string   `json:"carrier"`
	Name           string   `json:"name"`
	Phone          string   `json:"phone"`
	TrackingNumber string   `json:"tracking_number"`
}

var depth = -1

// UnmarshalJSON handles deserialization of a ChargeOutcomeRule.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *ChargeOutcomeRule) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type chargeOutcomeRule ChargeOutcomeRule
	var v chargeOutcomeRule
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = ChargeOutcomeRule(v)
	return nil
}
<|endoftext|>


<commit_before>package divaextractor

import (
	"encoding/json"
	"io"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	gq "github.com/PuerkitoBio/goquery"
	"github.com/ikeikeikeike/gopkg/convert"
	behavior "github.com/ikeikeikeike/gopkg/net/http"
	"github.com/ikeikeikeike/gopkg/str"
)

const EndPoint = "http://ja.wikipedia.org/w/api.php"

func tee(r io.Reader, debug bool) io.Reader {
	if !debug {
		return r
	}
	return io.TeeReader(r, os.Stdout)
}

type Wikipedia struct {
	*behavior.UserBehavior
	doc *gq.Document

	Unit  string
	Debug bool
}

func NewWikipedia() *Wikipedia {
	return &Wikipedia{
		UserBehavior: behavior.NewUserBehavior(),
		Unit:         "cm",
		Debug:        false,
	}
}

func (w *Wikipedia) Doc(page string) (*gq.Document, error) {
	api := EndPoint + "?action=parse&format=json&prop=text&uselang=ja&page="

	resp, err := w.Behave(api + url.QueryEscape(page))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var r struct {
		Parse struct {
			Title string
			Text  struct {
				Source string `json:"*"`
			}
		}
	}
	err = json.NewDecoder(tee(resp.Body, w.Debug)).Decode(&r)
	if err != nil {
		return nil, err
	}

	reader := strings.NewReader(r.Parse.Text.Source)
	return gq.NewDocumentFromReader(reader)
}

func (w *Wikipedia) Do(page string) error {
	doc, err := w.Doc(page)

	if err != nil {
		return err
	}

	w.doc = doc
	return nil
}

func (w *Wikipedia) Birthday() (r time.Time) {
	w.doc.Find(`table th:contains(生年月日)`).Each(func(i int, s *gq.Selection) {
		text := str.Clean(s.Next().Text())
		r, _ = time.Parse("2006年1月2日", text)
	})
	return
}

func (w *Wikipedia) Blood() (r string) {
	w.doc.Find(`table th:contains(血液型)`).Each(func(i int, s *gq.Selection) {
		r = str.Clean(strings.Replace(s.Next().Text(), "型", "", -1))
	})
	return
}

func (w *Wikipedia) HW() (r string) {
	w.doc.Find(`table th:contains(身長), table th:contains(体重)`).Each(func(i int, s *gq.Selection) {
		text := s.Next().Text()
		if strings.Contains(text, w.Unit) {
			r = str.Clean(strings.Replace(str.Clean(strings.Replace(text, w.Unit, "", -1)), "kg", "", -1))
		}
	})
	return
}

func (w *Wikipedia) Height() (r int) {
	hw := strings.Split(w.HW(), "/")
	if len(hw) > 0 {
		r, _ = convert.StrTo(str.Clean(strings.Split(w.HW(), "/")[0])).Int()
	}
	return
}

func (w *Wikipedia) Weight() (r int) {
	hw := strings.Split(w.HW(), "/")
	if len(hw) > 1 {
		r, _ = convert.StrTo(str.Clean(hw[1])).Int()
	}
	return
}

func (w *Wikipedia) BWH() (r string) {
	w.doc.Find(`table th:contains(スリーサイズ)`).Each(func(i int, s *gq.Selection) {
		text := s.Next().Text()
		if strings.Contains(text, w.Unit) {
			r = str.Clean(strings.Replace(text, w.Unit, "", -1))
		}
	})
	return
}

func (w *Wikipedia) Bust() (r int) {
	bhw := strings.Split(w.BWH(), "-")
	if len(bhw) > 0 {
		r, _ = convert.StrTo(str.Clean(bhw[0])).Int()
	}
	return
}

func (w *Wikipedia) Waist() (r int) {
	bhw := strings.Split(w.BWH(), "-")
	if len(bhw) > 1 {
		r, _ = convert.StrTo(str.Clean(strings.Split(w.BWH(), "-")[1])).Int()
	}
	return
}

func (w *Wikipedia) Hip() (r int) {
	bhw := strings.Split(w.BWH(), "-")
	if len(bhw) > 2 {
		r, _ = convert.StrTo(str.Clean(strings.Split(w.BWH(), "-")[2])).Int()
	}
	return
}

func (w *Wikipedia) Bracup() (r string) {
	var re = regexp.MustCompile("(?:[a-z]|[A-Z]){1}")
	w.doc.Find(`table th:contains(ブラのサイズ)`).Each(func(i int, s *gq.Selection) {
		text := s.Next().Text()
		if re.MatchString(text) {
			r = str.Clean(strings.ToUpper(string([]rune(text)[0])))
		}
	})
	return
}
<commit_msg>Added calclate cup size<commit_after>package divaextractor

import (
	"encoding/json"
	"io"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	gq "github.com/PuerkitoBio/goquery"
	"github.com/ikeikeikeike/go-bracmeister"
	"github.com/ikeikeikeike/gopkg/convert"
	behavior "github.com/ikeikeikeike/gopkg/net/http"
	"github.com/ikeikeikeike/gopkg/str"
)

const EndPoint = "http://ja.wikipedia.org/w/api.php"

func tee(r io.Reader, debug bool) io.Reader {
	if !debug {
		return r
	}
	return io.TeeReader(r, os.Stdout)
}

type Wikipedia struct {
	*behavior.UserBehavior
	doc *gq.Document

	Unit  string
	Debug bool
}

func NewWikipedia() *Wikipedia {
	return &Wikipedia{
		UserBehavior: behavior.NewUserBehavior(),
		Unit:         "cm",
		Debug:        false,
	}
}

func (w *Wikipedia) Doc(page string) (*gq.Document, error) {
	api := EndPoint + "?action=parse&format=json&prop=text&uselang=ja&page="

	resp, err := w.Behave(api + url.QueryEscape(page))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var r struct {
		Parse struct {
			Title string
			Text  struct {
				Source string `json:"*"`
			}
		}
	}
	err = json.NewDecoder(tee(resp.Body, w.Debug)).Decode(&r)
	if err != nil {
		return nil, err
	}

	reader := strings.NewReader(r.Parse.Text.Source)
	return gq.NewDocumentFromReader(reader)
}

func (w *Wikipedia) Do(page string) error {
	doc, err := w.Doc(page)

	if err != nil {
		return err
	}

	w.doc = doc
	return nil
}

func (w *Wikipedia) Birthday() (r time.Time) {
	w.doc.Find(`table th:contains(生年月日)`).Each(func(i int, s *gq.Selection) {
		text := str.Clean(s.Next().Text())
		r, _ = time.Parse("2006年1月2日", text)
	})
	return
}

func (w *Wikipedia) Blood() (r string) {
	w.doc.Find(`table th:contains(血液型)`).Each(func(i int, s *gq.Selection) {
		r = str.Clean(strings.Replace(s.Next().Text(), "型", "", -1))
	})
	return
}

func (w *Wikipedia) HW() (r string) {
	w.doc.Find(`table th:contains(身長), table th:contains(体重)`).Each(func(i int, s *gq.Selection) {
		text := s.Next().Text()
		if strings.Contains(text, w.Unit) {
			r = str.Clean(strings.Replace(str.Clean(strings.Replace(text, w.Unit, "", -1)), "kg", "", -1))
		}
	})
	return
}

func (w *Wikipedia) Height() (r int) {
	hw := strings.Split(w.HW(), "/")
	if len(hw) > 0 {
		r, _ = convert.StrTo(str.Clean(strings.Split(w.HW(), "/")[0])).Int()
	}
	return
}

func (w *Wikipedia) Weight() (r int) {
	hw := strings.Split(w.HW(), "/")
	if len(hw) > 1 {
		r, _ = convert.StrTo(str.Clean(hw[1])).Int()
	}
	return
}

func (w *Wikipedia) BWH() (r string) {
	w.doc.Find(`table th:contains(スリーサイズ)`).Each(func(i int, s *gq.Selection) {
		text := s.Next().Text()
		if strings.Contains(text, w.Unit) {
			r = str.Clean(strings.Replace(text, w.Unit, "", -1))
		}
	})
	return
}

func (w *Wikipedia) Bust() (r int) {
	bhw := strings.Split(w.BWH(), "-")
	if len(bhw) > 0 {
		r, _ = convert.StrTo(str.Clean(bhw[0])).Int()
	}
	return
}

func (w *Wikipedia) Waist() (r int) {
	bhw := strings.Split(w.BWH(), "-")
	if len(bhw) > 1 {
		r, _ = convert.StrTo(str.Clean(strings.Split(w.BWH(), "-")[1])).Int()
	}
	return
}

func (w *Wikipedia) Hip() (r int) {
	bhw := strings.Split(w.BWH(), "-")
	if len(bhw) > 2 {
		r, _ = convert.StrTo(str.Clean(strings.Split(w.BWH(), "-")[2])).Int()
	}
	return
}

func (w *Wikipedia) Bracup() (r string) {
	var re = regexp.MustCompile("(?:[a-z]|[A-Z]){1}")
	w.doc.Find(`table th:contains(ブラのサイズ)`).Each(func(i int, s *gq.Selection) {
		text := s.Next().Text()
		if re.MatchString(text) {
			r = str.Clean(strings.ToUpper(string([]rune(text)[0])))
		}
		if r == "" {
			h, b, w := w.Height(), w.Bust(), w.Waist()
			if h > 10 && b > 10 && w > 10 {
				r = bracmeister.Calc(h, b, w, true).Cup
				r = strings.ToUpper(str.Clean(r))
			}
		}
	})
	return
}
<|endoftext|>


<commit_before>/*
Package arrow provides C-style date formating and parsing, along with other date goodies.

See the github project page at http://github.com/bmuller/arrow for more info.
*/
package arrow

import (
	"strconv"
	"strings"
	"time"
)

type Arrow struct {
	time.Time
}

// Like time's constants, but with Day and Week
const (
	Nanosecond  time.Duration = 1
	Microsecond               = 1000 * Nanosecond
	Millisecond               = 1000 * Microsecond
	Second                    = 1000 * Millisecond
	Minute                    = 60 * Second
	Hour                      = 60 * Minute
	Day                       = 24 * Hour
	Week                      = 7 * Day
)

func New(t time.Time) Arrow {
	return Arrow{t}
}

func UTC() Arrow {
	return New(time.Now().UTC())
}

func Unix(sec int64, nsec int64) Arrow {
	return New(time.Unix(sec, nsec))
}

func Now() Arrow {
	return New(time.Now())
}

func Yesterday() Arrow {
	return Now().Yesterday()
}

func Tomorrow() Arrow {
	return Now().Tomorrow()
}

func NextSecond() Arrow {
	return Now().AddSeconds(1).AtBeginningOfSecond()
}

func NextMinute() Arrow {
	return Now().AddMinutes(1).AtBeginningOfMinute()
}

func NextHour() Arrow {
	return Now().AddHours(1).AtBeginningOfHour()
}

func NextDay() Arrow {
	return Now().AddDays(1).AtBeginningOfDay()
}

func SleepUntil(t Arrow) {
	time.Sleep(t.Sub(Now()))
}

// Get the current time in the given timezone.
// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func InTimezone(timezone string) Arrow {
	return Now().InTimezone(timezone)
}

func (a Arrow) Before(b Arrow) bool {
	return a.Time.Before(b.Time)
}

func (a Arrow) After(b Arrow) bool {
	return a.Time.After(b.Time)
}

func (a Arrow) Equal(b Arrow) bool {
	return a.Time.Equal(b.Time)
}

// Return an array of Arrow's from this one up to the given one,
// by duration.  For instance, Now().UpTo(Tomorrow(), Hour)
// will return an array of Arrow's from now until tomorrow by
// hour (inclusive of a and b).
func (a Arrow) UpTo(b Arrow, by time.Duration) []Arrow {
	var result []Arrow
	if a.After(b) {
		a, b = b, a
	}
	for a.Before(b) || a.Equal(b) {
		result = append(result, a)
		a = a.Add(by)
	}
	return result
}

func (a Arrow) Yesterday() Arrow {
	return a.AddDays(-1)
}

func (a Arrow) Tomorrow() Arrow {
	return a.AddDays(1)
}

func (a Arrow) UTC() Arrow {
	return New(a.Time.UTC())
}

func (a Arrow) Sub(b Arrow) time.Duration {
	return a.Time.Sub(b.Time)
}

// Add any duration parseable by time.ParseDuration
func (a Arrow) AddDuration(duration string) Arrow {
	if pduration, err := time.ParseDuration(duration); err == nil {
		return a.Add(pduration)
	}
	return a
}

func (a Arrow) Add(d time.Duration) Arrow {
	return New(a.Time.Add(d))
}

// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func (a Arrow) InTimezone(timezone string) Arrow {
	if location, err := time.LoadLocation(timezone); err == nil {
		return New(a.In(location))
	}
	return a
}

func (a Arrow) AddDays(days int) Arrow {
	return New(a.AddDate(0, 0, days))
}

func (a Arrow) AddHours(hours int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour+hours, min, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddMinutes(minutes int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min+minutes, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddSeconds(seconds int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min, sec+seconds, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AtBeginningOfSecond() Arrow {
	return New(a.Truncate(Second))
}

func (a Arrow) AtBeginningOfMinute() Arrow {
	return New(a.Truncate(Minute))
}

func (a Arrow) AtBeginningOfHour() Arrow {
	return New(a.Truncate(Hour))
}

func (a Arrow) AtBeginningOfDay() Arrow {
	d := time.Duration(-a.Hour()) * Hour
	return a.AtBeginningOfHour().Add(d)
}

func (a Arrow) AtBeginningOfWeek() Arrow {
	days := time.Duration(-1*int(a.Weekday())) * Day
	return a.AtBeginningOfDay().Add(days)
}

func (a Arrow) AtBeginningOfMonth() Arrow {
	days := time.Duration(-1*int(a.Day())+1) * Day
	return a.AtBeginningOfDay().Add(days)
}

func (a Arrow) AtBeginningOfYear() Arrow {
	days := time.Duration(-1*int(a.YearDay())+1) * Day
	return a.AtBeginningOfDay().Add(days)
}

// Add any durations parseable by time.ParseDuration
func (a Arrow) AddDurations(durations ...string) Arrow {
	for _, duration := range durations {
		a = a.AddDuration(duration)
	}
	return a
}

func formatConvert(format string) string {
	// create mapping from strftime to time in Go
	strftimeMapping := map[string]string{
		"%a": "Mon",
		"%A": "Monday",
		"%b": "Jan",
		"%B": "January",
		"%c": "", // locale not supported
		"%C": "06",
		"%d": "02",
		"%D": "01/02/06",
		"%e": "_2",
		"%E": "", // modifiers not supported
		"%F": "2006-01-02",
		"%G": "%G", // special case, see below
		"%g": "%g", // special case, see below
		"%h": "Jan",
		"%H": "15",
		"%I": "03",
		"%j": "%j", // special case, see below
		"%k": "%k", // special case, see below
		"%l": "_3",
		"%m": "01",
		"%M": "04",
		"%n": "\n",
		"%O": "", // modifiers not supported
		"%p": "PM",
		"%P": "pm",
		"%r": "03:04:05 PM",
		"%R": "15:04",
		"%s": "%s", // special case, see below
		"%S": "05",
		"%t": "\t",
		"%T": "15:04:05",
		"%u": "%u", // special case, see below
		"%U": "%U", // special case, see below
		"%V": "%V", // special case, see below
		"%w": "%w", // special case, see below
		"%W": "%W", // special case, see below
		"%x": "%x", // locale not supported
		"%X": "%X", // locale not supported
		"%y": "06",
		"%Y": "2006",
		"%z": "-0700",
		"%Z": "MST",
		"%+": "Mon Jan _2 15:04:05 MST 2006",
		"%%": "%%", // special case, see below
	}

	for fmt, conv := range strftimeMapping {
		format = strings.Replace(format, fmt, conv, -1)
	}

	return format
}

// Parse the time using the same format string types as strftime
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParse(layout, value string) (Arrow, error) {
	t, e := time.Parse(formatConvert(layout), value)
	return New(t), e
}

// Parse the time using the same format string types as strftime,
// within the given location.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParseInLocation(layout, value string, loc *time.Location) (Arrow, error) {
	t, e := time.ParseInLocation(formatConvert(layout), value, loc)
	return New(t), e
}

// Parse the time using the same format string types as strftime,
// within the given location (string value for timezone).
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParseInStringLocation(layout, value, timezone string) (Arrow, error) {
	if location, err := time.LoadLocation(timezone); err == nil {
		return CParseInLocation(layout, value, location)
	} else {
		return New(time.Time{}), err
	}
}

// Format the time using the same format string types as strftime.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func (a Arrow) CFormat(format string) string {
	format = a.Format(formatConvert(format))

	year, week := a.ISOWeek()
	yearday := a.YearDay()
	weekday := a.Weekday()
	syear := strconv.Itoa(year)
	sweek := strconv.Itoa(week)
	syearday := strconv.Itoa(yearday)
	sweekday := strconv.Itoa(int(weekday))

	if a.Year() > 999 {
		format = strings.Replace(format, "%G", syear, -1)
		format = strings.Replace(format, "%g", syear[2:4], -1)
	}

	format = strings.Replace(format, "%j", syearday, -1)
	if a.Hour() < 10 {
		shour := " " + strconv.Itoa(a.Hour())
		format = strings.Replace(format, "%k", shour, -1)
	}
	format = strings.Replace(format, "%s", strconv.FormatInt(a.Unix(), 10), -1)

	if weekday == 0 {
		format = strings.Replace(format, "%u", "7", -1)
	} else {
		format = strings.Replace(format, "%u", sweekday, -1)
	}

	format = strings.Replace(format, "%U", weekNumber(a, time.Sunday), -1)
	format = strings.Replace(format, "%U", sweek, -1)
	format = strings.Replace(format, "%w", sweekday, -1)
	format = strings.Replace(format, "%W", weekNumber(a, time.Monday), -1)
	return strings.Replace(format, "%%", "%", -1)
}

// Used for %U and %W:
// %U: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Sunday as the first day of week 01.
//
// %W: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Monday as the first day of week 01.
func weekNumber(a Arrow, firstday time.Weekday) string {
	dayone := a.AtBeginningOfYear()
	for dayone.Weekday() != time.Sunday {
		dayone = dayone.AddDays(1)
	}
	week := int(a.Sub(dayone.AddDays(-7)) / Week)
	return strconv.Itoa(week)
}
<commit_msg>added Epoch function<commit_after>/*
Package arrow provides C-style date formating and parsing, along with other date goodies.

See the github project page at http://github.com/bmuller/arrow for more info.
*/
package arrow

import (
	"strconv"
	"strings"
	"time"
)

type Arrow struct {
	time.Time
}

// Like time's constants, but with Day and Week
const (
	Nanosecond  time.Duration = 1
	Microsecond               = 1000 * Nanosecond
	Millisecond               = 1000 * Microsecond
	Second                    = 1000 * Millisecond
	Minute                    = 60 * Second
	Hour                      = 60 * Minute
	Day                       = 24 * Hour
	Week                      = 7 * Day
)

func New(t time.Time) Arrow {
	return Arrow{t}
}

func UTC() Arrow {
	return New(time.Now().UTC())
}

func Unix(sec int64, nsec int64) Arrow {
	return New(time.Unix(sec, nsec))
}

func Epoch() Arrow {
	return Unix(0, 0)
}

func Now() Arrow {
	return New(time.Now())
}

func Yesterday() Arrow {
	return Now().Yesterday()
}

func Tomorrow() Arrow {
	return Now().Tomorrow()
}

func NextSecond() Arrow {
	return Now().AddSeconds(1).AtBeginningOfSecond()
}

func NextMinute() Arrow {
	return Now().AddMinutes(1).AtBeginningOfMinute()
}

func NextHour() Arrow {
	return Now().AddHours(1).AtBeginningOfHour()
}

func NextDay() Arrow {
	return Now().AddDays(1).AtBeginningOfDay()
}

func SleepUntil(t Arrow) {
	time.Sleep(t.Sub(Now()))
}

// Get the current time in the given timezone.
// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func InTimezone(timezone string) Arrow {
	return Now().InTimezone(timezone)
}

func (a Arrow) Before(b Arrow) bool {
	return a.Time.Before(b.Time)
}

func (a Arrow) After(b Arrow) bool {
	return a.Time.After(b.Time)
}

func (a Arrow) Equal(b Arrow) bool {
	return a.Time.Equal(b.Time)
}

// Return an array of Arrow's from this one up to the given one,
// by duration.  For instance, Now().UpTo(Tomorrow(), Hour)
// will return an array of Arrow's from now until tomorrow by
// hour (inclusive of a and b).
func (a Arrow) UpTo(b Arrow, by time.Duration) []Arrow {
	var result []Arrow
	if a.After(b) {
		a, b = b, a
	}
	for a.Before(b) || a.Equal(b) {
		result = append(result, a)
		a = a.Add(by)
	}
	return result
}

func (a Arrow) Yesterday() Arrow {
	return a.AddDays(-1)
}

func (a Arrow) Tomorrow() Arrow {
	return a.AddDays(1)
}

func (a Arrow) UTC() Arrow {
	return New(a.Time.UTC())
}

func (a Arrow) Sub(b Arrow) time.Duration {
	return a.Time.Sub(b.Time)
}

// Add any duration parseable by time.ParseDuration
func (a Arrow) AddDuration(duration string) Arrow {
	if pduration, err := time.ParseDuration(duration); err == nil {
		return a.Add(pduration)
	}
	return a
}

func (a Arrow) Add(d time.Duration) Arrow {
	return New(a.Time.Add(d))
}

// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func (a Arrow) InTimezone(timezone string) Arrow {
	if location, err := time.LoadLocation(timezone); err == nil {
		return New(a.In(location))
	}
	return a
}

func (a Arrow) AddDays(days int) Arrow {
	return New(a.AddDate(0, 0, days))
}

func (a Arrow) AddHours(hours int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour+hours, min, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddMinutes(minutes int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min+minutes, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddSeconds(seconds int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min, sec+seconds, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AtBeginningOfSecond() Arrow {
	return New(a.Truncate(Second))
}

func (a Arrow) AtBeginningOfMinute() Arrow {
	return New(a.Truncate(Minute))
}

func (a Arrow) AtBeginningOfHour() Arrow {
	return New(a.Truncate(Hour))
}

func (a Arrow) AtBeginningOfDay() Arrow {
	d := time.Duration(-a.Hour()) * Hour
	return a.AtBeginningOfHour().Add(d)
}

func (a Arrow) AtBeginningOfWeek() Arrow {
	days := time.Duration(-1*int(a.Weekday())) * Day
	return a.AtBeginningOfDay().Add(days)
}

func (a Arrow) AtBeginningOfMonth() Arrow {
	days := time.Duration(-1*int(a.Day())+1) * Day
	return a.AtBeginningOfDay().Add(days)
}

func (a Arrow) AtBeginningOfYear() Arrow {
	days := time.Duration(-1*int(a.YearDay())+1) * Day
	return a.AtBeginningOfDay().Add(days)
}

// Add any durations parseable by time.ParseDuration
func (a Arrow) AddDurations(durations ...string) Arrow {
	for _, duration := range durations {
		a = a.AddDuration(duration)
	}
	return a
}

func formatConvert(format string) string {
	// create mapping from strftime to time in Go
	strftimeMapping := map[string]string{
		"%a": "Mon",
		"%A": "Monday",
		"%b": "Jan",
		"%B": "January",
		"%c": "", // locale not supported
		"%C": "06",
		"%d": "02",
		"%D": "01/02/06",
		"%e": "_2",
		"%E": "", // modifiers not supported
		"%F": "2006-01-02",
		"%G": "%G", // special case, see below
		"%g": "%g", // special case, see below
		"%h": "Jan",
		"%H": "15",
		"%I": "03",
		"%j": "%j", // special case, see below
		"%k": "%k", // special case, see below
		"%l": "_3",
		"%m": "01",
		"%M": "04",
		"%n": "\n",
		"%O": "", // modifiers not supported
		"%p": "PM",
		"%P": "pm",
		"%r": "03:04:05 PM",
		"%R": "15:04",
		"%s": "%s", // special case, see below
		"%S": "05",
		"%t": "\t",
		"%T": "15:04:05",
		"%u": "%u", // special case, see below
		"%U": "%U", // special case, see below
		"%V": "%V", // special case, see below
		"%w": "%w", // special case, see below
		"%W": "%W", // special case, see below
		"%x": "%x", // locale not supported
		"%X": "%X", // locale not supported
		"%y": "06",
		"%Y": "2006",
		"%z": "-0700",
		"%Z": "MST",
		"%+": "Mon Jan _2 15:04:05 MST 2006",
		"%%": "%%", // special case, see below
	}

	for fmt, conv := range strftimeMapping {
		format = strings.Replace(format, fmt, conv, -1)
	}

	return format
}

// Parse the time using the same format string types as strftime
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParse(layout, value string) (Arrow, error) {
	t, e := time.Parse(formatConvert(layout), value)
	return New(t), e
}

// Parse the time using the same format string types as strftime,
// within the given location.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParseInLocation(layout, value string, loc *time.Location) (Arrow, error) {
	t, e := time.ParseInLocation(formatConvert(layout), value, loc)
	return New(t), e
}

// Parse the time using the same format string types as strftime,
// within the given location (string value for timezone).
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParseInStringLocation(layout, value, timezone string) (Arrow, error) {
	if location, err := time.LoadLocation(timezone); err == nil {
		return CParseInLocation(layout, value, location)
	} else {
		return New(time.Time{}), err
	}
}

// Format the time using the same format string types as strftime.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func (a Arrow) CFormat(format string) string {
	format = a.Format(formatConvert(format))

	year, week := a.ISOWeek()
	yearday := a.YearDay()
	weekday := a.Weekday()
	syear := strconv.Itoa(year)
	sweek := strconv.Itoa(week)
	syearday := strconv.Itoa(yearday)
	sweekday := strconv.Itoa(int(weekday))

	if a.Year() > 999 {
		format = strings.Replace(format, "%G", syear, -1)
		format = strings.Replace(format, "%g", syear[2:4], -1)
	}

	format = strings.Replace(format, "%j", syearday, -1)
	if a.Hour() < 10 {
		shour := " " + strconv.Itoa(a.Hour())
		format = strings.Replace(format, "%k", shour, -1)
	}
	format = strings.Replace(format, "%s", strconv.FormatInt(a.Unix(), 10), -1)

	if weekday == 0 {
		format = strings.Replace(format, "%u", "7", -1)
	} else {
		format = strings.Replace(format, "%u", sweekday, -1)
	}

	format = strings.Replace(format, "%U", weekNumber(a, time.Sunday), -1)
	format = strings.Replace(format, "%U", sweek, -1)
	format = strings.Replace(format, "%w", sweekday, -1)
	format = strings.Replace(format, "%W", weekNumber(a, time.Monday), -1)
	return strings.Replace(format, "%%", "%", -1)
}

// Used for %U and %W:
// %U: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Sunday as the first day of week 01.
//
// %W: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Monday as the first day of week 01.
func weekNumber(a Arrow, firstday time.Weekday) string {
	dayone := a.AtBeginningOfYear()
	for dayone.Weekday() != time.Sunday {
		dayone = dayone.AddDays(1)
	}
	week := int(a.Sub(dayone.AddDays(-7)) / Week)
	return strconv.Itoa(week)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package e2e

import (
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = framework.KubeDescribe("Kibana Logging Instances Is Alive [Feature:Elasticsearch]", func() {
	f := framework.NewDefaultFramework("kibana-logging")

	BeforeEach(func() {
		// TODO: For now assume we are only testing cluster logging with Elasticsearch
		// and Kibana on GCE. Once we are sure that Elasticsearch and Kibana cluster level logging
		// works for other providers we should widen this scope of this test.
		framework.SkipUnlessProviderIs("gce")
	})

	It("should check that the Kibana logging instance is alive", func() {
		ClusterLevelLoggingWithKibana(f)
	})
})

const (
	kibanaKey   = "k8s-app"
	kibanaValue = "kibana-logging"
)

// ClusterLevelLoggingWithKibana is an end to end test that checks to see if Kibana is alive.
func ClusterLevelLoggingWithKibana(f *framework.Framework) {
	// graceTime is how long to keep retrying requests for status information.
	const graceTime = 10 * time.Minute

	// Check for the existence of the Kibana service.
	By("Checking the Kibana service exists.")
	s := f.ClientSet.Core().Services(api.NamespaceSystem)
	// Make a few attempts to connect. This makes the test robust against
	// being run as the first e2e test just after the e2e cluster has been created.
	var err error
	for start := time.Now(); time.Since(start) < graceTime; time.Sleep(5 * time.Second) {
		if _, err = s.Get("kibana-logging"); err == nil {
			break
		}
		framework.Logf("Attempt to check for the existence of the Kibana service failed after %v", time.Since(start))
	}
	Expect(err).NotTo(HaveOccurred())

	// Wait for the Kibana pod(s) to enter the running state.
	By("Checking to make sure the Kibana pods are running")
	label := labels.SelectorFromSet(labels.Set(map[string]string{kibanaKey: kibanaValue}))
	options := api.ListOptions{LabelSelector: label}
	pods, err := f.ClientSet.Core().Pods(api.NamespaceSystem).List(options)
	Expect(err).NotTo(HaveOccurred())
	for _, pod := range pods.Items {
		err = framework.WaitForPodRunningInNamespace(f.ClientSet, &pod)
		Expect(err).NotTo(HaveOccurred())
	}

	By("Checking to make sure we get a response from the Kibana UI.")
	err = nil
	for start := time.Now(); time.Since(start) < graceTime; time.Sleep(5 * time.Second) {
		proxyRequest, errProxy := framework.GetServicesProxyRequest(f.ClientSet, f.ClientSet.Core().RESTClient().Get())
		if errProxy != nil {
			framework.Logf("After %v failed to get services proxy request: %v", time.Since(start), errProxy)
			continue
		}
		// Query against the root URL for Kibana.
		_, err = proxyRequest.Namespace(api.NamespaceSystem).
			Name("kibana-logging").
			DoRaw()
		if err != nil {
			framework.Logf("After %v proxy call to kibana-logging failed: %v", time.Since(start), err)
			continue
		}
		break
	}
	Expect(err).NotTo(HaveOccurred())
}
<commit_msg>Fixed kibana e2e test<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package e2e

import (
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = framework.KubeDescribe("Kibana Logging Instances Is Alive [Feature:Elasticsearch]", func() {
	f := framework.NewDefaultFramework("kibana-logging")

	BeforeEach(func() {
		// TODO: For now assume we are only testing cluster logging with Elasticsearch
		// and Kibana on GCE. Once we are sure that Elasticsearch and Kibana cluster level logging
		// works for other providers we should widen this scope of this test.
		framework.SkipUnlessProviderIs("gce")
	})

	It("should check that the Kibana logging instance is alive", func() {
		ClusterLevelLoggingWithKibana(f)
	})
})

const (
	kibanaKey   = "k8s-app"
	kibanaValue = "kibana-logging"
)

// ClusterLevelLoggingWithKibana is an end to end test that checks to see if Kibana is alive.
func ClusterLevelLoggingWithKibana(f *framework.Framework) {
	// graceTime is how long to keep retrying requests for status information.
	const graceTime = 10 * time.Minute

	// Check for the existence of the Kibana service.
	By("Checking the Kibana service exists.")
	s := f.ClientSet.Core().Services(api.NamespaceSystem)
	// Make a few attempts to connect. This makes the test robust against
	// being run as the first e2e test just after the e2e cluster has been created.
	var err error
	for start := time.Now(); time.Since(start) < graceTime; time.Sleep(5 * time.Second) {
		if _, err = s.Get("kibana-logging"); err == nil {
			break
		}
		framework.Logf("Attempt to check for the existence of the Kibana service failed after %v", time.Since(start))
	}
	Expect(err).NotTo(HaveOccurred())

	// Wait for the Kibana pod(s) to enter the running state.
	By("Checking to make sure the Kibana pods are running")
	label := labels.SelectorFromSet(labels.Set(map[string]string{kibanaKey: kibanaValue}))
	options := api.ListOptions{LabelSelector: label}
	pods, err := f.ClientSet.Core().Pods(api.NamespaceSystem).List(options)
	Expect(err).NotTo(HaveOccurred())
	for _, pod := range pods.Items {
		err = framework.WaitForPodRunningInNamespace(f.ClientSet, &pod)
		Expect(err).NotTo(HaveOccurred())
	}

	By("Checking to make sure we get a response from the Kibana UI.")
	err = nil
	for start := time.Now(); time.Since(start) < graceTime; time.Sleep(5 * time.Second) {
		proxyRequest, errProxy := framework.GetServicesProxyRequest(f.ClientSet, f.ClientSet.Core().RESTClient().Get())
		if errProxy != nil {
			framework.Logf("After %v failed to get services proxy request: %v", time.Since(start), errProxy)
			err = errProxy
			continue
		}
		// Query against the root URL for Kibana.
		_, err = proxyRequest.Namespace(api.NamespaceSystem).
			Name("kibana-logging").
			DoRaw()
		if err != nil {
			framework.Logf("After %v proxy call to kibana-logging failed: %v", time.Since(start), err)
			continue
		}
		break
	}
	Expect(err).NotTo(HaveOccurred())
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

// Package bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil

import (
	"fmt"
	"os/exec"
	"path"
	"runtime"
	"strings"

	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"

	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/command"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/download"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/sysinit"
	"k8s.io/minikube/pkg/minikube/vmpath"
)

// TransferBinaries transfers all required Kubernetes binaries
func TransferBinaries(cfg config.KubernetesConfig, c command.Runner, sm sysinit.Manager, binariesURL string) error {
	ok, err := binariesExist(cfg, c)
	if err == nil && ok {
		klog.Info("Found k8s binaries, skipping transfer")
		return nil
	}
	klog.Infof("Didn't find k8s binaries: %v\nInitiating transfer...", err)

	dir := binRoot(cfg.KubernetesVersion)
	_, err = c.RunCmd(exec.Command("sudo", "mkdir", "-p", dir))
	if err != nil {
		return err
	}

	var g errgroup.Group
	for _, name := range constants.KubernetesReleaseBinaries {
		name := name
		g.Go(func() error {
			src, err := download.Binary(name, cfg.KubernetesVersion, "linux", runtime.GOARCH, binariesURL)
			if err != nil {
				return errors.Wrapf(err, "downloading %s", name)
			}

			if name == "kubelet" && sm.Active(name) {
				if err := sm.ForceStop(name); err != nil {
					klog.Errorf("unable to stop kubelet: %v", err)
				}
			}

			dst := path.Join(dir, name)
			if err := machine.CopyBinary(c, src, dst); err != nil {
				return errors.Wrapf(err, "copybinary %s -> %s", src, dst)
			}
			return nil
		})
	}
	return g.Wait()
}

// binariesExist returns true if the binaries already exist
func binariesExist(cfg config.KubernetesConfig, c command.Runner) (bool, error) {
	dir := binRoot(cfg.KubernetesVersion)
	rr, err := c.RunCmd(exec.Command("sudo", "ls", dir))
	stdout := rr.Stdout.String()
	if err != nil {
		return false, err
	}
	foundBinaries := map[string]struct{}{}
	for _, binary := range strings.Split(stdout, "\n") {
		foundBinaries[binary] = struct{}{}
	}
	for _, name := range constants.KubernetesReleaseBinaries {
		if _, ok := foundBinaries[name]; !ok {
			return false, fmt.Errorf("didn't find preexisting %s", name)
		}
	}
	return true, nil
}

// binRoot returns the persistent path binaries are stored in
func binRoot(version string) string {
	return path.Join(vmpath.GuestPersistentDir, "binaries", version)
}
<commit_msg>Move err check statement in bsutil pkg<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

// Package bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil

import (
	"fmt"
	"os/exec"
	"path"
	"runtime"
	"strings"

	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"

	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/command"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/download"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/sysinit"
	"k8s.io/minikube/pkg/minikube/vmpath"
)

// TransferBinaries transfers all required Kubernetes binaries
func TransferBinaries(cfg config.KubernetesConfig, c command.Runner, sm sysinit.Manager, binariesURL string) error {
	ok, err := binariesExist(cfg, c)
	if err == nil && ok {
		klog.Info("Found k8s binaries, skipping transfer")
		return nil
	}
	klog.Infof("Didn't find k8s binaries: %v\nInitiating transfer...", err)

	dir := binRoot(cfg.KubernetesVersion)
	_, err = c.RunCmd(exec.Command("sudo", "mkdir", "-p", dir))
	if err != nil {
		return err
	}

	var g errgroup.Group
	for _, name := range constants.KubernetesReleaseBinaries {
		name := name
		g.Go(func() error {
			src, err := download.Binary(name, cfg.KubernetesVersion, "linux", runtime.GOARCH, binariesURL)
			if err != nil {
				return errors.Wrapf(err, "downloading %s", name)
			}

			if name == "kubelet" && sm.Active(name) {
				if err := sm.ForceStop(name); err != nil {
					klog.Errorf("unable to stop kubelet: %v", err)
				}
			}

			dst := path.Join(dir, name)
			if err := machine.CopyBinary(c, src, dst); err != nil {
				return errors.Wrapf(err, "copybinary %s -> %s", src, dst)
			}
			return nil
		})
	}
	return g.Wait()
}

// binariesExist returns true if the binaries already exist
func binariesExist(cfg config.KubernetesConfig, c command.Runner) (bool, error) {
	dir := binRoot(cfg.KubernetesVersion)
	rr, err := c.RunCmd(exec.Command("sudo", "ls", dir))
	if err != nil {
		return false, err
	}
	stdout := rr.Stdout.String()
	foundBinaries := map[string]struct{}{}
	for _, binary := range strings.Split(stdout, "\n") {
		foundBinaries[binary] = struct{}{}
	}
	for _, name := range constants.KubernetesReleaseBinaries {
		if _, ok := foundBinaries[name]; !ok {
			return false, fmt.Errorf("didn't find preexisting %s", name)
		}
	}
	return true, nil
}

// binRoot returns the persistent path binaries are stored in
func binRoot(version string) string {
	return path.Join(vmpath.GuestPersistentDir, "binaries", version)
}
<|endoftext|>


<commit_before>package activeconfigmap

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/containerum/chkit/pkg/model/configmap"
	"github.com/containerum/chkit/pkg/util/namegen"
	"github.com/containerum/kube-client/pkg/model"
)

type Flags struct {
	Force      bool     `flag:"force f" desc:"suppress confirmation, optional"`
	File       string   `desc:"file with configmap data, .json, .yaml, .yml, optional"`
	Name       string   `desc:"configmap name, optional"`
	ItemFile   []string `flag:"item-file" desc:"configmap file, KEY:FILE_PATH or FILE_PATH"`
	ItemString []string `flag:"item-string" desc:"configmap item, KEY:VALUE string pair"`
}

func (flags Flags) ConfigMap() (configmap.ConfigMap, error) {
	var config = configmap.ConfigMap{
		Data: make(model.ConfigMapData, 16),
		Name: flags.Name,
	}

	if flags.File != "" {
		return FromFile(flags.File)
	} else if len(flags.ItemString) > 0 {
		items, err := getStringItems(flags.ItemString)
		if err != nil {
			return config, err
		}
		config = config.AddItems(items...)
	} else if len(flags.ItemFile) > 0 {
		items, err := getFileItems(flags.ItemFile)
		if err != nil {
			return config, err
		}
		config = config.AddItems(items...)
	}

	if config.Name == "" && len(config.Items()) > 0 {
		config.Name = namegen.Color() + "-" + config.Items()[0].Key()
	}

	return config, nil
}

func getStringItems(rawItems []string) ([]configmap.Item, error) {
	var items = make([]configmap.Item, 0, len(rawItems))
	for _, rawItem := range rawItems {
		var key string
		var value string
		if tokens := strings.SplitN(rawItem, ":", 2); len(tokens) == 2 {
			key = strings.TrimSpace(tokens[0])
			value = strings.TrimSpace(tokens[1])
		} else {
			return nil, fmt.Errorf("invalid token number in raw string item (got %v, required 2)", len(tokens))
		}
		items = append(items, configmap.NewItem(
			key,
			base64.StdEncoding.EncodeToString([]byte(value)),
		))
	}
	return items, nil
}

func getFileItems(rawItems []string) ([]configmap.Item, error) {
	var items = make([]configmap.Item, 0, len(rawItems))
	for _, rawItem := range rawItems {
		var filepath string
		var key string
		if tokens := strings.SplitN(rawItem, ":", 2); len(tokens) == 2 {
			key = strings.TrimSpace(tokens[0])
			filepath = tokens[1]
		} else if len(tokens) == 1 {
			key = path.Base(tokens[0])
			filepath = tokens[0]
		} else {
			return nil, fmt.Errorf("invalid token number in raw file item (got %v, required 2)", len(tokens))
		}
		value, err := ioutil.ReadFile(filepath)
		if err != nil {
			return nil, err
		}
		items = append(items, configmap.NewItem(
			key,
			string(value),
		))
	}
	return items, nil
}
<commit_msg>Encode in base64 configmap data when creating it from file<commit_after>package activeconfigmap

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/containerum/chkit/pkg/model/configmap"
	"github.com/containerum/chkit/pkg/util/namegen"
	"github.com/containerum/kube-client/pkg/model"
)

type Flags struct {
	Force      bool     `flag:"force f" desc:"suppress confirmation, optional"`
	File       string   `desc:"file with configmap data, .json, .yaml, .yml, optional"`
	Name       string   `desc:"configmap name, optional"`
	ItemFile   []string `flag:"item-file" desc:"configmap file, KEY:FILE_PATH or FILE_PATH"`
	ItemString []string `flag:"item-string" desc:"configmap item, KEY:VALUE string pair"`
}

func (flags Flags) ConfigMap() (configmap.ConfigMap, error) {
	var config = configmap.ConfigMap{
		Data: make(model.ConfigMapData, 16),
		Name: flags.Name,
	}

	if flags.File != "" {
		return FromFile(flags.File)
	} else if len(flags.ItemString) > 0 {
		items, err := getStringItems(flags.ItemString)
		if err != nil {
			return config, err
		}
		config = config.AddItems(items...)
	} else if len(flags.ItemFile) > 0 {
		items, err := getFileItems(flags.ItemFile)
		if err != nil {
			return config, err
		}
		config = config.AddItems(items...)
	}

	if config.Name == "" && len(config.Items()) > 0 {
		config.Name = namegen.Color() + "-" + config.Items()[0].Key()
	}

	return config, nil
}

func getStringItems(rawItems []string) ([]configmap.Item, error) {
	var items = make([]configmap.Item, 0, len(rawItems))
	for _, rawItem := range rawItems {
		var key string
		var value string
		if tokens := strings.SplitN(rawItem, ":", 2); len(tokens) == 2 {
			key = strings.TrimSpace(tokens[0])
			value = strings.TrimSpace(tokens[1])
		} else {
			return nil, fmt.Errorf("invalid token number in raw string item (got %v, required 2)", len(tokens))
		}
		items = append(items, configmap.NewItem(
			key,
			base64.StdEncoding.EncodeToString([]byte(value)),
		))
	}
	return items, nil
}

func getFileItems(rawItems []string) ([]configmap.Item, error) {
	var items = make([]configmap.Item, 0, len(rawItems))
	for _, rawItem := range rawItems {
		var filepath string
		var key string
		if tokens := strings.SplitN(rawItem, ":", 2); len(tokens) == 2 {
			key = strings.TrimSpace(tokens[0])
			filepath = tokens[1]
		} else if len(tokens) == 1 {
			key = path.Base(tokens[0])
			filepath = tokens[0]
		} else {
			return nil, fmt.Errorf("invalid token number in raw file item (got %v, required 2)", len(tokens))
		}
		value, err := ioutil.ReadFile(filepath)
		if err != nil {
			return nil, err
		}
		items = append(items, configmap.NewItem(
			key,
			base64.StdEncoding.EncodeToString(value),
		))
	}
	return items, nil
}
<|endoftext|>


<commit_before>package fundingoffer_test

import (
	"testing"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/fundingoffer"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewFundingOfferFromRaw(t *testing.T) {
	t.Run("invalid arguments", func(t *testing.T) {
		payload := []interface{}{652606505}

		got, err := fundingoffer.FromRaw(payload)
		require.NotNil(t, err)
		require.Nil(t, got)
	})

	t.Run("valid arguments", func(t *testing.T) {
		payload := []interface{}{
			652606505,
			"fETH",
			1574000611000,
			1574000611000,
			0.29797676,
			0.29797676,
			"LIMIT",
			nil,
			nil,
			0,
			"ACTIVE",
			nil,
			nil,
			nil,
			0.0002,
			2,
			1,
			nil,
			nil,
			0,
			nil,
		}

		got, err := fundingoffer.FromRaw(payload)
		require.Nil(t, err)

		expected := &fundingoffer.Offer{
			ID:         652606505,
			Symbol:     "fETH",
			MTSCreated: 1574000611000,
			MTSUpdated: 1574000611000,
			Amount:     0.29797676,
			AmountOrig: 0.29797676,
			Type:       "LIMIT",
			Flags:      0,
			Status:     "ACTIVE",
			Rate:       0.0002,
			Period:     2,
			Notify:     true,
			Hidden:     false,
			Insure:     false,
			Renew:      false,
			RateReal:   0,
		}
		assert.Equal(t, expected, got)
	})
}

func TestFundingOfferSnapshotFromRaw(t *testing.T) {
	t.Run("invalid arguments", func(t *testing.T) {
		payload := []interface{}{}
		got, err := fundingoffer.SnapshotFromRaw(payload)
		require.NotNil(t, err)
		require.Nil(t, got)
	})

	t.Run("partially valid arguments", func(t *testing.T) {
		payload := []interface{}{
			[]interface{}{
				652606505,
				"fETH",
				1574000611000,
				1574000611000,
				0.29797676,
				0.29797676,
				"LIMIT",
				nil,
				nil,
				0,
				"ACTIVE",
				nil,
				nil,
				nil,
				0.0002,
				2,
				1,
				nil,
				nil,
				0,
				nil,
			},
			[]interface{}{652606506},
		}
		got, err := fundingoffer.SnapshotFromRaw(payload)
		require.NotNil(t, err)
		require.Nil(t, got)
	})

	t.Run("valid arguments", func(t *testing.T) {
		payload := []interface{}{
			[]interface{}{
				652606505,
				"fETH",
				1574000611000,
				1574000611000,
				0.29797676,
				0.29797676,
				"LIMIT",
				nil,
				nil,
				0,
				"ACTIVE",
				nil,
				nil,
				nil,
				0.0002,
				2,
				1,
				nil,
				nil,
				0,
				nil,
			},
			[]interface{}{
				652606506,
				"fETH",
				1574000611000,
				1574000611000,
				0.29797676,
				0.29797676,
				"LIMIT",
				nil,
				nil,
				0,
				"ACTIVE",
				nil,
				nil,
				nil,
				0.0002,
				2,
				1,
				1,
				nil,
				0,
				nil,
			},
		}

		got, err := fundingoffer.SnapshotFromRaw(payload)
		require.Nil(t, err)

		expected := &fundingoffer.Snapshot{
			Snapshot: []*fundingoffer.Offer{
				{
					ID:         652606505,
					Symbol:     "fETH",
					MTSCreated: 1574000611000,
					MTSUpdated: 1574000611000,
					Amount:     0.29797676,
					AmountOrig: 0.29797676,
					Type:       "LIMIT",
					Flags:      0,
					Status:     "ACTIVE",
					Rate:       0.0002,
					Period:     2,
					Notify:     true,
					Hidden:     false,
					Insure:     false,
					Renew:      false,
					RateReal:   0,
				},
				{
					ID:         652606506,
					Symbol:     "fETH",
					MTSCreated: 1574000611000,
					MTSUpdated: 1574000611000,
					Amount:     0.29797676,
					AmountOrig: 0.29797676,
					Type:       "LIMIT",
					Flags:      0,
					Status:     "ACTIVE",
					Rate:       0.0002,
					Period:     2,
					Notify:     true,
					Hidden:     true,
					Insure:     false,
					Renew:      false,
					RateReal:   0,
				},
			},
		}

		assert.Equal(t, expected, got)
	})
}

func TestFundingOfferCancelRequest(t *testing.T) {
	t.Run("MarshalJSON", func(t *testing.T) {
		focr := fundingoffer.CancelRequest{ID: 123}

		got, err := focr.MarshalJSON()
		require.Nil(t, err)

		expected := "[0, \"foc\", null, {\"id\":123}]"
		assert.Equal(t, expected, string(got))
	})
}

func TestFundingOfferSubmitRequest(t *testing.T) {
	t.Run("MarshalJSON", func(t *testing.T) {
		fosr := fundingoffer.SubmitRequest{
			Type:   "LIMIT",
			Symbol: "fETH",
			Amount: 0.29797676,
			Rate:   0.0002,
			Period: 2,
		}
		got, err := fosr.MarshalJSON()
		require.Nil(t, err)

		expected := "[0, \"fon\", null, {\"type\":\"LIMIT\",\"symbol\":\"fETH\",\"amount\":\"0.29797676\",\"rate\":\"0.0002\",\"period\":2}]"
		assert.Equal(t, expected, string(got))
	})
}
<commit_msg>funcdingoffer CancelFromRaw test coverage<commit_after>package fundingoffer_test

import (
	"reflect"
	"testing"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/fundingoffer"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewFundingOfferFromRaw(t *testing.T) {
	t.Run("invalid arguments", func(t *testing.T) {
		payload := []interface{}{652606505}

		got, err := fundingoffer.FromRaw(payload)
		require.NotNil(t, err)
		require.Nil(t, got)
	})

	t.Run("valid arguments", func(t *testing.T) {
		payload := []interface{}{
			652606505,
			"fETH",
			1574000611000,
			1574000611000,
			0.29797676,
			0.29797676,
			"LIMIT",
			nil,
			nil,
			0,
			"ACTIVE",
			nil,
			nil,
			nil,
			0.0002,
			2,
			1,
			nil,
			nil,
			0,
			nil,
		}

		got, err := fundingoffer.FromRaw(payload)
		require.Nil(t, err)

		expected := &fundingoffer.Offer{
			ID:         652606505,
			Symbol:     "fETH",
			MTSCreated: 1574000611000,
			MTSUpdated: 1574000611000,
			Amount:     0.29797676,
			AmountOrig: 0.29797676,
			Type:       "LIMIT",
			Flags:      0,
			Status:     "ACTIVE",
			Rate:       0.0002,
			Period:     2,
			Notify:     true,
			Hidden:     false,
			Insure:     false,
			Renew:      false,
			RateReal:   0,
		}
		assert.Equal(t, expected, got)
	})
}

func TestFundingOfferSnapshotFromRaw(t *testing.T) {
	t.Run("invalid arguments", func(t *testing.T) {
		payload := []interface{}{}
		got, err := fundingoffer.SnapshotFromRaw(payload)
		require.NotNil(t, err)
		require.Nil(t, got)
	})

	t.Run("partially valid arguments", func(t *testing.T) {
		payload := []interface{}{
			[]interface{}{
				652606505,
				"fETH",
				1574000611000,
				1574000611000,
				0.29797676,
				0.29797676,
				"LIMIT",
				nil,
				nil,
				0,
				"ACTIVE",
				nil,
				nil,
				nil,
				0.0002,
				2,
				1,
				nil,
				nil,
				0,
				nil,
			},
			[]interface{}{652606506},
		}
		got, err := fundingoffer.SnapshotFromRaw(payload)
		require.NotNil(t, err)
		require.Nil(t, got)
	})

	t.Run("valid arguments", func(t *testing.T) {
		payload := []interface{}{
			[]interface{}{
				652606505,
				"fETH",
				1574000611000,
				1574000611000,
				0.29797676,
				0.29797676,
				"LIMIT",
				nil,
				nil,
				0,
				"ACTIVE",
				nil,
				nil,
				nil,
				0.0002,
				2,
				1,
				nil,
				nil,
				0,
				nil,
			},
			[]interface{}{
				652606506,
				"fETH",
				1574000611000,
				1574000611000,
				0.29797676,
				0.29797676,
				"LIMIT",
				nil,
				nil,
				0,
				"ACTIVE",
				nil,
				nil,
				nil,
				0.0002,
				2,
				1,
				1,
				nil,
				0,
				nil,
			},
		}

		got, err := fundingoffer.SnapshotFromRaw(payload)
		require.Nil(t, err)

		expected := &fundingoffer.Snapshot{
			Snapshot: []*fundingoffer.Offer{
				{
					ID:         652606505,
					Symbol:     "fETH",
					MTSCreated: 1574000611000,
					MTSUpdated: 1574000611000,
					Amount:     0.29797676,
					AmountOrig: 0.29797676,
					Type:       "LIMIT",
					Flags:      0,
					Status:     "ACTIVE",
					Rate:       0.0002,
					Period:     2,
					Notify:     true,
					Hidden:     false,
					Insure:     false,
					Renew:      false,
					RateReal:   0,
				},
				{
					ID:         652606506,
					Symbol:     "fETH",
					MTSCreated: 1574000611000,
					MTSUpdated: 1574000611000,
					Amount:     0.29797676,
					AmountOrig: 0.29797676,
					Type:       "LIMIT",
					Flags:      0,
					Status:     "ACTIVE",
					Rate:       0.0002,
					Period:     2,
					Notify:     true,
					Hidden:     true,
					Insure:     false,
					Renew:      false,
					RateReal:   0,
				},
			},
		}

		assert.Equal(t, expected, got)
	})
}

func TestFundingOfferCancelRequest(t *testing.T) {
	t.Run("MarshalJSON", func(t *testing.T) {
		focr := fundingoffer.CancelRequest{ID: 123}

		got, err := focr.MarshalJSON()
		require.Nil(t, err)

		expected := "[0, \"foc\", null, {\"id\":123}]"
		assert.Equal(t, expected, string(got))
	})
}

func TestFundingOfferSubmitRequest(t *testing.T) {
	t.Run("MarshalJSON", func(t *testing.T) {
		fosr := fundingoffer.SubmitRequest{
			Type:   "LIMIT",
			Symbol: "fETH",
			Amount: 0.29797676,
			Rate:   0.0002,
			Period: 2,
		}
		got, err := fosr.MarshalJSON()
		require.Nil(t, err)

		expected := "[0, \"fon\", null, {\"type\":\"LIMIT\",\"symbol\":\"fETH\",\"amount\":\"0.29797676\",\"rate\":\"0.0002\",\"period\":2}]"
		assert.Equal(t, expected, string(got))
	})
}

func TestCancelFromRaw(t *testing.T) {
	pld := []interface{}{
		652606505, "fETH", 1574000611000, 1574000611000, 0.29797676, 0.29797676, "LIMIT",
		nil, nil, 0, "ACTIVE", nil, nil, nil, 0.0002, 2, 1, nil, nil, 0, nil,
	}

	expected := "fundingoffer.Cancel"
	o, err := fundingoffer.CancelFromRaw(pld)
	assert.Nil(t, err)

	got := reflect.TypeOf(o).String()
	assert.Equal(t, expected, got)
}
<|endoftext|>


<commit_before>package navtreeimpl

import (
	"path"
	"sort"
	"strconv"

	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/plugins"
	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
	"github.com/grafana/grafana/pkg/services/featuremgmt"
	"github.com/grafana/grafana/pkg/services/navtree"
	"github.com/grafana/grafana/pkg/services/pluginsettings"
	"github.com/grafana/grafana/pkg/util"
)

func (s *ServiceImpl) addAppLinks(treeRoot *navtree.NavTreeRoot, c *models.ReqContext) error {
	topNavEnabled := s.features.IsEnabled(featuremgmt.FlagTopnav)
	hasAccess := ac.HasAccess(s.accessControl, c)
	appLinks := []*navtree.NavLink{}

	pss, err := s.pluginSettings.GetPluginSettings(c.Req.Context(), &pluginsettings.GetArgs{OrgID: c.OrgID})
	if err != nil {
		return err
	}

	isPluginEnabled := func(plugin plugins.PluginDTO) bool {
		if plugin.AutoEnabled {
			return true
		}
		for _, ps := range pss {
			if ps.PluginID == plugin.ID {
				return ps.Enabled
			}
		}
		return false
	}

	for _, plugin := range s.pluginStore.Plugins(c.Req.Context(), plugins.App) {
		if !isPluginEnabled(plugin) {
			continue
		}

		if !hasAccess(ac.ReqSignedIn,
			ac.EvalPermission(plugins.ActionAppAccess, plugins.ScopeProvider.GetResourceScope(plugin.ID))) {
			continue
		}

		if appNode := s.processAppPlugin(plugin, c, topNavEnabled, treeRoot); appNode != nil {
			appLinks = append(appLinks, appNode)
		}
	}

	if len(appLinks) > 0 {
		sort.SliceStable(appLinks, func(i, j int) bool {
			return appLinks[i].Text < appLinks[j].Text
		})
	}

	for _, appLink := range appLinks {
		treeRoot.AddSection(appLink)
	}

	return nil
}

func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqContext, topNavEnabled bool, treeRoot *navtree.NavTreeRoot) *navtree.NavLink {
	hasAccessToInclude := s.hasAccessToInclude(c, plugin.ID)
	appLink := &navtree.NavLink{
		Text:       plugin.Name,
		Id:         "plugin-page-" + plugin.ID,
		Img:        plugin.Info.Logos.Small,
		Section:    navtree.NavSectionPlugin,
		SortWeight: navtree.WeightPlugin,
		IsSection:  true,
		PluginID:   plugin.ID,
	}

	if topNavEnabled {
		appLink.Url = s.cfg.AppSubURL + "/a/" + plugin.ID
	} else {
		appLink.Url = path.Join(s.cfg.AppSubURL, plugin.DefaultNavURL)
	}

	for _, include := range plugin.Includes {
		if !hasAccessToInclude(include) {
			continue
		}

		if include.Type == "page" {
			link := &navtree.NavLink{
				Text:     include.Name,
				Icon:     include.Icon,
				PluginID: plugin.ID,
			}

			if len(include.Path) > 0 {
				link.Url = s.cfg.AppSubURL + include.Path
				if include.DefaultNav && include.AddToNav {
					appLink.Url = link.Url
				}
			} else {
				link.Url = s.cfg.AppSubURL + "/plugins/" + plugin.ID + "/page/" + include.Slug
			}

			// Register standalone plugin pages to certain sections using the Grafana config
			if pathConfig, ok := s.navigationAppPathConfig[include.Path]; ok {
				if sectionForPage := treeRoot.FindById(pathConfig.SectionID); sectionForPage != nil {
					link.Id = "standalone-plugin-page-" + include.Path
					link.SortWeight = pathConfig.SortWeight

					// Check if the section already has a page with the same URL, and in that case override it
					// (This only happens if it is explicitly set by `navigation.app_standalone_pages` in the INI config)
					isOverridingCorePage := false
					for _, child := range sectionForPage.Children {
						if child.Url == link.Url {
							child.Id = link.Id
							child.SortWeight = link.SortWeight
							child.PluginID = link.PluginID
							child.Children = []*navtree.NavLink{}
							isOverridingCorePage = true
							break
						}
					}

					// Append the page to the section
					if !isOverridingCorePage {
						sectionForPage.Children = append(sectionForPage.Children, link)
					}
				}

				// Register the page under the app
			} else if include.AddToNav {
				appLink.Children = append(appLink.Children, link)
			}
		}

		if include.Type == "dashboard" && include.AddToNav {
			dboardURL := include.DashboardURLPath()
			if dboardURL != "" {
				link := &navtree.NavLink{
					Url:      path.Join(s.cfg.AppSubURL, dboardURL),
					Text:     include.Name,
					PluginID: plugin.ID,
				}
				appLink.Children = append(appLink.Children, link)
			}
		}
	}

	// Apps without any nav children are not part of navtree
	if len(appLink.Children) == 0 {
		return nil
	}
	// If we only have one child and it's the app default nav then remove it from children
	if len(appLink.Children) == 1 && appLink.Children[0].Url == appLink.Url {
		appLink.Children = []*navtree.NavLink{}
	}

	if !topNavEnabled {
		return appLink
	}

	// Remove default nav child
	childrenWithoutDefault := []*navtree.NavLink{}
	for _, child := range appLink.Children {
		if child.Url != appLink.Url {
			childrenWithoutDefault = append(childrenWithoutDefault, child)
		}
	}
	appLink.Children = childrenWithoutDefault

	// Handle moving apps into specific navtree sections
	alertingNode := treeRoot.FindById(navtree.NavIDAlerting)
	sectionID := navtree.NavIDApps

	if navConfig, hasOverride := s.navigationAppConfig[plugin.ID]; hasOverride {
		appLink.SortWeight = navConfig.SortWeight
		sectionID = navConfig.SectionID

		if len(navConfig.Text) > 0 {
			appLink.Text = navConfig.Text
		}
	}

	if navNode := treeRoot.FindById(sectionID); navNode != nil {
		navNode.Children = append(navNode.Children, appLink)
	} else {
		switch sectionID {
		case navtree.NavIDApps:
			treeRoot.AddSection(&navtree.NavLink{
				Text:       "Apps",
				Icon:       "apps",
				SubTitle:   "App plugins that extend the Grafana experience",
				Id:         navtree.NavIDApps,
				Children:   []*navtree.NavLink{appLink},
				Section:    navtree.NavSectionCore,
				SortWeight: navtree.WeightApps,
				Url:        s.cfg.AppSubURL + "/apps",
			})
		case navtree.NavIDMonitoring:
			treeRoot.AddSection(&navtree.NavLink{
				Text:       "Monitoring",
				Id:         navtree.NavIDMonitoring,
				SubTitle:   "Monitoring and infrastructure apps",
				Icon:       "heart-rate",
				Section:    navtree.NavSectionCore,
				SortWeight: navtree.WeightMonitoring,
				Children:   []*navtree.NavLink{appLink},
				Url:        s.cfg.AppSubURL + "/monitoring",
			})
		case navtree.NavIDAlertsAndIncidents:
			if alertingNode != nil {
				treeRoot.AddSection(&navtree.NavLink{
					Text:       "Alerts & incidents",
					Id:         navtree.NavIDAlertsAndIncidents,
					SubTitle:   "Alerting and incident management apps",
					Icon:       "bell",
					Section:    navtree.NavSectionCore,
					SortWeight: navtree.WeightAlertsAndIncidents,
					Children:   []*navtree.NavLink{alertingNode, appLink},
					Url:        s.cfg.AppSubURL + "/alerts-and-incidents",
				})
				treeRoot.RemoveSection(alertingNode)
			}
		default:
			s.log.Error("Plugin app nav id not found", "pluginId", plugin.ID, "navId", sectionID)
		}
	}

	return nil
}

func (s *ServiceImpl) hasAccessToInclude(c *models.ReqContext, pluginID string) func(include *plugins.Includes) bool {
	hasAccess := ac.HasAccess(s.accessControl, c)
	return func(include *plugins.Includes) bool {
		useRBAC := s.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) &&
			!s.accessControl.IsDisabled() && include.RequiresRBACAction()
		if useRBAC && !hasAccess(ac.ReqHasRole(include.Role), ac.EvalPermission(include.Action)) {
			s.log.Debug("plugin include is covered by RBAC, user doesn't have access",
				"plugin", pluginID,
				"include", include.Name)
			return false
		} else if !useRBAC && !c.HasUserRole(include.Role) {
			return false
		}
		return true
	}
}

func (s *ServiceImpl) readNavigationSettings() {
	s.navigationAppConfig = map[string]NavigationAppConfig{
		"grafana-k8s-app":                  {SectionID: navtree.NavIDMonitoring, SortWeight: 1, Text: "Kubernetes"},
		"grafana-synthetic-monitoring-app": {SectionID: navtree.NavIDMonitoring, SortWeight: 2, Text: "Synthetics"},
		"grafana-oncall-app":               {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 1, Text: "OnCall"},
		"grafana-incident-app":             {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 2, Text: "Incident"},
		"grafana-ml-app":                   {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 3},
		"grafana-cloud-link-app":           {SectionID: navtree.NavIDCfg},
	}

	s.navigationAppPathConfig = map[string]NavigationAppConfig{
		"/a/grafana-auth-app": {SectionID: navtree.NavIDCfg, SortWeight: 7},
	}

	appSections := s.cfg.Raw.Section("navigation.app_sections")
	appStandalonePages := s.cfg.Raw.Section("navigation.app_standalone_pages")

	for _, key := range appSections.Keys() {
		pluginId := key.Name()
		// Support <id> <weight> value
		values := util.SplitString(appSections.Key(key.Name()).MustString(""))

		appCfg := &NavigationAppConfig{SectionID: values[0]}
		if len(values) > 1 {
			if weight, err := strconv.ParseInt(values[1], 10, 64); err == nil {
				appCfg.SortWeight = weight
			}
		}

		s.navigationAppConfig[pluginId] = *appCfg
	}

	for _, key := range appStandalonePages.Keys() {
		url := key.Name()
		// Support <id> <weight> value
		values := util.SplitString(appStandalonePages.Key(key.Name()).MustString(""))

		appCfg := &NavigationAppConfig{SectionID: values[0]}
		if len(values) > 1 {
			if weight, err := strconv.ParseInt(values[1], 10, 64); err == nil {
				appCfg.SortWeight = weight
			}
		}

		s.navigationAppPathConfig[url] = *appCfg
	}
}
<commit_msg>Navigation: rename Grafana Machine Learning to just Machine Learning (#58893)<commit_after>package navtreeimpl

import (
	"path"
	"sort"
	"strconv"

	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/plugins"
	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
	"github.com/grafana/grafana/pkg/services/featuremgmt"
	"github.com/grafana/grafana/pkg/services/navtree"
	"github.com/grafana/grafana/pkg/services/pluginsettings"
	"github.com/grafana/grafana/pkg/util"
)

func (s *ServiceImpl) addAppLinks(treeRoot *navtree.NavTreeRoot, c *models.ReqContext) error {
	topNavEnabled := s.features.IsEnabled(featuremgmt.FlagTopnav)
	hasAccess := ac.HasAccess(s.accessControl, c)
	appLinks := []*navtree.NavLink{}

	pss, err := s.pluginSettings.GetPluginSettings(c.Req.Context(), &pluginsettings.GetArgs{OrgID: c.OrgID})
	if err != nil {
		return err
	}

	isPluginEnabled := func(plugin plugins.PluginDTO) bool {
		if plugin.AutoEnabled {
			return true
		}
		for _, ps := range pss {
			if ps.PluginID == plugin.ID {
				return ps.Enabled
			}
		}
		return false
	}

	for _, plugin := range s.pluginStore.Plugins(c.Req.Context(), plugins.App) {
		if !isPluginEnabled(plugin) {
			continue
		}

		if !hasAccess(ac.ReqSignedIn,
			ac.EvalPermission(plugins.ActionAppAccess, plugins.ScopeProvider.GetResourceScope(plugin.ID))) {
			continue
		}

		if appNode := s.processAppPlugin(plugin, c, topNavEnabled, treeRoot); appNode != nil {
			appLinks = append(appLinks, appNode)
		}
	}

	if len(appLinks) > 0 {
		sort.SliceStable(appLinks, func(i, j int) bool {
			return appLinks[i].Text < appLinks[j].Text
		})
	}

	for _, appLink := range appLinks {
		treeRoot.AddSection(appLink)
	}

	return nil
}

func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqContext, topNavEnabled bool, treeRoot *navtree.NavTreeRoot) *navtree.NavLink {
	hasAccessToInclude := s.hasAccessToInclude(c, plugin.ID)
	appLink := &navtree.NavLink{
		Text:       plugin.Name,
		Id:         "plugin-page-" + plugin.ID,
		Img:        plugin.Info.Logos.Small,
		Section:    navtree.NavSectionPlugin,
		SortWeight: navtree.WeightPlugin,
		IsSection:  true,
		PluginID:   plugin.ID,
	}

	if topNavEnabled {
		appLink.Url = s.cfg.AppSubURL + "/a/" + plugin.ID
	} else {
		appLink.Url = path.Join(s.cfg.AppSubURL, plugin.DefaultNavURL)
	}

	for _, include := range plugin.Includes {
		if !hasAccessToInclude(include) {
			continue
		}

		if include.Type == "page" {
			link := &navtree.NavLink{
				Text:     include.Name,
				Icon:     include.Icon,
				PluginID: plugin.ID,
			}

			if len(include.Path) > 0 {
				link.Url = s.cfg.AppSubURL + include.Path
				if include.DefaultNav && include.AddToNav {
					appLink.Url = link.Url
				}
			} else {
				link.Url = s.cfg.AppSubURL + "/plugins/" + plugin.ID + "/page/" + include.Slug
			}

			// Register standalone plugin pages to certain sections using the Grafana config
			if pathConfig, ok := s.navigationAppPathConfig[include.Path]; ok {
				if sectionForPage := treeRoot.FindById(pathConfig.SectionID); sectionForPage != nil {
					link.Id = "standalone-plugin-page-" + include.Path
					link.SortWeight = pathConfig.SortWeight

					// Check if the section already has a page with the same URL, and in that case override it
					// (This only happens if it is explicitly set by `navigation.app_standalone_pages` in the INI config)
					isOverridingCorePage := false
					for _, child := range sectionForPage.Children {
						if child.Url == link.Url {
							child.Id = link.Id
							child.SortWeight = link.SortWeight
							child.PluginID = link.PluginID
							child.Children = []*navtree.NavLink{}
							isOverridingCorePage = true
							break
						}
					}

					// Append the page to the section
					if !isOverridingCorePage {
						sectionForPage.Children = append(sectionForPage.Children, link)
					}
				}

				// Register the page under the app
			} else if include.AddToNav {
				appLink.Children = append(appLink.Children, link)
			}
		}

		if include.Type == "dashboard" && include.AddToNav {
			dboardURL := include.DashboardURLPath()
			if dboardURL != "" {
				link := &navtree.NavLink{
					Url:      path.Join(s.cfg.AppSubURL, dboardURL),
					Text:     include.Name,
					PluginID: plugin.ID,
				}
				appLink.Children = append(appLink.Children, link)
			}
		}
	}

	// Apps without any nav children are not part of navtree
	if len(appLink.Children) == 0 {
		return nil
	}
	// If we only have one child and it's the app default nav then remove it from children
	if len(appLink.Children) == 1 && appLink.Children[0].Url == appLink.Url {
		appLink.Children = []*navtree.NavLink{}
	}

	if !topNavEnabled {
		return appLink
	}

	// Remove default nav child
	childrenWithoutDefault := []*navtree.NavLink{}
	for _, child := range appLink.Children {
		if child.Url != appLink.Url {
			childrenWithoutDefault = append(childrenWithoutDefault, child)
		}
	}
	appLink.Children = childrenWithoutDefault

	// Handle moving apps into specific navtree sections
	alertingNode := treeRoot.FindById(navtree.NavIDAlerting)
	sectionID := navtree.NavIDApps

	if navConfig, hasOverride := s.navigationAppConfig[plugin.ID]; hasOverride {
		appLink.SortWeight = navConfig.SortWeight
		sectionID = navConfig.SectionID

		if len(navConfig.Text) > 0 {
			appLink.Text = navConfig.Text
		}
	}

	if navNode := treeRoot.FindById(sectionID); navNode != nil {
		navNode.Children = append(navNode.Children, appLink)
	} else {
		switch sectionID {
		case navtree.NavIDApps:
			treeRoot.AddSection(&navtree.NavLink{
				Text:       "Apps",
				Icon:       "apps",
				SubTitle:   "App plugins that extend the Grafana experience",
				Id:         navtree.NavIDApps,
				Children:   []*navtree.NavLink{appLink},
				Section:    navtree.NavSectionCore,
				SortWeight: navtree.WeightApps,
				Url:        s.cfg.AppSubURL + "/apps",
			})
		case navtree.NavIDMonitoring:
			treeRoot.AddSection(&navtree.NavLink{
				Text:       "Monitoring",
				Id:         navtree.NavIDMonitoring,
				SubTitle:   "Monitoring and infrastructure apps",
				Icon:       "heart-rate",
				Section:    navtree.NavSectionCore,
				SortWeight: navtree.WeightMonitoring,
				Children:   []*navtree.NavLink{appLink},
				Url:        s.cfg.AppSubURL + "/monitoring",
			})
		case navtree.NavIDAlertsAndIncidents:
			if alertingNode != nil {
				treeRoot.AddSection(&navtree.NavLink{
					Text:       "Alerts & incidents",
					Id:         navtree.NavIDAlertsAndIncidents,
					SubTitle:   "Alerting and incident management apps",
					Icon:       "bell",
					Section:    navtree.NavSectionCore,
					SortWeight: navtree.WeightAlertsAndIncidents,
					Children:   []*navtree.NavLink{alertingNode, appLink},
					Url:        s.cfg.AppSubURL + "/alerts-and-incidents",
				})
				treeRoot.RemoveSection(alertingNode)
			}
		default:
			s.log.Error("Plugin app nav id not found", "pluginId", plugin.ID, "navId", sectionID)
		}
	}

	return nil
}

func (s *ServiceImpl) hasAccessToInclude(c *models.ReqContext, pluginID string) func(include *plugins.Includes) bool {
	hasAccess := ac.HasAccess(s.accessControl, c)
	return func(include *plugins.Includes) bool {
		useRBAC := s.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) &&
			!s.accessControl.IsDisabled() && include.RequiresRBACAction()
		if useRBAC && !hasAccess(ac.ReqHasRole(include.Role), ac.EvalPermission(include.Action)) {
			s.log.Debug("plugin include is covered by RBAC, user doesn't have access",
				"plugin", pluginID,
				"include", include.Name)
			return false
		} else if !useRBAC && !c.HasUserRole(include.Role) {
			return false
		}
		return true
	}
}

func (s *ServiceImpl) readNavigationSettings() {
	s.navigationAppConfig = map[string]NavigationAppConfig{
		"grafana-k8s-app":                  {SectionID: navtree.NavIDMonitoring, SortWeight: 1, Text: "Kubernetes"},
		"grafana-synthetic-monitoring-app": {SectionID: navtree.NavIDMonitoring, SortWeight: 2, Text: "Synthetics"},
		"grafana-oncall-app":               {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 1, Text: "OnCall"},
		"grafana-incident-app":             {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 2, Text: "Incident"},
		"grafana-ml-app":                   {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 3, Text: "Machine Learning"},
		"grafana-cloud-link-app":           {SectionID: navtree.NavIDCfg},
	}

	s.navigationAppPathConfig = map[string]NavigationAppConfig{
		"/a/grafana-auth-app": {SectionID: navtree.NavIDCfg, SortWeight: 7},
	}

	appSections := s.cfg.Raw.Section("navigation.app_sections")
	appStandalonePages := s.cfg.Raw.Section("navigation.app_standalone_pages")

	for _, key := range appSections.Keys() {
		pluginId := key.Name()
		// Support <id> <weight> value
		values := util.SplitString(appSections.Key(key.Name()).MustString(""))

		appCfg := &NavigationAppConfig{SectionID: values[0]}
		if len(values) > 1 {
			if weight, err := strconv.ParseInt(values[1], 10, 64); err == nil {
				appCfg.SortWeight = weight
			}
		}

		s.navigationAppConfig[pluginId] = *appCfg
	}

	for _, key := range appStandalonePages.Keys() {
		url := key.Name()
		// Support <id> <weight> value
		values := util.SplitString(appStandalonePages.Key(key.Name()).MustString(""))

		appCfg := &NavigationAppConfig{SectionID: values[0]}
		if len(values) > 1 {
			if weight, err := strconv.ParseInt(values[1], 10, 64); err == nil {
				appCfg.SortWeight = weight
			}
		}

		s.navigationAppPathConfig[url] = *appCfg
	}
}
<|endoftext|>