<commit_before>package payment

import (
	"encoding/json"
	"errors"
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"
	"socialapi/workers/payment/paypal"
	"socialapi/workers/payment/stripe"
	"time"

	"github.com/koding/logging"
)

var (
	ProviderNotFound       = errors.New("provider not found")
	ProviderNotImplemented = errors.New("provider not implemented")
	Log                    = logging.NewLogger("payment")
)

//----------------------------------------------------------
// SubscribeRequest
//----------------------------------------------------------

type SubscribeRequest struct {
	AccountId, Token, Email           string
	Provider, PlanTitle, PlanInterval string
}

func (s *SubscribeRequest) Do() (interface{}, error) {
	var err error

	switch s.Provider {
	case "stripe":
		err = stripe.Subscribe(
			s.Token, s.AccountId, s.Email, s.PlanTitle, s.PlanInterval,
		)
	case "paypal":
		err = paypal.SubscribeWithPlan(s.Token, s.AccountId, s.PlanTitle, s.PlanInterval)
	default:
		err = ProviderNotFound
	}

	if err != nil {
		Log.Error(
			"Subscribing account: %s to plan: %s failed. %s",
			s.AccountId, s.PlanTitle, err,
		)
	}

	return nil, err
}

//----------------------------------------------------------
// AccountRequest
//----------------------------------------------------------

type AccountRequest struct {
	AccountId string
}

type SubscriptionsResponse struct {
	AccountId          string    `json:"accountId"`
	PlanTitle          string    `json:"planTitle"`
	PlanInterval       string    `json:"planInterval"`
	State              string    `json:"state"`
	Provider           string    `json:"provider"`
	CurrentPeriodStart time.Time `json:"currentPeriodStart"`
	CurrentPeriodEnd   time.Time `json:"currentPeriodEnd"`
}

// Subscriptions return given `account_id` subscription if it exists.
// In case of no customer, or no subscriptions or no plan found, it
// returns the default plan as subscription.
func (a *AccountRequest) Subscriptions() (*SubscriptionsResponse, error) {
	if a.AccountId == "" {
		return nil, paymenterrors.ErrAccountIdIsNotSet
	}

	defaultResp := &SubscriptionsResponse{
		AccountId:    a.AccountId,
		PlanTitle:    "free",
		PlanInterval: "month",
		State:        "active",
		Provider:     "koding",
	}

	customer, err := stripe.FindCustomerByOldId(a.AccountId)
	if err != nil {
		return defaultResp, nil
	}

	subscriptions, err := stripe.FindCustomerSubscriptions(customer)
	if err != nil {
		return defaultResp, nil
	}

	if len(subscriptions) == 0 {
		return defaultResp, nil
	}

	currentSubscription := subscriptions[0]

	// cancel implies user took the action after satisfying provider limits,
	// therefore we return `free` plan for them
	if currentSubscription.State == paymentmodels.SubscriptionStateCanceled {
		return defaultResp, nil
	}

	plan := &paymentmodels.Plan{}
	err = plan.ById(currentSubscription.PlanId)
	if err != nil {
		return defaultResp, nil
	}

	resp := &SubscriptionsResponse{
		AccountId:          a.AccountId,
		PlanTitle:          plan.Title,
		PlanInterval:       plan.Interval,
		CurrentPeriodStart: currentSubscription.CurrentPeriodStart,
		CurrentPeriodEnd:   currentSubscription.CurrentPeriodEnd,
		State:              currentSubscription.State,
		Provider:           currentSubscription.Provider,
	}

	return resp, nil
}

func (a *AccountRequest) Invoices() ([]*stripe.StripeInvoiceResponse, error) {
	invoices, err := stripe.FindInvoicesForCustomer(a.AccountId)
	if err != nil && err != paymenterrors.ErrCustomerNotFound {
		Log.Error("Fetching invoices for account: %s failed. %s", a.AccountId, err)
	}

	return invoices, err
}

func (a *AccountRequest) CreditCard() (*stripe.CreditCardResponse, error) {
	resp, err := stripe.GetCreditCard(a.AccountId)
	if err != nil && err != paymenterrors.ErrCustomerNotFound {
		Log.Error("Fetching cc for account: %s failed. %s", a.AccountId, err)
	}

	return resp, err
}

func (a *AccountRequest) Delete() (interface{}, error) {
	err := stripe.DeleteCustomer(a.AccountId)
	if err != nil {
		Log.Error("Deleting account: %s failed. %s", a.AccountId, err)
	}

	return nil, err
}

//----------------------------------------------------------
// UpdateCreditCard
//----------------------------------------------------------

type UpdateCreditCardRequest struct {
	AccountId, Provider, Token string
}

func (u *UpdateCreditCardRequest) Do() (interface{}, error) {
	switch u.Provider {
	case "stripe":
		err := stripe.UpdateCreditCard(u.AccountId, u.Token)
		if err != nil {
			Log.Error("Updating cc for account: %s failed. %s", u.AccountId, err)
		}

		return nil, err
	case "paypal":
		return nil, ProviderNotImplemented
	default:
		return nil, ProviderNotFound
	}
}

//----------------------------------------------------------
// StripeWebhook
//----------------------------------------------------------

type StripeWebhook struct {
	Name     string `json:"type"`
	Created  int    `json:"created"`
	Livemode bool   `json:"livemode"`
	Id       string `json:"id"`
	Data     struct {
		Object interface{} `json:"object"`
	} `json:"data"`
}

func (s *StripeWebhook) Do() (interface{}, error) {
	var err error

	if !s.Livemode {
		Log.Error("Received test Stripe webhook: %v", s)
		return nil, nil
	}

	raw, err := json.Marshal(s.Data.Object)
	if err != nil {
		Log.Error("Error marshalling Stripe webhook '%v' : %v", s, err)
		return nil, err
	}

	switch s.Name {
	case "customer.subscription.deleted":
		err = stripe.SubscriptionDeletedWebhook(raw)
	case "invoice.created":
		err = stripe.InvoiceCreatedWebhook(raw)
	case "customer.deleted":
		err = stripe.CustomerDeletedWebhook(raw)
	}

	if err != nil {
		Log.Error("Error handling Stripe webhook '%v' : %v", s, err)
	}

	return nil, err
}

//----------------------------------------------------------
// Paypal
//----------------------------------------------------------

type PaypalRequest struct {
	Token     string `json:"token"`
	AccountId string `json:"accountId"`
}

func (p *PaypalRequest) Success() (interface{}, error) {
	return nil, paypal.Subscribe(p.Token, p.AccountId)
}

func (p *PaypalRequest) Cancel() (interface{}, error) {
	return nil, nil
}

type PaypalGetTokenRequest struct {
	PlanTitle    string `json:"planTitle"`
	PlanInterval string `json:"planInterval"`
}

func (p *PaypalGetTokenRequest) Do() (interface{}, error) {
	return paypal.GetToken(p.PlanTitle, p.PlanInterval)
}

//----------------------------------------------------------
// Webhook
//----------------------------------------------------------

type PaypalWebhook struct {
	Status  string `json:"payment_status"`
	PayerId string `json:"payer_id"`
}

var PaypalActionCancel = "cancel"

var PaypalStatusActionMap = map[string]string{
	"Denied":   PaypalActionCancel,
	"Expired":  PaypalActionCancel,
	"Failed":   PaypalActionCancel,
	"Reversed": PaypalActionCancel,
	"Voided":   PaypalActionCancel,
}

func (p *PaypalWebhook) Do() (interface{}, error) {
	action, ok := PaypalStatusActionMap[p.Status]
	if !ok {
		return nil, nil
	}

	var err error

	switch action {
	case PaypalActionCancel:
		err = paypal.CancelSubscription(p.PayerId)
	}

	return nil, err
}
<commit_msg>payments: listen to more paypal webhooks<commit_after>package payment

import (
	"encoding/json"
	"errors"
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"
	"socialapi/workers/payment/paypal"
	"socialapi/workers/payment/stripe"
	"time"

	"github.com/koding/logging"
)

var (
	ProviderNotFound       = errors.New("provider not found")
	ProviderNotImplemented = errors.New("provider not implemented")
	Log                    = logging.NewLogger("payment")
)

//----------------------------------------------------------
// SubscribeRequest
//----------------------------------------------------------

type SubscribeRequest struct {
	AccountId, Token, Email           string
	Provider, PlanTitle, PlanInterval string
}

func (s *SubscribeRequest) Do() (interface{}, error) {
	var err error

	switch s.Provider {
	case "stripe":
		err = stripe.Subscribe(
			s.Token, s.AccountId, s.Email, s.PlanTitle, s.PlanInterval,
		)
	case "paypal":
		err = paypal.SubscribeWithPlan(s.Token, s.AccountId, s.PlanTitle, s.PlanInterval)
	default:
		err = ProviderNotFound
	}

	if err != nil {
		Log.Error(
			"Subscribing account: %s to plan: %s failed. %s",
			s.AccountId, s.PlanTitle, err,
		)
	}

	return nil, err
}

//----------------------------------------------------------
// AccountRequest
//----------------------------------------------------------

type AccountRequest struct {
	AccountId string
}

type SubscriptionsResponse struct {
	AccountId          string    `json:"accountId"`
	PlanTitle          string    `json:"planTitle"`
	PlanInterval       string    `json:"planInterval"`
	State              string    `json:"state"`
	Provider           string    `json:"provider"`
	CurrentPeriodStart time.Time `json:"currentPeriodStart"`
	CurrentPeriodEnd   time.Time `json:"currentPeriodEnd"`
}

// Subscriptions return given `account_id` subscription if it exists.
// In case of no customer, or no subscriptions or no plan found, it
// returns the default plan as subscription.
func (a *AccountRequest) Subscriptions() (*SubscriptionsResponse, error) {
	if a.AccountId == "" {
		return nil, paymenterrors.ErrAccountIdIsNotSet
	}

	defaultResp := &SubscriptionsResponse{
		AccountId:    a.AccountId,
		PlanTitle:    "free",
		PlanInterval: "month",
		State:        "active",
		Provider:     "koding",
	}

	customer, err := stripe.FindCustomerByOldId(a.AccountId)
	if err != nil {
		return defaultResp, nil
	}

	subscriptions, err := stripe.FindCustomerSubscriptions(customer)
	if err != nil {
		return defaultResp, nil
	}

	if len(subscriptions) == 0 {
		return defaultResp, nil
	}

	currentSubscription := subscriptions[0]

	// cancel implies user took the action after satisfying provider limits,
	// therefore we return `free` plan for them
	if currentSubscription.State == paymentmodels.SubscriptionStateCanceled {
		return defaultResp, nil
	}

	plan := &paymentmodels.Plan{}
	err = plan.ById(currentSubscription.PlanId)
	if err != nil {
		return defaultResp, nil
	}

	resp := &SubscriptionsResponse{
		AccountId:          a.AccountId,
		PlanTitle:          plan.Title,
		PlanInterval:       plan.Interval,
		CurrentPeriodStart: currentSubscription.CurrentPeriodStart,
		CurrentPeriodEnd:   currentSubscription.CurrentPeriodEnd,
		State:              currentSubscription.State,
		Provider:           currentSubscription.Provider,
	}

	return resp, nil
}

func (a *AccountRequest) Invoices() ([]*stripe.StripeInvoiceResponse, error) {
	invoices, err := stripe.FindInvoicesForCustomer(a.AccountId)
	if err != nil && err != paymenterrors.ErrCustomerNotFound {
		Log.Error("Fetching invoices for account: %s failed. %s", a.AccountId, err)
	}

	return invoices, err
}

func (a *AccountRequest) CreditCard() (*stripe.CreditCardResponse, error) {
	resp, err := stripe.GetCreditCard(a.AccountId)
	if err != nil && err != paymenterrors.ErrCustomerNotFound {
		Log.Error("Fetching cc for account: %s failed. %s", a.AccountId, err)
	}

	return resp, err
}

func (a *AccountRequest) Delete() (interface{}, error) {
	err := stripe.DeleteCustomer(a.AccountId)
	if err != nil {
		Log.Error("Deleting account: %s failed. %s", a.AccountId, err)
	}

	return nil, err
}

//----------------------------------------------------------
// UpdateCreditCard
//----------------------------------------------------------

type UpdateCreditCardRequest struct {
	AccountId, Provider, Token string
}

func (u *UpdateCreditCardRequest) Do() (interface{}, error) {
	switch u.Provider {
	case "stripe":
		err := stripe.UpdateCreditCard(u.AccountId, u.Token)
		if err != nil {
			Log.Error("Updating cc for account: %s failed. %s", u.AccountId, err)
		}

		return nil, err
	case "paypal":
		return nil, ProviderNotImplemented
	default:
		return nil, ProviderNotFound
	}
}

//----------------------------------------------------------
// StripeWebhook
//----------------------------------------------------------

type StripeWebhook struct {
	Name     string `json:"type"`
	Created  int    `json:"created"`
	Livemode bool   `json:"livemode"`
	Id       string `json:"id"`
	Data     struct {
		Object interface{} `json:"object"`
	} `json:"data"`
}

func (s *StripeWebhook) Do() (interface{}, error) {
	var err error

	if !s.Livemode {
		Log.Error("Received test Stripe webhook: %v", s)
		return nil, nil
	}

	raw, err := json.Marshal(s.Data.Object)
	if err != nil {
		Log.Error("Error marshalling Stripe webhook '%v' : %v", s, err)
		return nil, err
	}

	switch s.Name {
	case "customer.subscription.deleted":
		err = stripe.SubscriptionDeletedWebhook(raw)
	case "invoice.created":
		err = stripe.InvoiceCreatedWebhook(raw)
	case "customer.deleted":
		err = stripe.CustomerDeletedWebhook(raw)
	}

	if err != nil {
		Log.Error("Error handling Stripe webhook '%v' : %v", s, err)
	}

	return nil, err
}

//----------------------------------------------------------
// Paypal
//----------------------------------------------------------

type PaypalRequest struct {
	Token     string `json:"token"`
	AccountId string `json:"accountId"`
}

func (p *PaypalRequest) Success() (interface{}, error) {
	return nil, paypal.Subscribe(p.Token, p.AccountId)
}

func (p *PaypalRequest) Cancel() (interface{}, error) {
	return nil, nil
}

type PaypalGetTokenRequest struct {
	PlanTitle    string `json:"planTitle"`
	PlanInterval string `json:"planInterval"`
}

func (p *PaypalGetTokenRequest) Do() (interface{}, error) {
	return paypal.GetToken(p.PlanTitle, p.PlanInterval)
}

//----------------------------------------------------------
// Webhook
//----------------------------------------------------------

type PaypalWebhook struct {
	TransactionType string `json:"txn_type"`
	Status          string `json:"payment_status"`
	PayerId         string `json:"payer_id"`
}

var PaypalActionCancel = "cancel"

var PaypalStatusActionMap = map[string]string{
	"Denied":   PaypalActionCancel,
	"Expired":  PaypalActionCancel,
	"Failed":   PaypalActionCancel,
	"Reversed": PaypalActionCancel,
	"Voided":   PaypalActionCancel,
}

var PaypalTransactionActionMap = map[string]string{
	"subscr_cancel": PaypalActionCancel,
	"subscr_eot":    PaypalActionCancel,
}

func (p *PaypalWebhook) Do() (interface{}, error) {
	action, ok := PaypalStatusActionMap[p.Status]
	if !ok {
		action, ok = PaypalTransactionActionMap[p.TransactionType]
		if !ok {
			return nil, nil
		}
	}

	var err error

	switch action {
	case PaypalActionCancel:
		err = paypal.CancelSubscription(p.PayerId)
	}

	return nil, err
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import (
	"context"
	"reflect"
	"strconv"

	"github.com/arangodb/go-driver/util"
)

const (
	keyRevision      = "arangodb-revision"
	keyRevisions     = "arangodb-revisions"
	keyReturnNew     = "arangodb-returnNew"
	keyReturnOld     = "arangodb-returnOld"
	keySilent        = "arangodb-silent"
	keyWaitForSync   = "arangodb-waitForSync"
	keyDetails       = "arangodb-details"
	keyKeepNull      = "arangodb-keepNull"
	keyMergeObjects  = "arangodb-mergeObjects"
	keyRawResponse   = "arangodb-rawResponse"
	keyImportDetails = "arangodb-importDetails"
	keyResponse      = "arangodb-response"
	keyEndpoint      = "arangodb-endpoint"
)

// WithRevision is used to configure a context to make document
// functions specify an explicit revision of the document using an `If-Match` condition.
func WithRevision(parent context.Context, revision string) context.Context {
	return context.WithValue(contextOrBackground(parent), keyRevision, revision)
}

// WithRevisions is used to configure a context to make multi-document
// functions specify explicit revisions of the documents.
func WithRevisions(parent context.Context, revisions []string) context.Context {
	return context.WithValue(contextOrBackground(parent), keyRevisions, revisions)
}

// WithReturnNew is used to configure a context to make create, update & replace document
// functions return the new document into the given result.
func WithReturnNew(parent context.Context, result interface{}) context.Context {
	return context.WithValue(contextOrBackground(parent), keyReturnNew, result)
}

// WithReturnOld is used to configure a context to make update & replace document
// functions return the old document into the given result.
func WithReturnOld(parent context.Context, result interface{}) context.Context {
	return context.WithValue(contextOrBackground(parent), keyReturnOld, result)
}

// WithDetails is used to configure a context to make Client.Version return additional details.
// You can pass a single (optional) boolean. If that is set to false, you explicitly ask to not provide details.
func WithDetails(parent context.Context, value ...bool) context.Context {
	v := true
	if len(value) == 1 {
		v = value[0]
	}
	return context.WithValue(contextOrBackground(parent), keyDetails, v)
}

// WithEndpoint is used to configure a context that forces a request to be executed on a specific endpoint.
// If you specify an endpoint like this, failover is disabled.
// If you specify an unknown endpoint, an InvalidArgumentError is returned from requests.
func WithEndpoint(parent context.Context, endpoint string) context.Context {
	endpoint = util.FixupEndpointURLScheme(endpoint)
	return context.WithValue(contextOrBackground(parent), keyEndpoint, endpoint)
}

// WithKeepNull is used to configure a context to make update functions keep null fields (value==true)
// or remove fields with null values (value==false).
func WithKeepNull(parent context.Context, value bool) context.Context {
	return context.WithValue(contextOrBackground(parent), keyKeepNull, value)
}

// WithMergeObjects is used to configure a context to make update functions merge objects present in both
// the existing document and the patch document (value==true) or overwrite objects in the existing document
// with objects found in the patch document (value==false)
func WithMergeObjects(parent context.Context, value bool) context.Context {
	return context.WithValue(contextOrBackground(parent), keyMergeObjects, value)
}

// WithSilent is used to configure a context to make functions return an empty result (silent==true),
// instead of a metadata result (silent==false, default).
// You can pass a single (optional) boolean. If that is set to false, you explicitly ask to return metadata result.
func WithSilent(parent context.Context, value ...bool) context.Context {
	v := true
	if len(value) == 1 {
		v = value[0]
	}
	return context.WithValue(contextOrBackground(parent), keySilent, v)
}

// WithWaitForSync is used to configure a context to make modification
// functions wait until the data has been synced to disk (or not).
// You can pass a single (optional) boolean. If that is set to false, you explicitly do not wait for
// data to be synced to disk.
func WithWaitForSync(parent context.Context, value ...bool) context.Context {
	v := true
	if len(value) == 1 {
		v = value[0]
	}
	return context.WithValue(contextOrBackground(parent), keyWaitForSync, v)
}

// WithRawResponse is used to configure a context that will make all functions store the raw response into a
// buffer.
func WithRawResponse(parent context.Context, value *[]byte) context.Context {
	return context.WithValue(contextOrBackground(parent), keyRawResponse, value)
}

// WithResponse is used to configure a context that will make all functions store the response into the given value.
func WithResponse(parent context.Context, value *Response) context.Context {
	return context.WithValue(contextOrBackground(parent), keyResponse, value)
}

// WithImportDetails is used to configure a context that will make import document requests return
// details about documents that could not be imported.
func WithImportDetails(parent context.Context, value *[]string) context.Context {
	return context.WithValue(contextOrBackground(parent), keyImportDetails, value)
}

type contextSettings struct {
	Silent        bool
	WaitForSync   bool
	ReturnOld     interface{}
	ReturnNew     interface{}
	Revision      string
	Revisions     []string
	ImportDetails *[]string
}

// applyContextSettings returns the settings configured in the context in the given request.
// It then returns information about the applied settings that may be needed later in API implementation functions.
func applyContextSettings(ctx context.Context, req Request) contextSettings {
	result := contextSettings{}
	if ctx == nil {
		return result
	}
	// Details
	if v := ctx.Value(keyDetails); v != nil {
		if details, ok := v.(bool); ok {
			req.SetQuery("details", strconv.FormatBool(details))
		}
	}
	// KeepNull
	if v := ctx.Value(keyKeepNull); v != nil {
		if keepNull, ok := v.(bool); ok {
			req.SetQuery("keepNull", strconv.FormatBool(keepNull))
		}
	}
	// MergeObjects
	if v := ctx.Value(keyMergeObjects); v != nil {
		if mergeObjects, ok := v.(bool); ok {
			req.SetQuery("mergeObjects", strconv.FormatBool(mergeObjects))
		}
	}
	// Silent
	if v := ctx.Value(keySilent); v != nil {
		if silent, ok := v.(bool); ok {
			req.SetQuery("silent", strconv.FormatBool(silent))
			result.Silent = silent
		}
	}
	// WaitForSync
	if v := ctx.Value(keyWaitForSync); v != nil {
		if waitForSync, ok := v.(bool); ok {
			req.SetQuery("waitForSync", strconv.FormatBool(waitForSync))
			result.WaitForSync = waitForSync
		}
	}
	// ReturnOld
	if v := ctx.Value(keyReturnOld); v != nil {
		req.SetQuery("returnOld", "true")
		result.ReturnOld = v
	}
	// ReturnNew
	if v := ctx.Value(keyReturnNew); v != nil {
		req.SetQuery("returnNew", "true")
		result.ReturnNew = v
	}
	// If-Match
	if v := ctx.Value(keyRevision); v != nil {
		if rev, ok := v.(string); ok {
			req.SetHeader("If-Match", rev)
			result.Revision = rev
		}
	}
	// Revisions
	if v := ctx.Value(keyRevisions); v != nil {
		if revs, ok := v.([]string); ok {
			req.SetQuery("ignoreRevs", "false")
			result.Revisions = revs
		}
	}
	// ImportDetails
	if v := ctx.Value(keyImportDetails); v != nil {
		if details, ok := v.(*[]string); ok {
			req.SetQuery("details", "true")
			result.ImportDetails = details
		}
	}
	return result
}

// okStatus returns one of the given status codes depending on the WaitForSync field value.
// If WaitForSync==true, statusWithWaitForSync is returned, otherwise statusWithoutWaitForSync is returned.
func (cs contextSettings) okStatus(statusWithWaitForSync, statusWithoutWaitForSync int) int {
	if cs.WaitForSync {
		return statusWithWaitForSync
	} else {
		return statusWithoutWaitForSync
	}
}

// contextOrBackground returns the given context if it is not nil.
// Returns context.Background() otherwise.
func contextOrBackground(ctx context.Context) context.Context {
	if ctx != nil {
		return ctx
	}
	return context.Background()
}

// withDocumentAt returns a context derived from the given parent context to be used in multi-document options
// that needs a client side "loop" implementation.
// It handle:
// - WithRevisions
// - WithReturnNew
// - WithReturnOld
func withDocumentAt(ctx context.Context, index int) (context.Context, error) {
	if ctx == nil {
		return nil, nil
	}
	// Revisions
	if v := ctx.Value(keyRevisions); v != nil {
		if revs, ok := v.([]string); ok {
			if index >= len(revs) {
				return nil, WithStack(InvalidArgumentError{Message: "Index out of range: revisions"})
			}
			ctx = WithRevision(ctx, revs[index])
		}
	}
	// ReturnOld
	if v := ctx.Value(keyReturnOld); v != nil {
		val := reflect.ValueOf(v)
		ctx = WithReturnOld(ctx, val.Index(index).Interface())
	}
	// ReturnNew
	if v := ctx.Value(keyReturnNew); v != nil {
		val := reflect.ValueOf(v)
		ctx = WithReturnNew(ctx, val.Index(index).Interface())
	}

	return ctx, nil
}
<commit_msg>Added WithIsRestore (not internal for normal clien use!!!!)<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import (
	"context"
	"reflect"
	"strconv"

	"github.com/arangodb/go-driver/util"
)

const (
	keyRevision      = "arangodb-revision"
	keyRevisions     = "arangodb-revisions"
	keyReturnNew     = "arangodb-returnNew"
	keyReturnOld     = "arangodb-returnOld"
	keySilent        = "arangodb-silent"
	keyWaitForSync   = "arangodb-waitForSync"
	keyDetails       = "arangodb-details"
	keyKeepNull      = "arangodb-keepNull"
	keyMergeObjects  = "arangodb-mergeObjects"
	keyRawResponse   = "arangodb-rawResponse"
	keyImportDetails = "arangodb-importDetails"
	keyResponse      = "arangodb-response"
	keyEndpoint      = "arangodb-endpoint"
	keyIsRestore     = "arangodb-isRestore"
)

// WithRevision is used to configure a context to make document
// functions specify an explicit revision of the document using an `If-Match` condition.
func WithRevision(parent context.Context, revision string) context.Context {
	return context.WithValue(contextOrBackground(parent), keyRevision, revision)
}

// WithRevisions is used to configure a context to make multi-document
// functions specify explicit revisions of the documents.
func WithRevisions(parent context.Context, revisions []string) context.Context {
	return context.WithValue(contextOrBackground(parent), keyRevisions, revisions)
}

// WithReturnNew is used to configure a context to make create, update & replace document
// functions return the new document into the given result.
func WithReturnNew(parent context.Context, result interface{}) context.Context {
	return context.WithValue(contextOrBackground(parent), keyReturnNew, result)
}

// WithReturnOld is used to configure a context to make update & replace document
// functions return the old document into the given result.
func WithReturnOld(parent context.Context, result interface{}) context.Context {
	return context.WithValue(contextOrBackground(parent), keyReturnOld, result)
}

// WithDetails is used to configure a context to make Client.Version return additional details.
// You can pass a single (optional) boolean. If that is set to false, you explicitly ask to not provide details.
func WithDetails(parent context.Context, value ...bool) context.Context {
	v := true
	if len(value) == 1 {
		v = value[0]
	}
	return context.WithValue(contextOrBackground(parent), keyDetails, v)
}

// WithEndpoint is used to configure a context that forces a request to be executed on a specific endpoint.
// If you specify an endpoint like this, failover is disabled.
// If you specify an unknown endpoint, an InvalidArgumentError is returned from requests.
func WithEndpoint(parent context.Context, endpoint string) context.Context {
	endpoint = util.FixupEndpointURLScheme(endpoint)
	return context.WithValue(contextOrBackground(parent), keyEndpoint, endpoint)
}

// WithKeepNull is used to configure a context to make update functions keep null fields (value==true)
// or remove fields with null values (value==false).
func WithKeepNull(parent context.Context, value bool) context.Context {
	return context.WithValue(contextOrBackground(parent), keyKeepNull, value)
}

// WithMergeObjects is used to configure a context to make update functions merge objects present in both
// the existing document and the patch document (value==true) or overwrite objects in the existing document
// with objects found in the patch document (value==false)
func WithMergeObjects(parent context.Context, value bool) context.Context {
	return context.WithValue(contextOrBackground(parent), keyMergeObjects, value)
}

// WithSilent is used to configure a context to make functions return an empty result (silent==true),
// instead of a metadata result (silent==false, default).
// You can pass a single (optional) boolean. If that is set to false, you explicitly ask to return metadata result.
func WithSilent(parent context.Context, value ...bool) context.Context {
	v := true
	if len(value) == 1 {
		v = value[0]
	}
	return context.WithValue(contextOrBackground(parent), keySilent, v)
}

// WithWaitForSync is used to configure a context to make modification
// functions wait until the data has been synced to disk (or not).
// You can pass a single (optional) boolean. If that is set to false, you explicitly do not wait for
// data to be synced to disk.
func WithWaitForSync(parent context.Context, value ...bool) context.Context {
	v := true
	if len(value) == 1 {
		v = value[0]
	}
	return context.WithValue(contextOrBackground(parent), keyWaitForSync, v)
}

// WithRawResponse is used to configure a context that will make all functions store the raw response into a
// buffer.
func WithRawResponse(parent context.Context, value *[]byte) context.Context {
	return context.WithValue(contextOrBackground(parent), keyRawResponse, value)
}

// WithResponse is used to configure a context that will make all functions store the response into the given value.
func WithResponse(parent context.Context, value *Response) context.Context {
	return context.WithValue(contextOrBackground(parent), keyResponse, value)
}

// WithImportDetails is used to configure a context that will make import document requests return
// details about documents that could not be imported.
func WithImportDetails(parent context.Context, value *[]string) context.Context {
	return context.WithValue(contextOrBackground(parent), keyImportDetails, value)
}

// WithIsRestore is used to configure a context to make insert functions use the "isRestore=<value>"
// setting.
// Note: This function is intended for internal (replication) use. It is NOT intended to
// be used by normal client. This CAN screw up your database.
func WithIsRestore(parent context.Context, value bool) context.Context {
	return context.WithValue(contextOrBackground(parent), keyIsRestore, value)
}

type contextSettings struct {
	Silent        bool
	WaitForSync   bool
	ReturnOld     interface{}
	ReturnNew     interface{}
	Revision      string
	Revisions     []string
	ImportDetails *[]string
	IsRestore     bool
}

// applyContextSettings returns the settings configured in the context in the given request.
// It then returns information about the applied settings that may be needed later in API implementation functions.
func applyContextSettings(ctx context.Context, req Request) contextSettings {
	result := contextSettings{}
	if ctx == nil {
		return result
	}
	// Details
	if v := ctx.Value(keyDetails); v != nil {
		if details, ok := v.(bool); ok {
			req.SetQuery("details", strconv.FormatBool(details))
		}
	}
	// KeepNull
	if v := ctx.Value(keyKeepNull); v != nil {
		if keepNull, ok := v.(bool); ok {
			req.SetQuery("keepNull", strconv.FormatBool(keepNull))
		}
	}
	// MergeObjects
	if v := ctx.Value(keyMergeObjects); v != nil {
		if mergeObjects, ok := v.(bool); ok {
			req.SetQuery("mergeObjects", strconv.FormatBool(mergeObjects))
		}
	}
	// Silent
	if v := ctx.Value(keySilent); v != nil {
		if silent, ok := v.(bool); ok {
			req.SetQuery("silent", strconv.FormatBool(silent))
			result.Silent = silent
		}
	}
	// WaitForSync
	if v := ctx.Value(keyWaitForSync); v != nil {
		if waitForSync, ok := v.(bool); ok {
			req.SetQuery("waitForSync", strconv.FormatBool(waitForSync))
			result.WaitForSync = waitForSync
		}
	}
	// ReturnOld
	if v := ctx.Value(keyReturnOld); v != nil {
		req.SetQuery("returnOld", "true")
		result.ReturnOld = v
	}
	// ReturnNew
	if v := ctx.Value(keyReturnNew); v != nil {
		req.SetQuery("returnNew", "true")
		result.ReturnNew = v
	}
	// If-Match
	if v := ctx.Value(keyRevision); v != nil {
		if rev, ok := v.(string); ok {
			req.SetHeader("If-Match", rev)
			result.Revision = rev
		}
	}
	// Revisions
	if v := ctx.Value(keyRevisions); v != nil {
		if revs, ok := v.([]string); ok {
			req.SetQuery("ignoreRevs", "false")
			result.Revisions = revs
		}
	}
	// ImportDetails
	if v := ctx.Value(keyImportDetails); v != nil {
		if details, ok := v.(*[]string); ok {
			req.SetQuery("details", "true")
			result.ImportDetails = details
		}
	}
	// IsRestore
	if v := ctx.Value(keyIsRestore); v != nil {
		if isRestore, ok := v.(bool); ok {
			req.SetQuery("isRestore", strconv.FormatBool(isRestore))
			result.IsRestore = isRestore
		}
	}
	return result
}

// okStatus returns one of the given status codes depending on the WaitForSync field value.
// If WaitForSync==true, statusWithWaitForSync is returned, otherwise statusWithoutWaitForSync is returned.
func (cs contextSettings) okStatus(statusWithWaitForSync, statusWithoutWaitForSync int) int {
	if cs.WaitForSync {
		return statusWithWaitForSync
	} else {
		return statusWithoutWaitForSync
	}
}

// contextOrBackground returns the given context if it is not nil.
// Returns context.Background() otherwise.
func contextOrBackground(ctx context.Context) context.Context {
	if ctx != nil {
		return ctx
	}
	return context.Background()
}

// withDocumentAt returns a context derived from the given parent context to be used in multi-document options
// that needs a client side "loop" implementation.
// It handle:
// - WithRevisions
// - WithReturnNew
// - WithReturnOld
func withDocumentAt(ctx context.Context, index int) (context.Context, error) {
	if ctx == nil {
		return nil, nil
	}
	// Revisions
	if v := ctx.Value(keyRevisions); v != nil {
		if revs, ok := v.([]string); ok {
			if index >= len(revs) {
				return nil, WithStack(InvalidArgumentError{Message: "Index out of range: revisions"})
			}
			ctx = WithRevision(ctx, revs[index])
		}
	}
	// ReturnOld
	if v := ctx.Value(keyReturnOld); v != nil {
		val := reflect.ValueOf(v)
		ctx = WithReturnOld(ctx, val.Index(index).Interface())
	}
	// ReturnNew
	if v := ctx.Value(keyReturnNew); v != nil {
		val := reflect.ValueOf(v)
		ctx = WithReturnNew(ctx, val.Index(index).Interface())
	}

	return ctx, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Vitess 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 planbuilder

import (
	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vtgate/semantics"
)

type queryProjection struct {
	/*
		select id1, id3, count(id2) as iddd from... group by id3, id1 order by id1, id3
		select id1, id3, count(id2) as iddd from... group by id3, id1 order by id1, id1

		exprs : id1, id3
		aggr : count(id2)
		grouping : id3, id1
		ordering: id1, id3
	*/

	selectExprs             []*sqlparser.AliasedExpr
	aggrExprs               []*sqlparser.AliasedExpr
	groupOrderingCommonExpr map[sqlparser.Expr]*sqlparser.Order

	//groupExprs  sqlparser.GroupBy

	orderExprs sqlparser.OrderBy

	// orderExprColMap keeps a map between the Order object and the offset into the select expressions list
	orderExprColMap map[*sqlparser.Order]int
}

func newQueryProjection() *queryProjection {
	return &queryProjection{
		groupOrderingCommonExpr: map[sqlparser.Expr]*sqlparser.Order{},
		orderExprColMap:         map[*sqlparser.Order]int{},
	}
}

func createQPFromSelect(sel *sqlparser.Select) (*queryProjection, error) {
	qp := newQueryProjection()

	for _, selExp := range sel.SelectExprs {
		exp, ok := selExp.(*sqlparser.AliasedExpr)
		if !ok {
			return nil, semantics.Gen4NotSupportedF("%T in select list", selExp)
		}
		fExpr, ok := exp.Expr.(*sqlparser.FuncExpr)
		if ok && fExpr.IsAggregate() {
			if len(fExpr.Exprs) != 1 {
				return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.SyntaxError, "multiple arguments inside the function '%s'", sqlparser.String(fExpr))
			}
			qp.aggrExprs = append(qp.aggrExprs, exp)
			continue
		}
		if nodeHasAggregates(exp.Expr) {
			return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: complex aggregate expression")
		}
		qp.selectExprs = append(qp.selectExprs, exp)
	}

	qp.orderExprs = sel.OrderBy

	allExpr := append(qp.selectExprs, qp.aggrExprs...)
	for _, order := range sel.OrderBy {
		for offset, expr := range allExpr {
			if sqlparser.EqualsExpr(order.Expr, expr.Expr) {
				qp.orderExprColMap[order] = offset
				break
			}
			// TODO: handle alias and column offset
		}
	}

	if sel.GroupBy == nil || sel.OrderBy == nil {
		return qp, nil
	}

	//for _, exp := range sel.GroupBy {
	//	for _, order := range sel.OrderBy {
	//		if sqlparser.EqualsExpr(exp, order.Expr) {
	//			qp.groupOrderingCommonExpr[exp] = order
	//			break
	//		}
	//	}
	//}

	return qp, nil
}
<commit_msg>clean up<commit_after>/*
Copyright 2021 The Vitess 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 planbuilder

import (
	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vtgate/semantics"
)

type queryProjection struct {
	selectExprs             []*sqlparser.AliasedExpr
	aggrExprs               []*sqlparser.AliasedExpr
	groupOrderingCommonExpr map[sqlparser.Expr]*sqlparser.Order

	orderExprs sqlparser.OrderBy

	// orderExprColMap keeps a map between the Order object and the offset into the select expressions list
	orderExprColMap map[*sqlparser.Order]int
}

func newQueryProjection() *queryProjection {
	return &queryProjection{
		groupOrderingCommonExpr: map[sqlparser.Expr]*sqlparser.Order{},
		orderExprColMap:         map[*sqlparser.Order]int{},
	}
}

func createQPFromSelect(sel *sqlparser.Select) (*queryProjection, error) {
	qp := newQueryProjection()

	for _, selExp := range sel.SelectExprs {
		exp, ok := selExp.(*sqlparser.AliasedExpr)
		if !ok {
			return nil, semantics.Gen4NotSupportedF("%T in select list", selExp)
		}
		fExpr, ok := exp.Expr.(*sqlparser.FuncExpr)
		if ok && fExpr.IsAggregate() {
			if len(fExpr.Exprs) != 1 {
				return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.SyntaxError, "multiple arguments inside the function '%s'", sqlparser.String(fExpr))
			}
			qp.aggrExprs = append(qp.aggrExprs, exp)
			continue
		}
		if nodeHasAggregates(exp.Expr) {
			return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: complex aggregate expression")
		}
		qp.selectExprs = append(qp.selectExprs, exp)
	}

	qp.orderExprs = sel.OrderBy

	allExpr := append(qp.selectExprs, qp.aggrExprs...)
	for _, order := range sel.OrderBy {
		for offset, expr := range allExpr {
			if sqlparser.EqualsExpr(order.Expr, expr.Expr) {
				qp.orderExprColMap[order] = offset
				break
			}
			// TODO: handle alias and column offset
		}
	}

	if sel.GroupBy == nil || sel.OrderBy == nil {
		return qp, nil
	}

	return qp, nil
}
<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import "reflect"

func GetAccessContextManagerAccessLevelCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
	name, err := assetName(d, config, "//accesscontextmanager.googleapis.com/{{name}}")
	if err != nil {
		return Asset{}, err
	}
	if obj, err := GetAccessContextManagerAccessLevelApiObject(d, config); err == nil {
		return Asset{
			Name: name,
			Type: "accesscontextmanager.googleapis.com/AccessLevel",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/accesscontextmanager/v1/rest",
				DiscoveryName:        "AccessLevel",
				Data:                 obj,
			},
		}, nil
	} else {
		return Asset{}, err
	}
}

func GetAccessContextManagerAccessLevelApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	titleProp, err := expandAccessContextManagerAccessLevelTitle(d.Get("title"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("title"); !isEmptyValue(reflect.ValueOf(titleProp)) && (ok || !reflect.DeepEqual(v, titleProp)) {
		obj["title"] = titleProp
	}
	descriptionProp, err := expandAccessContextManagerAccessLevelDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	basicProp, err := expandAccessContextManagerAccessLevelBasic(d.Get("basic"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("basic"); !isEmptyValue(reflect.ValueOf(basicProp)) && (ok || !reflect.DeepEqual(v, basicProp)) {
		obj["basic"] = basicProp
	}
	parentProp, err := expandAccessContextManagerAccessLevelParent(d.Get("parent"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("parent"); !isEmptyValue(reflect.ValueOf(parentProp)) && (ok || !reflect.DeepEqual(v, parentProp)) {
		obj["parent"] = parentProp
	}
	nameProp, err := expandAccessContextManagerAccessLevelName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}

	return resourceAccessContextManagerAccessLevelEncoder(d, config, obj)
}

func resourceAccessContextManagerAccessLevelEncoder(d TerraformResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
	delete(obj, "parent")
	return obj, nil
}

func expandAccessContextManagerAccessLevelTitle(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasic(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedCombiningFunction, err := expandAccessContextManagerAccessLevelBasicCombiningFunction(original["combining_function"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedCombiningFunction); val.IsValid() && !isEmptyValue(val) {
		transformed["combiningFunction"] = transformedCombiningFunction
	}

	transformedConditions, err := expandAccessContextManagerAccessLevelBasicConditions(original["conditions"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedConditions); val.IsValid() && !isEmptyValue(val) {
		transformed["conditions"] = transformedConditions
	}

	return transformed, nil
}

func expandAccessContextManagerAccessLevelBasicCombiningFunction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedIpSubnetworks, err := expandAccessContextManagerAccessLevelBasicConditionsIpSubnetworks(original["ip_subnetworks"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedIpSubnetworks); val.IsValid() && !isEmptyValue(val) {
			transformed["ipSubnetworks"] = transformedIpSubnetworks
		}

		transformedRequiredAccessLevels, err := expandAccessContextManagerAccessLevelBasicConditionsRequiredAccessLevels(original["required_access_levels"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedRequiredAccessLevels); val.IsValid() && !isEmptyValue(val) {
			transformed["requiredAccessLevels"] = transformedRequiredAccessLevels
		}

		transformedMembers, err := expandAccessContextManagerAccessLevelBasicConditionsMembers(original["members"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedMembers); val.IsValid() && !isEmptyValue(val) {
			transformed["members"] = transformedMembers
		}

		transformedNegate, err := expandAccessContextManagerAccessLevelBasicConditionsNegate(original["negate"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedNegate); val.IsValid() && !isEmptyValue(val) {
			transformed["negate"] = transformedNegate
		}

		transformedDevicePolicy, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicy(original["device_policy"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedDevicePolicy); val.IsValid() && !isEmptyValue(val) {
			transformed["devicePolicy"] = transformedDevicePolicy
		}

		transformedRegions, err := expandAccessContextManagerAccessLevelBasicConditionsRegions(original["regions"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedRegions); val.IsValid() && !isEmptyValue(val) {
			transformed["regions"] = transformedRegions
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsIpSubnetworks(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsRequiredAccessLevels(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsMembers(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsNegate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedRequireScreenLock, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireScreenLock(original["require_screen_lock"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedRequireScreenLock); val.IsValid() && !isEmptyValue(val) {
		transformed["requireScreenLock"] = transformedRequireScreenLock
	}

	transformedAllowedEncryptionStatuses, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedEncryptionStatuses(original["allowed_encryption_statuses"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedAllowedEncryptionStatuses); val.IsValid() && !isEmptyValue(val) {
		transformed["allowedEncryptionStatuses"] = transformedAllowedEncryptionStatuses
	}

	transformedAllowedDeviceManagementLevels, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedDeviceManagementLevels(original["allowed_device_management_levels"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedAllowedDeviceManagementLevels); val.IsValid() && !isEmptyValue(val) {
		transformed["allowedDeviceManagementLevels"] = transformedAllowedDeviceManagementLevels
	}

	transformedOsConstraints, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraints(original["os_constraints"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedOsConstraints); val.IsValid() && !isEmptyValue(val) {
		transformed["osConstraints"] = transformedOsConstraints
	}

	transformedRequireAdminApproval, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireAdminApproval(original["require_admin_approval"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedRequireAdminApproval); val.IsValid() && !isEmptyValue(val) {
		transformed["requireAdminApproval"] = transformedRequireAdminApproval
	}

	transformedRequireCorpOwned, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireCorpOwned(original["require_corp_owned"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedRequireCorpOwned); val.IsValid() && !isEmptyValue(val) {
		transformed["requireCorpOwned"] = transformedRequireCorpOwned
	}

	return transformed, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireScreenLock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedEncryptionStatuses(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedDeviceManagementLevels(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraints(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedMinimumVersion, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsMinimumVersion(original["minimum_version"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedMinimumVersion); val.IsValid() && !isEmptyValue(val) {
			transformed["minimumVersion"] = transformedMinimumVersion
		}

		transformedOsType, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsOsType(original["os_type"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedOsType); val.IsValid() && !isEmptyValue(val) {
			transformed["osType"] = transformedOsType
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsMinimumVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsOsType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireAdminApproval(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireCorpOwned(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsRegions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelParent(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}
<commit_msg>Add different api name for screen lock (#3433) (#428)<commit_after>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import "reflect"

func GetAccessContextManagerAccessLevelCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
	name, err := assetName(d, config, "//accesscontextmanager.googleapis.com/{{name}}")
	if err != nil {
		return Asset{}, err
	}
	if obj, err := GetAccessContextManagerAccessLevelApiObject(d, config); err == nil {
		return Asset{
			Name: name,
			Type: "accesscontextmanager.googleapis.com/AccessLevel",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/accesscontextmanager/v1/rest",
				DiscoveryName:        "AccessLevel",
				Data:                 obj,
			},
		}, nil
	} else {
		return Asset{}, err
	}
}

func GetAccessContextManagerAccessLevelApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	titleProp, err := expandAccessContextManagerAccessLevelTitle(d.Get("title"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("title"); !isEmptyValue(reflect.ValueOf(titleProp)) && (ok || !reflect.DeepEqual(v, titleProp)) {
		obj["title"] = titleProp
	}
	descriptionProp, err := expandAccessContextManagerAccessLevelDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	basicProp, err := expandAccessContextManagerAccessLevelBasic(d.Get("basic"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("basic"); !isEmptyValue(reflect.ValueOf(basicProp)) && (ok || !reflect.DeepEqual(v, basicProp)) {
		obj["basic"] = basicProp
	}
	parentProp, err := expandAccessContextManagerAccessLevelParent(d.Get("parent"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("parent"); !isEmptyValue(reflect.ValueOf(parentProp)) && (ok || !reflect.DeepEqual(v, parentProp)) {
		obj["parent"] = parentProp
	}
	nameProp, err := expandAccessContextManagerAccessLevelName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}

	return resourceAccessContextManagerAccessLevelEncoder(d, config, obj)
}

func resourceAccessContextManagerAccessLevelEncoder(d TerraformResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
	delete(obj, "parent")
	return obj, nil
}

func expandAccessContextManagerAccessLevelTitle(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasic(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedCombiningFunction, err := expandAccessContextManagerAccessLevelBasicCombiningFunction(original["combining_function"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedCombiningFunction); val.IsValid() && !isEmptyValue(val) {
		transformed["combiningFunction"] = transformedCombiningFunction
	}

	transformedConditions, err := expandAccessContextManagerAccessLevelBasicConditions(original["conditions"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedConditions); val.IsValid() && !isEmptyValue(val) {
		transformed["conditions"] = transformedConditions
	}

	return transformed, nil
}

func expandAccessContextManagerAccessLevelBasicCombiningFunction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedIpSubnetworks, err := expandAccessContextManagerAccessLevelBasicConditionsIpSubnetworks(original["ip_subnetworks"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedIpSubnetworks); val.IsValid() && !isEmptyValue(val) {
			transformed["ipSubnetworks"] = transformedIpSubnetworks
		}

		transformedRequiredAccessLevels, err := expandAccessContextManagerAccessLevelBasicConditionsRequiredAccessLevels(original["required_access_levels"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedRequiredAccessLevels); val.IsValid() && !isEmptyValue(val) {
			transformed["requiredAccessLevels"] = transformedRequiredAccessLevels
		}

		transformedMembers, err := expandAccessContextManagerAccessLevelBasicConditionsMembers(original["members"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedMembers); val.IsValid() && !isEmptyValue(val) {
			transformed["members"] = transformedMembers
		}

		transformedNegate, err := expandAccessContextManagerAccessLevelBasicConditionsNegate(original["negate"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedNegate); val.IsValid() && !isEmptyValue(val) {
			transformed["negate"] = transformedNegate
		}

		transformedDevicePolicy, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicy(original["device_policy"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedDevicePolicy); val.IsValid() && !isEmptyValue(val) {
			transformed["devicePolicy"] = transformedDevicePolicy
		}

		transformedRegions, err := expandAccessContextManagerAccessLevelBasicConditionsRegions(original["regions"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedRegions); val.IsValid() && !isEmptyValue(val) {
			transformed["regions"] = transformedRegions
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsIpSubnetworks(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsRequiredAccessLevels(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsMembers(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsNegate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedRequireScreenLock, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireScreenLock(original["require_screen_lock"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedRequireScreenLock); val.IsValid() && !isEmptyValue(val) {
		transformed["requireScreenlock"] = transformedRequireScreenLock
	}

	transformedAllowedEncryptionStatuses, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedEncryptionStatuses(original["allowed_encryption_statuses"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedAllowedEncryptionStatuses); val.IsValid() && !isEmptyValue(val) {
		transformed["allowedEncryptionStatuses"] = transformedAllowedEncryptionStatuses
	}

	transformedAllowedDeviceManagementLevels, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedDeviceManagementLevels(original["allowed_device_management_levels"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedAllowedDeviceManagementLevels); val.IsValid() && !isEmptyValue(val) {
		transformed["allowedDeviceManagementLevels"] = transformedAllowedDeviceManagementLevels
	}

	transformedOsConstraints, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraints(original["os_constraints"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedOsConstraints); val.IsValid() && !isEmptyValue(val) {
		transformed["osConstraints"] = transformedOsConstraints
	}

	transformedRequireAdminApproval, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireAdminApproval(original["require_admin_approval"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedRequireAdminApproval); val.IsValid() && !isEmptyValue(val) {
		transformed["requireAdminApproval"] = transformedRequireAdminApproval
	}

	transformedRequireCorpOwned, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireCorpOwned(original["require_corp_owned"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedRequireCorpOwned); val.IsValid() && !isEmptyValue(val) {
		transformed["requireCorpOwned"] = transformedRequireCorpOwned
	}

	return transformed, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireScreenLock(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedEncryptionStatuses(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyAllowedDeviceManagementLevels(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraints(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedMinimumVersion, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsMinimumVersion(original["minimum_version"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedMinimumVersion); val.IsValid() && !isEmptyValue(val) {
			transformed["minimumVersion"] = transformedMinimumVersion
		}

		transformedOsType, err := expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsOsType(original["os_type"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedOsType); val.IsValid() && !isEmptyValue(val) {
			transformed["osType"] = transformedOsType
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsMinimumVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyOsConstraintsOsType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireAdminApproval(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsDevicePolicyRequireCorpOwned(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelBasicConditionsRegions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelParent(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandAccessContextManagerAccessLevelName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}
<|endoftext|>


<commit_before>package goskeleton

import (
	"reflect"
	"github.com/codegangsta/inject"
	"github.com/BurntSushi/toml"
	"github.com/gin-gonic/gin"
	"git.oschina.net/zuobao/gozuobao/logger"
)

type Engine struct {
	*gin.Engine
	Injector inject.Injector
}

func isStruct(t reflect.Type) bool {
	for t != nil {
		switch t.Kind()  {
		case reflect.Struct:
			return true
		case reflect.Ptr:
			t = t.Elem()
		case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
			return false
		default:
			return false
		}
	}
	return false
}

// 递归注入
func recursiveInject(injector inject.Injector, value interface {}) error {

	var err error
	err = injector.Apply(value)
	if err != nil {
		logger.Errorln("injector.Apply error ")
		return err
	}

	v := reflect.ValueOf(value)

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

	if v.Kind() != reflect.Struct {
		return nil
	}

	t := v.Type()

	for i := 0; i < v.NumField(); i++ {

		f := v.Field(i)
		structField := t.Field(i)

		// 指针类型，或有注入标记的，不再处理
		if f.Kind() == reflect.Ptr && structField.Tag == "inject" {
			continue
		} else if f.CanSet() && isStruct(structField.Type){

			err = recursiveInject(injector, f.Addr().Interface())

			if err != nil {
				break
			}
		}
	}

	return err
}

type ContextPostLoad interface {
 	PostLoad()
}

func LoadDataFromFile(injector inject.Injector, data interface {}, ctxFilePath string) error  {

	_, err := toml.DecodeFile(ctxFilePath, data)
	if err != nil {
		return err
	}

	postLoad, ok := data.(ContextPostLoad)
	if ok {
		postLoad.PostLoad()
	}

	ctxValue := reflect.ValueOf(data)
	ctxType := reflect.TypeOf(data)

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

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

	if ctxValue.Kind() == reflect.Struct {
		for fieldIndex:=0; fieldIndex < ctxValue.NumField(); fieldIndex++ {
			fieldValue := ctxValue.Field(fieldIndex)
			fieldType := fieldValue.Type()

			if isStruct(fieldType)   {
				injector.Map(fieldValue.Interface())
			} else if fieldType.Kind() == reflect.Interface {
				t := ctxType.Field(fieldIndex).Type
				injector.Set(t, fieldValue)
			}
		}
	}

	//
	//	递归注入
	//
	err = recursiveInject(injector, data)
	if err != nil {
		logger.Println("recursiveInject error:", err)
	}

	return err

}
<commit_msg>name fixed<commit_after>package goskeleton

import (
	"reflect"
	"github.com/codegangsta/inject"
	"github.com/BurntSushi/toml"
	"github.com/gin-gonic/gin"
	"git.oschina.net/zuobao/gozuobao/logger"
)

type Engine struct {
	*gin.Engine
	Injector inject.Injector
}

func isStruct(t reflect.Type) bool {
	for t != nil {
		switch t.Kind()  {
		case reflect.Struct:
			return true
		case reflect.Ptr:
			t = t.Elem()
		case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
			return false
		default:
			return false
		}
	}
	return false
}

// 递归注入
func recursiveInject(injector inject.Injector, value interface {}) error {

	var err error
	err = injector.Apply(value)
	if err != nil {
		logger.Errorln("injector.Apply error ")
		return err
	}

	v := reflect.ValueOf(value)

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

	if v.Kind() != reflect.Struct {
		return nil
	}

	t := v.Type()

	for i := 0; i < v.NumField(); i++ {

		f := v.Field(i)
		structField := t.Field(i)

		// 指针类型，或有注入标记的，不再处理
		if f.Kind() == reflect.Ptr && structField.Tag == "inject" {
			continue
		} else if f.CanSet() && isStruct(structField.Type){

			err = recursiveInject(injector, f.Addr().Interface())

			if err != nil {
				break
			}
		}
	}

	return err
}

type ContextPostLoad interface {
 	PostLoad()
}

func LoadDataFromFile(injector inject.Injector, data interface {}, ctxFilePath string) error  {

	_, err := toml.DecodeFile(ctxFilePath, data)
	if err != nil {
		return err
	}

	postLoad, ok := data.(ContextPostLoad)
	if ok {
		postLoad.PostLoad()
	}

	ctxValue := reflect.ValueOf(data)
	ctxType := reflect.TypeOf(data)

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

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

	if ctxValue.Kind() == reflect.Struct {
		for fieldIndex:=0; fieldIndex < ctxValue.NumField(); fieldIndex++ {
			fieldValue := ctxValue.Field(fieldIndex)
			fieldType := fieldValue.Type()

			if isStruct(fieldType)   {
				injector.Map(fieldValue.Interface())
			} else if fieldType.Kind() == reflect.Interface {
				t := ctxType.Field(fieldIndex).Type
				injector.Set(t, fieldValue)
			}
		}
	}

	//
	//	递归注入
	//
	err = recursiveInject(injector, data)
	if err != nil {
		logger.Errorln("recursiveInject error:", err)
	}

	return err

}
<|endoftext|>


<commit_before>package slack

import (
	"errors"
	"net/url"
	"strconv"
	"strings"
)

const (
	// Add here the defaults in the siten
	DEFAULT_FILES_USER    = ""
	DEFAULT_FILES_TS_FROM = 0
	DEFAULT_FILES_TS_TO   = -1
	DEFAULT_FILES_TYPES   = "all"
	DEFAULT_FILES_COUNT   = 100
	DEFAULT_FILES_PAGE    = 1
)

// File contains all the information for a file
type File struct {
	ID        string   `json:"id"`
	Created   JSONTime `json:"created"`
	Timestamp JSONTime `json:"timestamp"`

	Name              string `json:"name"`
	Title             string `json:"title"`
	Mimetype          string `json:"mimetype"`
	ImageExifRotation int    `json:"image_exif_rotation"`
	Filetype          string `json:"filetype"`
	PrettyType        string `json:"pretty_type"`
	User              string `json:"user"`

	Mode         string `json:"mode"`
	Editable     bool   `json:"editable"`
	IsExternal   bool   `json:"is_external"`
	ExternalType string `json:"external_type"`

	Size int `json:"size"`

	URL                string `json:"url"`          // Deprecated - never set
	URLDownload        string `json:"url_download"` // Deprecated - never set
	URLPrivate         string `json:"url_private"`
	URLPrivateDownload string `json:"url_private_download"`

	OriginalH   int    `json:"original_h"`
	OriginalW   int    `json:"original_w"`
	Thumb64     string `json:"thumb_64"`
	Thumb80     string `json:"thumb_80"`
	Thumb160    string `json:"thumb_160"`
	Thumb360    string `json:"thumb_360"`
	Thumb360Gif string `json:"thumb_360_gif"`
	Thumb360W   int    `json:"thumb_360_w"`
	Thumb360H   int    `json:"thumb_360_h"`
	Thumb480    string `json:"thumb_480"`
	Thumb480W   int    `json:"thumb_480_w"`
	Thumb480H   int    `json:"thumb_480_h"`
	Thumb720    string `json:"thumb_720"`
	Thumb720W   int    `json:"thumb_720_w"`
	Thumb720H   int    `json:"thumb_720_h"`
	Thumb960    string `json:"thumb_960"`
	Thumb960W   int    `json:"thumb_960_w"`
	Thumb960H   int    `json:"thumb_960_h"`
	Thumb1024   string `json:"thumb_1024"`
	Thumb1024W  int    `json:"thumb_1024_w"`
	Thumb1024H  int    `json:"thumb_1024_h"`

	Permalink       string `json:"permalink"`
	PermalinkPublic string `json:"permalink_public"`

	EditLink         string `json:"edit_link"`
	Preview          string `json:"preview"`
	PreviewHighlight string `json:"preview_highlight"`
	Lines            int    `json:"lines"`
	LinesMore        int    `json:"lines_more"`

	IsPublic        bool     `json:"is_public"`
	PublicURLShared bool     `json:"public_url_shared"`
	Channels        []string `json:"channels"`
	Groups          []string `json:"groups"`
	IMs             []string `json:"ims"`
	InitialComment  Comment  `json:"initial_comment"`
	CommentsCount   int      `json:"comments_count"`
	NumStars        int      `json:"num_stars"`
	IsStarred       bool     `json:"is_starred"`
}

// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request
type FileUploadParameters struct {
	File           string
	Content        string
	Filetype       string
	Filename       string
	Title          string
	InitialComment string
	Channels       []string
}

// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
type GetFilesParameters struct {
	User          string
	TimestampFrom JSONTime
	TimestampTo   JSONTime
	Types         string
	Count         int
	Page          int
}

type fileResponseFull struct {
	File     `json:"file"`
	Paging   `json:"paging"`
	Comments []Comment `json:"comments"`
	Files    []File    `json:"files"`

	SlackResponse
}

// NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set
func NewGetFilesParameters() GetFilesParameters {
	return GetFilesParameters{
		User:          DEFAULT_FILES_USER,
		TimestampFrom: DEFAULT_FILES_TS_FROM,
		TimestampTo:   DEFAULT_FILES_TS_TO,
		Types:         DEFAULT_FILES_TYPES,
		Count:         DEFAULT_FILES_COUNT,
		Page:          DEFAULT_FILES_PAGE,
	}
}

func fileRequest(path string, values url.Values, debug bool) (*fileResponseFull, error) {
	response := &fileResponseFull{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetFileInfo retrieves a file and related comments
func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileID},
		"count": {strconv.Itoa(count)},
		"page":  {strconv.Itoa(page)},
	}
	response, err := fileRequest("files.info", values, api.debug)
	if err != nil {
		return nil, nil, nil, err
	}
	return &response.File, response.Comments, &response.Paging, nil
}

// GetFiles retrieves all files according to the parameters given
func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	if params.User != DEFAULT_FILES_USER {
		values.Add("user", params.User)
	}
	// XXX: this is broken. fix it with a proper unix timestamp
	if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
		values.Add("ts_from", params.TimestampFrom.String())
	}
	if params.TimestampTo != DEFAULT_FILES_TS_TO {
		values.Add("ts_to", params.TimestampTo.String())
	}
	if params.Types != DEFAULT_FILES_TYPES {
		values.Add("types", params.Types)
	}
	if params.Count != DEFAULT_FILES_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_FILES_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}
	response, err := fileRequest("files.list", values, api.debug)
	if err != nil {
		return nil, nil, err
	}
	return response.Files, &response.Paging, nil
}

// UploadFile uploads a file
func (api *Client) UploadFile(params FileUploadParameters) (file *File, err error) {
	// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
	// investigation needed, but for now this will do.
	_, err = api.AuthTest()
	if err != nil {
		return nil, err
	}
	response := &fileResponseFull{}
	values := url.Values{
		"token": {api.config.token},
	}
	if params.Filetype != "" {
		values.Add("filetype", params.Filetype)
	}
	if params.Filename != "" {
		values.Add("filename", params.Filename)
	}
	if params.Title != "" {
		values.Add("title", params.Title)
	}
	if params.InitialComment != "" {
		values.Add("initial_comment", params.InitialComment)
	}
	if len(params.Channels) != 0 {
		values.Add("channels", strings.Join(params.Channels, ","))
	}
	if params.Content != "" {
		values.Add("content", params.Content)
		err = post("files.upload", values, response, api.debug)
	} else if params.File != "" {
		err = postWithMultipartResponse("files.upload", params.File, values, response, api.debug)
	}
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return &response.File, nil
}

// DeleteFile deletes a file
func (api *Client) DeleteFile(fileID string) error {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileID},
	}
	_, err := fileRequest("files.delete", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}
<commit_msg>Add share/revoke methods for files<commit_after>package slack

import (
	"errors"
	"net/url"
	"strconv"
	"strings"
)

const (
	// Add here the defaults in the siten
	DEFAULT_FILES_USER    = ""
	DEFAULT_FILES_TS_FROM = 0
	DEFAULT_FILES_TS_TO   = -1
	DEFAULT_FILES_TYPES   = "all"
	DEFAULT_FILES_COUNT   = 100
	DEFAULT_FILES_PAGE    = 1
)

// File contains all the information for a file
type File struct {
	ID        string   `json:"id"`
	Created   JSONTime `json:"created"`
	Timestamp JSONTime `json:"timestamp"`

	Name              string `json:"name"`
	Title             string `json:"title"`
	Mimetype          string `json:"mimetype"`
	ImageExifRotation int    `json:"image_exif_rotation"`
	Filetype          string `json:"filetype"`
	PrettyType        string `json:"pretty_type"`
	User              string `json:"user"`

	Mode         string `json:"mode"`
	Editable     bool   `json:"editable"`
	IsExternal   bool   `json:"is_external"`
	ExternalType string `json:"external_type"`

	Size int `json:"size"`

	URL                string `json:"url"`          // Deprecated - never set
	URLDownload        string `json:"url_download"` // Deprecated - never set
	URLPrivate         string `json:"url_private"`
	URLPrivateDownload string `json:"url_private_download"`

	OriginalH   int    `json:"original_h"`
	OriginalW   int    `json:"original_w"`
	Thumb64     string `json:"thumb_64"`
	Thumb80     string `json:"thumb_80"`
	Thumb160    string `json:"thumb_160"`
	Thumb360    string `json:"thumb_360"`
	Thumb360Gif string `json:"thumb_360_gif"`
	Thumb360W   int    `json:"thumb_360_w"`
	Thumb360H   int    `json:"thumb_360_h"`
	Thumb480    string `json:"thumb_480"`
	Thumb480W   int    `json:"thumb_480_w"`
	Thumb480H   int    `json:"thumb_480_h"`
	Thumb720    string `json:"thumb_720"`
	Thumb720W   int    `json:"thumb_720_w"`
	Thumb720H   int    `json:"thumb_720_h"`
	Thumb960    string `json:"thumb_960"`
	Thumb960W   int    `json:"thumb_960_w"`
	Thumb960H   int    `json:"thumb_960_h"`
	Thumb1024   string `json:"thumb_1024"`
	Thumb1024W  int    `json:"thumb_1024_w"`
	Thumb1024H  int    `json:"thumb_1024_h"`

	Permalink       string `json:"permalink"`
	PermalinkPublic string `json:"permalink_public"`

	EditLink         string `json:"edit_link"`
	Preview          string `json:"preview"`
	PreviewHighlight string `json:"preview_highlight"`
	Lines            int    `json:"lines"`
	LinesMore        int    `json:"lines_more"`

	IsPublic        bool     `json:"is_public"`
	PublicURLShared bool     `json:"public_url_shared"`
	Channels        []string `json:"channels"`
	Groups          []string `json:"groups"`
	IMs             []string `json:"ims"`
	InitialComment  Comment  `json:"initial_comment"`
	CommentsCount   int      `json:"comments_count"`
	NumStars        int      `json:"num_stars"`
	IsStarred       bool     `json:"is_starred"`
}

// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request
type FileUploadParameters struct {
	File           string
	Content        string
	Filetype       string
	Filename       string
	Title          string
	InitialComment string
	Channels       []string
}

// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
type GetFilesParameters struct {
	User          string
	TimestampFrom JSONTime
	TimestampTo   JSONTime
	Types         string
	Count         int
	Page          int
}

type fileResponseFull struct {
	File     `json:"file"`
	Paging   `json:"paging"`
	Comments []Comment `json:"comments"`
	Files    []File    `json:"files"`

	SlackResponse
}

// NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set
func NewGetFilesParameters() GetFilesParameters {
	return GetFilesParameters{
		User:          DEFAULT_FILES_USER,
		TimestampFrom: DEFAULT_FILES_TS_FROM,
		TimestampTo:   DEFAULT_FILES_TS_TO,
		Types:         DEFAULT_FILES_TYPES,
		Count:         DEFAULT_FILES_COUNT,
		Page:          DEFAULT_FILES_PAGE,
	}
}

func fileRequest(path string, values url.Values, debug bool) (*fileResponseFull, error) {
	response := &fileResponseFull{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetFileInfo retrieves a file and related comments
func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileID},
		"count": {strconv.Itoa(count)},
		"page":  {strconv.Itoa(page)},
	}
	response, err := fileRequest("files.info", values, api.debug)
	if err != nil {
		return nil, nil, nil, err
	}
	return &response.File, response.Comments, &response.Paging, nil
}

// GetFiles retrieves all files according to the parameters given
func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	if params.User != DEFAULT_FILES_USER {
		values.Add("user", params.User)
	}
	// XXX: this is broken. fix it with a proper unix timestamp
	if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
		values.Add("ts_from", params.TimestampFrom.String())
	}
	if params.TimestampTo != DEFAULT_FILES_TS_TO {
		values.Add("ts_to", params.TimestampTo.String())
	}
	if params.Types != DEFAULT_FILES_TYPES {
		values.Add("types", params.Types)
	}
	if params.Count != DEFAULT_FILES_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_FILES_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}
	response, err := fileRequest("files.list", values, api.debug)
	if err != nil {
		return nil, nil, err
	}
	return response.Files, &response.Paging, nil
}

// UploadFile uploads a file
func (api *Client) UploadFile(params FileUploadParameters) (file *File, err error) {
	// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
	// investigation needed, but for now this will do.
	_, err = api.AuthTest()
	if err != nil {
		return nil, err
	}
	response := &fileResponseFull{}
	values := url.Values{
		"token": {api.config.token},
	}
	if params.Filetype != "" {
		values.Add("filetype", params.Filetype)
	}
	if params.Filename != "" {
		values.Add("filename", params.Filename)
	}
	if params.Title != "" {
		values.Add("title", params.Title)
	}
	if params.InitialComment != "" {
		values.Add("initial_comment", params.InitialComment)
	}
	if len(params.Channels) != 0 {
		values.Add("channels", strings.Join(params.Channels, ","))
	}
	if params.Content != "" {
		values.Add("content", params.Content)
		err = post("files.upload", values, response, api.debug)
	} else if params.File != "" {
		err = postWithMultipartResponse("files.upload", params.File, values, response, api.debug)
	}
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return &response.File, nil
}

// DeleteFile deletes a file
func (api *Client) DeleteFile(fileID string) error {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileID},
	}
	_, err := fileRequest("files.delete", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}

// RevokeFilePublicURL disables public/external sharing for a file
func (api *Client) RevokeFilePublicURL(fileID string) (*File, error) {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileID},
	}
	response, err := fileRequest("files.revokePublicURL", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.File, nil
}

// ShareFilePublicURL enabled public/external sharing for a file
func (api *Client) ShareFilePublicURL(fileID string) (*File, []Comment, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileID},
	}
	response, err := fileRequest("files.sharedPublicURL", values, api.debug)
	if err != nil {
		return nil, nil, nil, err
	}
	return &response.File, response.Comments, &response.Paging, nil
}
<|endoftext|>