<commit_before>package bulk

import (
	"crypto/tls"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/cf_http"
	"github.com/cloudfoundry-incubator/nsync/recipebuilder"
	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/route-emitter/cfroutes"
	"github.com/cloudfoundry-incubator/runtime-schema/cc_messages"
	"github.com/cloudfoundry-incubator/runtime-schema/metric"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
)

const (
	syncDesiredLRPsDuration = metric.Duration("DesiredLRPSyncDuration")
)

//go:generate counterfeiter -o fakes/fake_recipe_builder.go . RecipeBuilder
type RecipeBuilder interface {
	Build(*cc_messages.DesireAppRequestFromCC) (*receptor.DesiredLRPCreateRequest, error)
}

type Processor struct {
	receptorClient  receptor.Client
	pollingInterval time.Duration
	domainTTL       time.Duration
	bulkBatchSize   uint
	skipCertVerify  bool
	logger          lager.Logger
	fetcher         Fetcher
	builder         RecipeBuilder
	clock           clock.Clock
}

func NewProcessor(
	receptorClient receptor.Client,
	pollingInterval time.Duration,
	domainTTL time.Duration,
	bulkBatchSize uint,
	skipCertVerify bool,
	logger lager.Logger,
	fetcher Fetcher,
	builder RecipeBuilder,
	clock clock.Clock,
) *Processor {
	return &Processor{
		receptorClient:  receptorClient,
		pollingInterval: pollingInterval,
		domainTTL:       domainTTL,
		bulkBatchSize:   bulkBatchSize,
		skipCertVerify:  skipCertVerify,
		logger:          logger,
		fetcher:         fetcher,
		builder:         builder,
		clock:           clock,
	}
}

func (p *Processor) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	close(ready)

	httpClient := cf_http.NewClient()
	httpClient.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: p.skipCertVerify,
			MinVersion:         tls.VersionTLS10,
		},
	}

	timer := p.clock.NewTimer(p.pollingInterval)
	stop := p.sync(signals, httpClient)

	for {
		if stop {
			return nil
		}

		select {
		case <-signals:
			return nil
		case <-timer.C():
			stop = p.sync(signals, httpClient)
			timer.Reset(p.pollingInterval)
		}
	}
}

func (p *Processor) sync(signals <-chan os.Signal, httpClient *http.Client) bool {
	start := p.clock.Now()
	defer func() {
		duration := p.clock.Now().Sub(start)
		syncDesiredLRPsDuration.Send(duration)
	}()

	logger := p.logger.Session("sync")

	existing, err := p.getDesiredLRPs(logger)
	if err != nil {
		return false
	}

	differ := NewDiffer(existing)

	cancel := make(chan struct{})

	fingerprints, fingerprintErrors := p.fetcher.FetchFingerprints(
		logger,
		cancel,
		httpClient,
	)

	diffErrors := differ.Diff(
		logger,
		cancel,
		fingerprints,
	)

	missingApps, missingAppsErrors := p.fetcher.FetchDesiredApps(
		logger,
		cancel,
		httpClient,
		differ.Missing(),
	)

	createErrors := p.createMissingDesiredLRPs(logger, cancel, missingApps)

	staleApps, staleAppErrors := p.fetcher.FetchDesiredApps(
		logger,
		cancel,
		httpClient,
		differ.Stale(),
	)

	updateErrors := p.updateStaleDesiredLRPs(logger, cancel, staleApps)

	bumpFreshness := true
	success := true

	fingerprintErrors, fingerprintErrorCount := countErrors(fingerprintErrors)

	errors := mergeErrors(
		fingerprintErrors,
		diffErrors,
		missingAppsErrors,
		staleAppErrors,
		createErrors,
		updateErrors,
	)

process_loop:
	for {
		select {
		case err, open := <-errors:
			if err != nil {
				logger.Error("not-bumping-freshness-because-of", err)
				bumpFreshness = false
			}
			if !open {
				break process_loop
			}
		case <-signals:
			close(cancel)
			return true
		}
	}

	if <-fingerprintErrorCount != 0 {
		logger.Error("failed-to-fetch-all-cc-fingerprints", nil)
		success = false
	}

	if success {
		deleteList := <-differ.Deleted()
		p.deleteExcess(logger, cancel, deleteList)
	}

	if bumpFreshness && success {
		logger.Info("bumping-freshness")

		err = p.receptorClient.UpsertDomain(recipebuilder.LRPDomain, p.domainTTL)
		if err != nil {
			logger.Error("failed-to-upsert-domain", err)
		}
	}

	return false
}

func (p *Processor) createMissingDesiredLRPs(
	logger lager.Logger,
	cancel <-chan struct{},
	missing <-chan []cc_messages.DesireAppRequestFromCC,
) <-chan error {
	logger = logger.Session("create-missing-desired-lrps")

	errc := make(chan error, 1)

	go func() {
		defer close(errc)

		for {
			var desireAppRequests []cc_messages.DesireAppRequestFromCC

			select {
			case <-cancel:
				return

			case selected, open := <-missing:
				if !open {
					return
				}

				desireAppRequests = selected
			}

			logger.Info("processing-batch", lager.Data{"size": len(desireAppRequests)})

			for _, desireAppRequest := range desireAppRequests {
				createReq, err := p.builder.Build(&desireAppRequest)
				if err != nil {
					logger.Error("failed-to-build-create-desired-lrp-request", err, lager.Data{
						"desire-app-request": desireAppRequest,
					})
					errc <- err
					continue
				}

				err = p.receptorClient.CreateDesiredLRP(*createReq)
				if err != nil {
					logger.Error("failed-to-create-desired-lrp", err, lager.Data{
						"create-request": createReq,
					})
					errc <- err
					continue
				}
			}
		}
	}()

	return errc
}

func (p *Processor) updateStaleDesiredLRPs(
	logger lager.Logger,
	cancel <-chan struct{},
	stale <-chan []cc_messages.DesireAppRequestFromCC,
) <-chan error {
	logger = logger.Session("update-stale-desired-lrps")

	errc := make(chan error, 1)

	go func() {
		defer close(errc)

		for {
			var staleAppRequests []cc_messages.DesireAppRequestFromCC

			select {
			case <-cancel:
				return

			case selected, open := <-stale:
				if !open {
					return
				}

				staleAppRequests = selected
			}

			logger.Info("processing-batch", lager.Data{"size": len(staleAppRequests)})

			for _, desireAppRequest := range staleAppRequests {
				updateReq := receptor.DesiredLRPUpdateRequest{}
				updateReq.Instances = &desireAppRequest.NumInstances
				updateReq.Annotation = &desireAppRequest.ETag
				updateReq.Routes = cfroutes.CFRoutes{
					{Hostnames: desireAppRequest.Routes, Port: recipebuilder.DefaultPort},
				}.RoutingInfo()

				err := p.receptorClient.UpdateDesiredLRP(desireAppRequest.ProcessGuid, updateReq)
				if err != nil {
					logger.Error("failed-to-update-stale-lrp", err, lager.Data{
						"update-request": updateReq,
					})
					errc <- err
					continue
				}
			}
		}
	}()

	return errc
}

func (p *Processor) getDesiredLRPs(logger lager.Logger) ([]receptor.DesiredLRPResponse, error) {
	logger.Info("getting-desired-lrps-from-bbs")

	existing, err := p.receptorClient.DesiredLRPsByDomain(recipebuilder.LRPDomain)
	if err != nil {
		logger.Error("failed-to-get-desired-lrps", err)
		return nil, err
	}

	logger.Info("got-desired-lrps-from-bbs", lager.Data{"count": len(existing)})
	return existing, nil
}

func (p *Processor) deleteExcess(logger lager.Logger, cancel <-chan struct{}, excess []string) {
	logger = logger.Session("delete-excess")

	logger.Info(
		"processing-batch",
		lager.Data{"size": len(excess)},
	)

	for _, deleteGuid := range excess {
		err := p.receptorClient.DeleteDesiredLRP(deleteGuid)
		if err != nil {
			logger.Error(
				"failed-to-delete-desired-lrp",
				err,
				lager.Data{"delete-request": deleteGuid},
			)
		}
	}
}

func countErrors(source <-chan error) (<-chan error, <-chan int) {
	count := make(chan int, 1)
	dest := make(chan error, 1)
	var errorCount int

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		for e := range source {
			errorCount++
			dest <- e
		}

		close(dest)
		wg.Done()
	}()

	go func() {
		wg.Wait()

		count <- errorCount
		close(count)
	}()

	return dest, count
}

func mergeErrors(channels ...<-chan error) <-chan error {
	out := make(chan error)
	wg := sync.WaitGroup{}

	for _, ch := range channels {
		wg.Add(1)

		go func(c <-chan error) {
			for e := range c {
				out <- e
			}
			wg.Done()
		}(ch)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}
<commit_msg>Add logging to bulk processor [#90332636]<commit_after>package bulk

import (
	"crypto/tls"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/cf_http"
	"github.com/cloudfoundry-incubator/nsync/recipebuilder"
	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/route-emitter/cfroutes"
	"github.com/cloudfoundry-incubator/runtime-schema/cc_messages"
	"github.com/cloudfoundry-incubator/runtime-schema/metric"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
)

const (
	syncDesiredLRPsDuration = metric.Duration("DesiredLRPSyncDuration")
)

//go:generate counterfeiter -o fakes/fake_recipe_builder.go . RecipeBuilder
type RecipeBuilder interface {
	Build(*cc_messages.DesireAppRequestFromCC) (*receptor.DesiredLRPCreateRequest, error)
}

type Processor struct {
	receptorClient  receptor.Client
	pollingInterval time.Duration
	domainTTL       time.Duration
	bulkBatchSize   uint
	skipCertVerify  bool
	logger          lager.Logger
	fetcher         Fetcher
	builder         RecipeBuilder
	clock           clock.Clock
}

func NewProcessor(
	receptorClient receptor.Client,
	pollingInterval time.Duration,
	domainTTL time.Duration,
	bulkBatchSize uint,
	skipCertVerify bool,
	logger lager.Logger,
	fetcher Fetcher,
	builder RecipeBuilder,
	clock clock.Clock,
) *Processor {
	return &Processor{
		receptorClient:  receptorClient,
		pollingInterval: pollingInterval,
		domainTTL:       domainTTL,
		bulkBatchSize:   bulkBatchSize,
		skipCertVerify:  skipCertVerify,
		logger:          logger,
		fetcher:         fetcher,
		builder:         builder,
		clock:           clock,
	}
}

func (p *Processor) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	close(ready)

	httpClient := cf_http.NewClient()
	httpClient.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: p.skipCertVerify,
			MinVersion:         tls.VersionTLS10,
		},
	}

	timer := p.clock.NewTimer(p.pollingInterval)
	stop := p.sync(signals, httpClient)

	for {
		if stop {
			return nil
		}

		select {
		case <-signals:
			return nil
		case <-timer.C():
			stop = p.sync(signals, httpClient)
			timer.Reset(p.pollingInterval)
		}
	}
}

func (p *Processor) sync(signals <-chan os.Signal, httpClient *http.Client) bool {
	start := p.clock.Now()
	defer func() {
		duration := p.clock.Now().Sub(start)
		syncDesiredLRPsDuration.Send(duration)
	}()

	logger := p.logger.Session("sync")
	logger.Info("starting")
	defer logger.Info("done")

	existing, err := p.getDesiredLRPs(logger)
	if err != nil {
		return false
	}

	differ := NewDiffer(existing)

	cancel := make(chan struct{})

	fingerprints, fingerprintErrors := p.fetcher.FetchFingerprints(
		logger,
		cancel,
		httpClient,
	)

	diffErrors := differ.Diff(
		logger,
		cancel,
		fingerprints,
	)

	missingApps, missingAppsErrors := p.fetcher.FetchDesiredApps(
		logger,
		cancel,
		httpClient,
		differ.Missing(),
	)

	createErrors := p.createMissingDesiredLRPs(logger, cancel, missingApps)

	staleApps, staleAppErrors := p.fetcher.FetchDesiredApps(
		logger,
		cancel,
		httpClient,
		differ.Stale(),
	)

	updateErrors := p.updateStaleDesiredLRPs(logger, cancel, staleApps)

	bumpFreshness := true
	success := true

	fingerprintErrors, fingerprintErrorCount := countErrors(fingerprintErrors)

	errors := mergeErrors(
		fingerprintErrors,
		diffErrors,
		missingAppsErrors,
		staleAppErrors,
		createErrors,
		updateErrors,
	)

	logger.Info("processing-updates-and-creates")
process_loop:
	for {
		select {
		case err, open := <-errors:
			if err != nil {
				logger.Error("not-bumping-freshness-because-of", err)
				bumpFreshness = false
			}
			if !open {
				break process_loop
			}
		case sig := <-signals:
			logger.Info("exiting", lager.Data{"received-signal": sig})
			close(cancel)
			return true
		}
	}
	logger.Info("done-processing-updates-and-creates")

	if <-fingerprintErrorCount != 0 {
		logger.Error("failed-to-fetch-all-cc-fingerprints", nil)
		success = false
	}

	if success {
		deleteList := <-differ.Deleted()
		p.deleteExcess(logger, cancel, deleteList)
	}

	if bumpFreshness && success {
		logger.Info("bumping-freshness")

		err = p.receptorClient.UpsertDomain(recipebuilder.LRPDomain, p.domainTTL)
		if err != nil {
			logger.Error("failed-to-upsert-domain", err)
		}
	}

	return false
}

func (p *Processor) createMissingDesiredLRPs(
	logger lager.Logger,
	cancel <-chan struct{},
	missing <-chan []cc_messages.DesireAppRequestFromCC,
) <-chan error {
	logger = logger.Session("create-missing-desired-lrps")

	errc := make(chan error, 1)

	go func() {
		defer close(errc)

		for {
			var desireAppRequests []cc_messages.DesireAppRequestFromCC

			select {
			case <-cancel:
				return

			case selected, open := <-missing:
				if !open {
					return
				}

				desireAppRequests = selected
			}

			logger.Info("processing-batch", lager.Data{"size": len(desireAppRequests)})

			for _, desireAppRequest := range desireAppRequests {
				createReq, err := p.builder.Build(&desireAppRequest)
				if err != nil {
					logger.Error("failed-to-build-create-desired-lrp-request", err, lager.Data{
						"desire-app-request": desireAppRequest,
					})
					errc <- err
					continue
				}

				err = p.receptorClient.CreateDesiredLRP(*createReq)
				if err != nil {
					logger.Error("failed-to-create-desired-lrp", err, lager.Data{
						"create-request": createReq,
					})
					errc <- err
					continue
				}
			}
		}
	}()

	return errc
}

func (p *Processor) updateStaleDesiredLRPs(
	logger lager.Logger,
	cancel <-chan struct{},
	stale <-chan []cc_messages.DesireAppRequestFromCC,
) <-chan error {
	logger = logger.Session("update-stale-desired-lrps")

	errc := make(chan error, 1)

	go func() {
		defer close(errc)

		for {
			var staleAppRequests []cc_messages.DesireAppRequestFromCC

			select {
			case <-cancel:
				return

			case selected, open := <-stale:
				if !open {
					return
				}

				staleAppRequests = selected
			}

			logger.Info("processing-batch", lager.Data{"size": len(staleAppRequests)})

			for _, desireAppRequest := range staleAppRequests {
				updateReq := receptor.DesiredLRPUpdateRequest{}
				updateReq.Instances = &desireAppRequest.NumInstances
				updateReq.Annotation = &desireAppRequest.ETag
				updateReq.Routes = cfroutes.CFRoutes{
					{Hostnames: desireAppRequest.Routes, Port: recipebuilder.DefaultPort},
				}.RoutingInfo()

				err := p.receptorClient.UpdateDesiredLRP(desireAppRequest.ProcessGuid, updateReq)
				if err != nil {
					logger.Error("failed-to-update-stale-lrp", err, lager.Data{
						"update-request": updateReq,
					})
					errc <- err
					continue
				}
			}
		}
	}()

	return errc
}

func (p *Processor) getDesiredLRPs(logger lager.Logger) ([]receptor.DesiredLRPResponse, error) {
	logger.Info("getting-desired-lrps-from-bbs")
	existing, err := p.receptorClient.DesiredLRPsByDomain(recipebuilder.LRPDomain)
	if err != nil {
		logger.Error("failed-getting-desired-lrps-from-bbs", err)
		return nil, err
	}
	logger.Info("succeeded-getting-desired-lrps-from-bbs", lager.Data{"count": len(existing)})

	return existing, nil
}

func (p *Processor) deleteExcess(logger lager.Logger, cancel <-chan struct{}, excess []string) {
	logger = logger.Session("delete-excess")

	logger.Info("processing-batch", lager.Data{"size": len(excess)})
	for _, deleteGuid := range excess {
		err := p.receptorClient.DeleteDesiredLRP(deleteGuid)
		if err != nil {
			logger.Error("failed-processing-batch", err, lager.Data{"delete-request": deleteGuid})
		}
	}
	logger.Info("succeeded-processing-batch")
}

func countErrors(source <-chan error) (<-chan error, <-chan int) {
	count := make(chan int, 1)
	dest := make(chan error, 1)
	var errorCount int

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		for e := range source {
			errorCount++
			dest <- e
		}

		close(dest)
		wg.Done()
	}()

	go func() {
		wg.Wait()

		count <- errorCount
		close(count)
	}()

	return dest, count
}

func mergeErrors(channels ...<-chan error) <-chan error {
	out := make(chan error)
	wg := sync.WaitGroup{}

	for _, ch := range channels {
		wg.Add(1)

		go func(c <-chan error) {
			for e := range c {
				out <- e
			}
			wg.Done()
		}(ch)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}
<|endoftext|>


<commit_before>//
//
// File generated from our OpenAPI spec
//
//

package stripe

// RadarEarlyFraudWarningFraudType are strings that map to the type of fraud labelled by the issuer.
type RadarEarlyFraudWarningFraudType string

// List of values that RadarEarlyFraudWarningFraudType can take.
const (
	RadarEarlyFraudWarningFraudTypeCardNeverReceived         RadarEarlyFraudWarningFraudType = "card_never_received"
	RadarEarlyFraudWarningFraudTypeFraudulentCardApplication RadarEarlyFraudWarningFraudType = "fraudulent_card_application"
	RadarEarlyFraudWarningFraudTypeMadeWithCounterfeitCard   RadarEarlyFraudWarningFraudType = "made_with_counterfeit_card"
	RadarEarlyFraudWarningFraudTypeMadeWithLostCard          RadarEarlyFraudWarningFraudType = "made_with_lost_card"
	RadarEarlyFraudWarningFraudTypeMadeWithStolenCard        RadarEarlyFraudWarningFraudType = "made_with_stolen_card"
	RadarEarlyFraudWarningFraudTypeMisc                      RadarEarlyFraudWarningFraudType = "misc"
	RadarEarlyFraudWarningFraudTypeUnauthorizedUseOfCard     RadarEarlyFraudWarningFraudType = "unauthorized_use_of_card"
)

// RadarEarlyFraudWarningParams is the set of parameters that can be used when
// retrieving early fraud warnings. For more details see
// https://stripe.com/docs/api/early_fraud_warnings/retrieve.
type RadarEarlyFraudWarningParams struct {
	Params `form:"*"`
}

// RadarEarlyFraudWarningListParams is the set of parameters that can be used when
// listing early fraud warnings. For more details see
// https://stripe.com/docs/api/early_fraud_warnings/list.
type RadarEarlyFraudWarningListParams struct {
	ListParams `form:"*"`
	Charge     *string `form:"charge"`
}

// RadarEarlyFraudWarning is the resource representing an early fraud warning. For
// more details see https://stripe.com/docs/api/early_fraud_warnings/object.
type RadarEarlyFraudWarning struct {
	APIResource
	Actionable bool                            `json:"actionable"`
	Charge     *Charge                         `json:"charge"`
	Created    int64                           `json:"created"`
	FraudType  RadarEarlyFraudWarningFraudType `json:"fraud_type"`
	ID         string                          `json:"id"`
	Livemode   bool                            `json:"livemode"`
	Object     string                          `json:"object"`
}

// RadarEarlyFraudWarningList is a list of early fraud warnings as retrieved from a
// list endpoint.
type RadarEarlyFraudWarningList struct {
	APIResource
	ListMeta
	// TODO: rename `Values` to `Data` in a future major version for consistency with other List structs
	Values []*RadarEarlyFraudWarning `json:"data"`
}
<commit_msg>API Updates (#1279)<commit_after>//
//
// File generated from our OpenAPI spec
//
//

package stripe

// RadarEarlyFraudWarningFraudType are strings that map to the type of fraud labelled by the issuer.
type RadarEarlyFraudWarningFraudType string

// List of values that RadarEarlyFraudWarningFraudType can take.
const (
	RadarEarlyFraudWarningFraudTypeCardNeverReceived         RadarEarlyFraudWarningFraudType = "card_never_received"
	RadarEarlyFraudWarningFraudTypeFraudulentCardApplication RadarEarlyFraudWarningFraudType = "fraudulent_card_application"
	RadarEarlyFraudWarningFraudTypeMadeWithCounterfeitCard   RadarEarlyFraudWarningFraudType = "made_with_counterfeit_card"
	RadarEarlyFraudWarningFraudTypeMadeWithLostCard          RadarEarlyFraudWarningFraudType = "made_with_lost_card"
	RadarEarlyFraudWarningFraudTypeMadeWithStolenCard        RadarEarlyFraudWarningFraudType = "made_with_stolen_card"
	RadarEarlyFraudWarningFraudTypeMisc                      RadarEarlyFraudWarningFraudType = "misc"
	RadarEarlyFraudWarningFraudTypeUnauthorizedUseOfCard     RadarEarlyFraudWarningFraudType = "unauthorized_use_of_card"
)

// RadarEarlyFraudWarningParams is the set of parameters that can be used when
// retrieving early fraud warnings. For more details see
// https://stripe.com/docs/api/early_fraud_warnings/retrieve.
type RadarEarlyFraudWarningParams struct {
	Params `form:"*"`
}

// RadarEarlyFraudWarningListParams is the set of parameters that can be used when
// listing early fraud warnings. For more details see
// https://stripe.com/docs/api/early_fraud_warnings/list.
type RadarEarlyFraudWarningListParams struct {
	ListParams    `form:"*"`
	Charge        *string `form:"charge"`
	PaymentIntent *string `form:"payment_intent"`
}

// RadarEarlyFraudWarning is the resource representing an early fraud warning. For
// more details see https://stripe.com/docs/api/early_fraud_warnings/object.
type RadarEarlyFraudWarning struct {
	APIResource
	Actionable    bool                            `json:"actionable"`
	Charge        *Charge                         `json:"charge"`
	Created       int64                           `json:"created"`
	FraudType     RadarEarlyFraudWarningFraudType `json:"fraud_type"`
	ID            string                          `json:"id"`
	Livemode      bool                            `json:"livemode"`
	Object        string                          `json:"object"`
	PaymentIntent *PaymentIntent                  `json:"payment_intent"`
}

// RadarEarlyFraudWarningList is a list of early fraud warnings as retrieved from a
// list endpoint.
type RadarEarlyFraudWarningList struct {
	APIResource
	ListMeta
	// TODO: rename `Values` to `Data` in a future major version for consistency with other List structs
	Values []*RadarEarlyFraudWarning `json:"data"`
}
<|endoftext|>


<commit_before>package missinggo

import (
	"math/rand"
	"time"
)

func JitterDuration(average, plusMinus time.Duration) (ret time.Duration) {
	ret = average - plusMinus
	ret += time.Duration(rand.Int63n(2*int64(plusMinus) + 1))
	return
}
<commit_msg>Comment on JitterDuration<commit_after>package missinggo

import (
	"math/rand"
	"time"
)

// Returns random duration in the range [average-plusMinus,
// average+plusMinus]. Negative plusMinus will likely panic. Be aware that if
// plusMinus >= average, you may get a zero or negative Duration. The
// distribution function is unspecified, in case I find a more appropriate one
// in the future.
func JitterDuration(average, plusMinus time.Duration) (ret time.Duration) {
	ret = average - plusMinus
	ret += time.Duration(rand.Int63n(2*int64(plusMinus) + 1))
	return
}
<|endoftext|>


<commit_before>/* Copyright (c) 2016 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package evereader

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/jasonish/evebox/log"
	"os"
)

type Bookmark struct {
	// The filename.
	Path string `json:"path"`

	// The offset, for Eve this is the line number.
	Offset uint64 `json:"offset"`

	// The sile of the file referenced in path.
	Size int64 `json:"size"`

	Sys interface{} `json:"sys"`
}

type Bookmarker struct {
	Filename string
	Reader   *EveReader
}

func ConfigureBookmarker(path string, bookmarkDirectory string, reader *EveReader) *Bookmarker {
	var bookmarkPath string

	if bookmarkDirectory == "" {
		bookmarkPath = fmt.Sprintf("%s.bookmark", path)
	} else {
		hash := md5.Sum([]byte(path))
		bookmarkPath = fmt.Sprintf("%s/%x.bookmark",
			bookmarkDirectory, hash)
	}

	log.Info("Using bookmark file %s", bookmarkPath)

	return &Bookmarker{
		Filename: bookmarkPath,
		Reader:   reader,
	}
}

func (b *Bookmarker) GetBookmark() *Bookmark {
	bookmark := Bookmark{}
	bookmark.Path = b.Reader.path
	bookmark.Offset = b.Reader.Pos()

	fileInfo, err := b.Reader.GetFileInfo()
	if err == nil {
		bookmark.Sys = GetSys(fileInfo)
		bookmark.Size = fileInfo.Size()
	}

	return &bookmark
}

func (b *Bookmarker) WriteBookmark(bookmark *Bookmark) error {
	file, err := os.Create(b.Filename)
	if err != nil {
		return err
	}
	encoder := json.NewEncoder(file)
	err = encoder.Encode(bookmark)
	if err != nil {
		return err
	}
	return nil
}

func (b *Bookmarker) ReadBookmark() (*Bookmark, error) {
	file, err := os.Open(b.Filename)
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	decoder.UseNumber()
	var bookmark Bookmark
	err = decoder.Decode(&bookmark)
	if err != nil {
		return nil, err
	}
	return &bookmark, nil
}

func (b *Bookmarker) BookmarkIsValid(bookmark *Bookmark) bool {

	if bookmark.Path != b.Reader.path {
		return false
	}

	fileInfo, err := b.Reader.GetFileInfo()
	if err == nil {

		// If the current file size is less than the bookmark file
		// size it was likely truncated, invalidate.
		if fileInfo.Size() < bookmark.Size {
			return false
		}

		if !SameSys(bookmark.Sys, GetSys(fileInfo)) {
			log.Error("Inodes don't match")
		}
	}

	return true
}

func (b *Bookmarker) Init(end bool) error {
	bookmark, err := b.ReadBookmark()
	if err == nil && b.BookmarkIsValid(bookmark) {
		err = b.Reader.SkipTo(bookmark.Offset)
		if err != nil {
			log.Error("Failed to skip to line %d, will skip to end of file: %s", err)
			b.Reader.SkipToEnd()
		}
	} else {
		log.Info("Failed to read bookmark: %s", err)
		if end {
			log.Info("Will start reading at end of file.")
			b.Reader.SkipToEnd()
		} else {
			log.Info("Will start reading at beginning of file.")
		}
	}

	// Test write.
	bookmark = b.GetBookmark()
	return b.WriteBookmark(bookmark)
}
<commit_msg>evereader: better logging around stale bookmarks<commit_after>/* Copyright (c) 2016 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package evereader

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/jasonish/evebox/log"
	"os"
)

type Bookmark struct {
	// The filename.
	Path string `json:"path"`

	// The offset, for Eve this is the line number.
	Offset uint64 `json:"offset"`

	// The sile of the file referenced in path.
	Size int64 `json:"size"`

	Sys interface{} `json:"sys"`
}

type Bookmarker struct {
	Filename string
	Reader   *EveReader
}

func ConfigureBookmarker(path string, bookmarkDirectory string, reader *EveReader) *Bookmarker {
	var bookmarkPath string

	if bookmarkDirectory == "" {
		bookmarkPath = fmt.Sprintf("%s.bookmark", path)
	} else {
		hash := md5.Sum([]byte(path))
		bookmarkPath = fmt.Sprintf("%s/%x.bookmark",
			bookmarkDirectory, hash)
	}

	log.Info("Using bookmark file %s", bookmarkPath)

	return &Bookmarker{
		Filename: bookmarkPath,
		Reader:   reader,
	}
}

func (b *Bookmarker) GetBookmark() *Bookmark {
	bookmark := Bookmark{}
	bookmark.Path = b.Reader.path
	bookmark.Offset = b.Reader.Pos()

	fileInfo, err := b.Reader.GetFileInfo()
	if err == nil {
		bookmark.Sys = GetSys(fileInfo)
		bookmark.Size = fileInfo.Size()
	}

	return &bookmark
}

func (b *Bookmarker) WriteBookmark(bookmark *Bookmark) error {
	file, err := os.Create(b.Filename)
	if err != nil {
		return err
	}
	encoder := json.NewEncoder(file)
	err = encoder.Encode(bookmark)
	if err != nil {
		return err
	}
	return nil
}

func (b *Bookmarker) ReadBookmark() (*Bookmark, error) {
	file, err := os.Open(b.Filename)
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	decoder.UseNumber()
	var bookmark Bookmark
	err = decoder.Decode(&bookmark)
	if err != nil {
		return nil, err
	}
	return &bookmark, nil
}

func (b *Bookmarker) BookmarkIsValid(bookmark *Bookmark) bool {

	if bookmark.Path != b.Reader.path {
		return false
	}

	fileInfo, err := b.Reader.GetFileInfo()
	if err == nil {

		// If the current file size is less than the bookmark file
		// size it was likely truncated, invalidate.
		if fileInfo.Size() < bookmark.Size {
			return false
		}

		if !SameSys(bookmark.Sys, GetSys(fileInfo)) {
			log.Debug("Current file does not matched bookmarked inode")
			return false
		}
	}

	return true
}

func (b *Bookmarker) Init(end bool) error {
	bookmark, err := b.ReadBookmark()

	if err == nil && b.BookmarkIsValid(bookmark) {
		err = b.Reader.SkipTo(bookmark.Offset)
		if err != nil {
			log.Error("Failed to skip to line %d, will skip to end of file: %s", err)
			b.Reader.SkipToEnd()
		}
	} else {
		if err != nil {
			log.Info("Failed to read bookmark: %s", err)
		} else {
			log.Info("Stale bookmark found")
		}
		if end {
			log.Info("Will start reading at end of file.")
			b.Reader.SkipToEnd()
		} else {
			log.Info("Will start reading at beginning of file.")
		}
	}

	// Test write.
	bookmark = b.GetBookmark()
	return b.WriteBookmark(bookmark)
}
<|endoftext|>


<commit_before>package gorm

import (
	"fmt"
)

// defaultCallback hold default callbacks defined by gorm
var defaultCallback = &Callback{}

// Callback contains callbacks that used when CURD objects
//   Field `creates` hold callbacks will be call when creating object
//   Field `updates` hold callbacks will be call when updating object
//   Field `deletes` hold callbacks will be call when deleting object
//   Field `queries` hold callbacks will be call when querying object with query methods like Find, First, Related, Association...
//   Field `rowQueries` hold callbacks will be call when querying object with Row, Rows...
//   Field `processors` hold all callback processors, will be used to generate above callbacks in order
type Callback struct {
	creates    []*func(scope *Scope)
	updates    []*func(scope *Scope)
	deletes    []*func(scope *Scope)
	queries    []*func(scope *Scope)
	rowQueries []*func(scope *Scope)
	processors []*CallbackProcessor
}

// CallbackProcessor contains all informations for a callback
type CallbackProcessor struct {
	name      string              // current callback's name
	before    string              // register current callback before a callback
	after     string              // register current callback after a callback
	replace   bool                // replace callbacks with same name
	remove    bool                // delete callbacks with same name
	kind      string              // callback type: create, update, delete, query, row_query
	processor *func(scope *Scope) // callback handler
	parent    *Callback
}

func (c *Callback) addProcessor(kind string) *CallbackProcessor {
	cp := &CallbackProcessor{kind: kind, parent: c}
	c.processors = append(c.processors, cp)
	return cp
}

func (c *Callback) clone() *Callback {
	return &Callback{
		creates:    c.creates,
		updates:    c.updates,
		deletes:    c.deletes,
		queries:    c.queries,
		processors: c.processors,
	}
}

// Create could be used to register callbacks for creating object
//     db.Callback().Create().After("gorm:create").Register("plugin:run_after_create", func(*Scope) {
//       // business logic
//       ...
//
//       // set error if some thing wrong happened, will rollback the creating
//       scope.Err(errors.New("error"))
//     })
func (c *Callback) Create() *CallbackProcessor {
	return c.addProcessor("create")
}

// Update could be used to register callbacks for updating object, refer `Create` for usage
func (c *Callback) Update() *CallbackProcessor {
	return c.addProcessor("update")
}

// Delete could be used to register callbacks for deleting object, refer `Create` for usage
func (c *Callback) Delete() *CallbackProcessor {
	return c.addProcessor("delete")
}

// Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
// refer `Create` for usage
func (c *Callback) Query() *CallbackProcessor {
	return c.addProcessor("query")
}

// RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
func (c *Callback) RowQuery() *CallbackProcessor {
	return c.addProcessor("row_query")
}

// After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
func (cp *CallbackProcessor) After(callbackName string) *CallbackProcessor {
	cp.after = callbackName
	return cp
}

// Before insert a new callback before callback `callbackName`, refer `Callbacks.Create`
func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
	cp.before = callbackName
	return cp
}

// Register a new callback, refer `Callbacks.Create`
func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
	cp.name = callbackName
	cp.processor = &callback
	cp.parent.reorder()
}

// Remove a registered callback
//     db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
func (cp *CallbackProcessor) Remove(callbackName string) {
	fmt.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
	cp.name = callbackName
	cp.remove = true
	cp.parent.reorder()
}

// Replace a registered callback with new callback
//     db.Callback().Create().Replace("gorm:update_time_stamp_when_create", func(*Scope) {
//		   scope.SetColumn("Created", now)
//		   scope.SetColumn("Updated", now)
//     })
func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
	fmt.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
	cp.name = callbackName
	cp.processor = &callback
	cp.replace = true
	cp.parent.reorder()
}

// Get registered callback
//    db.Callback().Create().Get("gorm:create")
func (cp *CallbackProcessor) Get(callbackName string) (callback func(scope *Scope)) {
	for _, processor := range cp.parent.processors {
		if processor.name == callbackName && processor.kind == cp.kind && !cp.remove {
			return *cp.processor
		}
	}
	return nil
}

// getRIndex get right index from string slice
func getRIndex(strs []string, str string) int {
	for i := len(strs) - 1; i >= 0; i-- {
		if strs[i] == str {
			return i
		}
	}
	return -1
}

// sortProcessors sort callback processors based on its before, after, remove, replace
func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
	var (
		allNames, sortedNames []string
		sortCallbackProcessor func(c *CallbackProcessor)
	)

	for _, cp := range cps {
		// show warning message the callback name already exists
		if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
			fmt.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
		}
		allNames = append(allNames, cp.name)
	}

	sortCallbackProcessor = func(c *CallbackProcessor) {
		if getRIndex(sortedNames, c.name) == -1 { // if not sorted
			if c.before != "" { // if defined before callback
				if index := getRIndex(sortedNames, c.before); index != -1 {
					// if before callback already sorted, append current callback just after it
					sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
				} else if index := getRIndex(allNames, c.before); index != -1 {
					// if before callback exists but haven't sorted, append current callback to last
					sortedNames = append(sortedNames, c.name)
					sortCallbackProcessor(cps[index])
				}
			}

			if c.after != "" { // if defined after callback
				if index := getRIndex(sortedNames, c.after); index != -1 {
					// if after callback already sorted, append current callback just before it
					sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
				} else if index := getRIndex(allNames, c.after); index != -1 {
					// if after callback exists but haven't sorted
					cp := cps[index]
					// set after callback's before callback to current callback
					if cp.before == "" {
						cp.before = c.name
					}
					sortCallbackProcessor(cp)
				}
			}

			// if current callback haven't been sorted, append it to last
			if getRIndex(sortedNames, c.name) == -1 {
				sortedNames = append(sortedNames, c.name)
			}
		}
	}

	for _, cp := range cps {
		sortCallbackProcessor(cp)
	}

	var sortedFuncs []*func(scope *Scope)
	for _, name := range sortedNames {
		if index := getRIndex(allNames, name); !cps[index].remove {
			sortedFuncs = append(sortedFuncs, cps[index].processor)
		}
	}

	return sortedFuncs
}

// reorder all registered processors, and reset CURD callbacks
func (c *Callback) reorder() {
	var creates, updates, deletes, queries, rowQueries []*CallbackProcessor

	for _, processor := range c.processors {
		switch processor.kind {
		case "create":
			creates = append(creates, processor)
		case "update":
			updates = append(updates, processor)
		case "delete":
			deletes = append(deletes, processor)
		case "query":
			queries = append(queries, processor)
		case "row_query":
			rowQueries = append(rowQueries, processor)
		}
	}

	c.creates = sortProcessors(creates)
	c.updates = sortProcessors(updates)
	c.deletes = sortProcessors(deletes)
	c.queries = sortProcessors(queries)
	c.rowQueries = sortProcessors(rowQueries)
}
<commit_msg>Don't sort processors that without name<commit_after>package gorm

import (
	"fmt"
)

// defaultCallback hold default callbacks defined by gorm
var defaultCallback = &Callback{}

// Callback contains callbacks that used when CURD objects
//   Field `creates` hold callbacks will be call when creating object
//   Field `updates` hold callbacks will be call when updating object
//   Field `deletes` hold callbacks will be call when deleting object
//   Field `queries` hold callbacks will be call when querying object with query methods like Find, First, Related, Association...
//   Field `rowQueries` hold callbacks will be call when querying object with Row, Rows...
//   Field `processors` hold all callback processors, will be used to generate above callbacks in order
type Callback struct {
	creates    []*func(scope *Scope)
	updates    []*func(scope *Scope)
	deletes    []*func(scope *Scope)
	queries    []*func(scope *Scope)
	rowQueries []*func(scope *Scope)
	processors []*CallbackProcessor
}

// CallbackProcessor contains all informations for a callback
type CallbackProcessor struct {
	name      string              // current callback's name
	before    string              // register current callback before a callback
	after     string              // register current callback after a callback
	replace   bool                // replace callbacks with same name
	remove    bool                // delete callbacks with same name
	kind      string              // callback type: create, update, delete, query, row_query
	processor *func(scope *Scope) // callback handler
	parent    *Callback
}

func (c *Callback) addProcessor(kind string) *CallbackProcessor {
	cp := &CallbackProcessor{kind: kind, parent: c}
	c.processors = append(c.processors, cp)
	return cp
}

func (c *Callback) clone() *Callback {
	return &Callback{
		creates:    c.creates,
		updates:    c.updates,
		deletes:    c.deletes,
		queries:    c.queries,
		processors: c.processors,
	}
}

// Create could be used to register callbacks for creating object
//     db.Callback().Create().After("gorm:create").Register("plugin:run_after_create", func(*Scope) {
//       // business logic
//       ...
//
//       // set error if some thing wrong happened, will rollback the creating
//       scope.Err(errors.New("error"))
//     })
func (c *Callback) Create() *CallbackProcessor {
	return c.addProcessor("create")
}

// Update could be used to register callbacks for updating object, refer `Create` for usage
func (c *Callback) Update() *CallbackProcessor {
	return c.addProcessor("update")
}

// Delete could be used to register callbacks for deleting object, refer `Create` for usage
func (c *Callback) Delete() *CallbackProcessor {
	return c.addProcessor("delete")
}

// Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
// refer `Create` for usage
func (c *Callback) Query() *CallbackProcessor {
	return c.addProcessor("query")
}

// RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
func (c *Callback) RowQuery() *CallbackProcessor {
	return c.addProcessor("row_query")
}

// After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
func (cp *CallbackProcessor) After(callbackName string) *CallbackProcessor {
	cp.after = callbackName
	return cp
}

// Before insert a new callback before callback `callbackName`, refer `Callbacks.Create`
func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
	cp.before = callbackName
	return cp
}

// Register a new callback, refer `Callbacks.Create`
func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
	cp.name = callbackName
	cp.processor = &callback
	cp.parent.reorder()
}

// Remove a registered callback
//     db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
func (cp *CallbackProcessor) Remove(callbackName string) {
	fmt.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
	cp.name = callbackName
	cp.remove = true
	cp.parent.reorder()
}

// Replace a registered callback with new callback
//     db.Callback().Create().Replace("gorm:update_time_stamp_when_create", func(*Scope) {
//		   scope.SetColumn("Created", now)
//		   scope.SetColumn("Updated", now)
//     })
func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
	fmt.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
	cp.name = callbackName
	cp.processor = &callback
	cp.replace = true
	cp.parent.reorder()
}

// Get registered callback
//    db.Callback().Create().Get("gorm:create")
func (cp *CallbackProcessor) Get(callbackName string) (callback func(scope *Scope)) {
	for _, p := range cp.parent.processors {
		if p.name == callbackName && p.kind == cp.kind && !cp.remove {
			return *p.processor
		}
	}
	return nil
}

// getRIndex get right index from string slice
func getRIndex(strs []string, str string) int {
	for i := len(strs) - 1; i >= 0; i-- {
		if strs[i] == str {
			return i
		}
	}
	return -1
}

// sortProcessors sort callback processors based on its before, after, remove, replace
func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
	var (
		allNames, sortedNames []string
		sortCallbackProcessor func(c *CallbackProcessor)
	)

	for _, cp := range cps {
		// show warning message the callback name already exists
		if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
			fmt.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
		}
		allNames = append(allNames, cp.name)
	}

	sortCallbackProcessor = func(c *CallbackProcessor) {
		if getRIndex(sortedNames, c.name) == -1 { // if not sorted
			if c.before != "" { // if defined before callback
				if index := getRIndex(sortedNames, c.before); index != -1 {
					// if before callback already sorted, append current callback just after it
					sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
				} else if index := getRIndex(allNames, c.before); index != -1 {
					// if before callback exists but haven't sorted, append current callback to last
					sortedNames = append(sortedNames, c.name)
					sortCallbackProcessor(cps[index])
				}
			}

			if c.after != "" { // if defined after callback
				if index := getRIndex(sortedNames, c.after); index != -1 {
					// if after callback already sorted, append current callback just before it
					sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
				} else if index := getRIndex(allNames, c.after); index != -1 {
					// if after callback exists but haven't sorted
					cp := cps[index]
					// set after callback's before callback to current callback
					if cp.before == "" {
						cp.before = c.name
					}
					sortCallbackProcessor(cp)
				}
			}

			// if current callback haven't been sorted, append it to last
			if getRIndex(sortedNames, c.name) == -1 {
				sortedNames = append(sortedNames, c.name)
			}
		}
	}

	for _, cp := range cps {
		sortCallbackProcessor(cp)
	}

	var sortedFuncs []*func(scope *Scope)
	for _, name := range sortedNames {
		if index := getRIndex(allNames, name); !cps[index].remove {
			sortedFuncs = append(sortedFuncs, cps[index].processor)
		}
	}

	return sortedFuncs
}

// reorder all registered processors, and reset CURD callbacks
func (c *Callback) reorder() {
	var creates, updates, deletes, queries, rowQueries []*CallbackProcessor

	for _, processor := range c.processors {
		if processor.name != "" {
			switch processor.kind {
			case "create":
				creates = append(creates, processor)
			case "update":
				updates = append(updates, processor)
			case "delete":
				deletes = append(deletes, processor)
			case "query":
				queries = append(queries, processor)
			case "row_query":
				rowQueries = append(rowQueries, processor)
			}
		}
	}

	c.creates = sortProcessors(creates)
	c.updates = sortProcessors(updates)
	c.deletes = sortProcessors(deletes)
	c.queries = sortProcessors(queries)
	c.rowQueries = sortProcessors(rowQueries)
}
<|endoftext|>


<commit_before>package nodeflock

import (
	"fmt"
	"os"
	"time"
)

type CallResult struct {
	Result interface{} `json:"result"`
	Error  string      `json:"error"`
	Log    []struct {
		Level string        `json:"level"`
		Data  []interface{} `json:"data"`
		Stack []string      `json:"stack"`
	} `json:"log"`
}
type processRentalApplicationResponse struct {
	err     error
	process *process
}

type processRentalApplication struct {
	chanResponse chan processRentalApplicationResponse
}

type Flock struct {
	size             int
	processes        []*process
	chanRentalReturn chan *process
	chanRentalApply  chan chan processRentalApplicationResponse //chan *processRentalApplication
	idCounter        int
}

func NewFlock(jsModuleFile string, size int) (f *Flock, err error) {
	f = &Flock{
		size:             size,
		processes:        []*process{},
		chanRentalReturn: make(chan *process),
		chanRentalApply:  make(chan chan processRentalApplicationResponse),
	}

	go func() {
		busy := map[int]bool{}
		applications := []chan processRentalApplicationResponse{} //[]*processRentalApplication{}
		chanProcessDied := make(chan int)
		lastSourceChange := getFileChange(jsModuleFile)
		for {
			// is anyone obsolote
			numOkProcesses := 0
			for _, deathRowProcess := range f.processes {
				if _, isBusy := busy[deathRowProcess.id]; !isBusy && deathRowProcess.sourceFileChange < lastSourceChange {
					deathRowProcess.kill()
				} else {
					numOkProcesses++
				}
			}

			// spawn processes, if we lost some
			for i := numOkProcesses; i < f.size; i++ {
				p, processErr := newProcess(jsModuleFile, lastSourceChange, f.getId(), chanProcessDied)
				if processErr != nil {
					err = processErr
					return
				}
				f.processes = append(f.processes, p)
			}

			select {
			case <-time.After(time.Millisecond):
				lastSourceChange = getFileChange(jsModuleFile)
				//fmt.Println("checking source", lastSourceChange)
			case deadProcessID := <-chanProcessDied:
				// a process died
				// handle error
				// remove process
				f.processes = filterProcesses(f.processes, func(process *process) bool {
					return process.id != deadProcessID
				})
				delete(busy, deadProcessID)
			case rentalApplication := <-f.chanRentalApply:
				//fmt.Println("incoming application")
				applications = append(applications, rentalApplication)
			case returnedProcess := <-f.chanRentalReturn:
				// back to the
				//fmt.Println("got one back")
				delete(busy, returnedProcess.id)
			}
			// let us see if anyone has time
			processedApplications := []chan processRentalApplicationResponse{}
		applicationLoop:
			for _, application := range applications {

				if len(busy) == len(f.processes) {
					// they are all busy
					fmt.Println("everybody is busy")
					break
				}
				var lonelyProcess *process
				lonelyProcess = nil
				for _, process := range f.processes {
					_, isBusy := busy[process.id]
					if !isBusy && process.sourceFileChange == lastSourceChange {
						if lonelyProcess == nil {
							lonelyProcess = process
						}
						if lonelyProcess.lastCall < process.lastCall {
							lonelyProcess = process
						}
					}
				}

				// this one is not busy
				processedApplications = append(processedApplications, application)
				// make the applicant happy
				//fmt.Println("sending process to applicant", process.id)
				if lonelyProcess != nil {
					busy[lonelyProcess.id] = true
					application <- processRentalApplicationResponse{
						process: lonelyProcess,
						err:     nil,
					}
					continue applicationLoop

				} else {
					fmt.Println("could not find lonely process")
				}

			}
			// clean up
			cleanedApplications := []chan processRentalApplicationResponse{}
		applicationLoopClean:
			for _, application := range applications {
				for _, processedApplication := range processedApplications {
					if processedApplication == application {
						continue applicationLoopClean
					}
				}
				cleanedApplications = append(cleanedApplications, application)
			}
			applications = cleanedApplications
			// fmt.Println(busy)
			//fmt.Println(time.Now().Sub(start))

		}
	}()

	return
}

func getFileChange(file string) int64 {
	fileInfo, err := os.Stat(file)
	if err != nil {
		return int64(0)
	}
	return fileInfo.ModTime().UnixNano()
}

func filterProcesses(processes []*process, filter func(*process) bool) (cleaned []*process) {
	survivors := []*process{}
	for _, process := range processes {
		if filter(process) {
			survivors = append(survivors, process)
		}
	}
	return survivors
}

func (f *Flock) getId() int {
	f.idCounter++
	return f.idCounter
}

func (f *Flock) borrowProcess() (p *process, err error) {
	// dummy implementation
	application := make(chan processRentalApplicationResponse)
	f.chanRentalApply <- application
	response := <-application
	return response.process, response.err
}

func (f *Flock) returnProcess(p *process) {
	//fmt.Println("returning process", p.id)
	f.chanRentalReturn <- p
}

func (f *Flock) CallRaw(jsonBytes []byte) (rawResult []byte, err error) {
	p, processErr := f.borrowProcess()
	if processErr != nil {
		err = processErr
		return
	}
	//fmt.Println("got a process", p.id)
	rawResult, callErr := p.rawCallJS(jsonBytes)
	f.returnProcess(p)
	return rawResult, callErr
}

func (f *Flock) Call(funcName string, args ...interface{}) (result CallResult, err error) {
	return
}
<commit_msg>verbose source file checkoung<commit_after>package nodeflock

import (
	"fmt"
	"os"
	"time"
)

type CallResult struct {
	Result interface{} `json:"result"`
	Error  string      `json:"error"`
	Log    []struct {
		Level string        `json:"level"`
		Data  []interface{} `json:"data"`
		Stack []string      `json:"stack"`
	} `json:"log"`
}
type processRentalApplicationResponse struct {
	err     error
	process *process
}

type processRentalApplication struct {
	chanResponse chan processRentalApplicationResponse
}

type Flock struct {
	size             int
	processes        []*process
	chanRentalReturn chan *process
	chanRentalApply  chan chan processRentalApplicationResponse //chan *processRentalApplication
	idCounter        int
}

func NewFlock(jsModuleFile string, size int) (f *Flock, err error) {
	f = &Flock{
		size:             size,
		processes:        []*process{},
		chanRentalReturn: make(chan *process),
		chanRentalApply:  make(chan chan processRentalApplicationResponse),
	}

	go func() {
		busy := map[int]bool{}
		applications := []chan processRentalApplicationResponse{} //[]*processRentalApplication{}
		chanProcessDied := make(chan int)
		lastSourceChange := getFileChange(jsModuleFile)
		for {
			// is anyone obsolote
			numOkProcesses := 0
			for _, deathRowProcess := range f.processes {
				if _, isBusy := busy[deathRowProcess.id]; !isBusy && deathRowProcess.sourceFileChange < lastSourceChange {
					deathRowProcess.kill()
				} else {
					numOkProcesses++
				}
			}

			// spawn processes, if we lost some
			for i := numOkProcesses; i < f.size; i++ {
				p, processErr := newProcess(jsModuleFile, lastSourceChange, f.getId(), chanProcessDied)
				if processErr != nil {
					err = processErr
					return
				}
				f.processes = append(f.processes, p)
			}

			select {
			case <-time.After(time.Millisecond):
				newSourceChange := getFileChange(jsModuleFile)
				if newSourceChange != lastSourceChange {
					fmt.Println("source file update", jsModuleFile, lastSourceChange, newSourceChange)
				}
				lastSourceChange = newSourceChange
				//fmt.Println("checking source", lastSourceChange)
			case deadProcessID := <-chanProcessDied:
				// a process died
				// handle error
				// remove process
				f.processes = filterProcesses(f.processes, func(process *process) bool {
					return process.id != deadProcessID
				})
				delete(busy, deadProcessID)
			case rentalApplication := <-f.chanRentalApply:
				//fmt.Println("incoming application")
				applications = append(applications, rentalApplication)
			case returnedProcess := <-f.chanRentalReturn:
				// back to the
				//fmt.Println("got one back")
				delete(busy, returnedProcess.id)
			}
			// let us see if anyone has time
			processedApplications := []chan processRentalApplicationResponse{}
		applicationLoop:
			for _, application := range applications {

				if len(busy) == len(f.processes) {
					// they are all busy
					fmt.Println("everybody is busy")
					break
				}
				var lonelyProcess *process
				lonelyProcess = nil
				for _, process := range f.processes {
					_, isBusy := busy[process.id]
					if !isBusy && process.sourceFileChange == lastSourceChange {
						if lonelyProcess == nil {
							lonelyProcess = process
						}
						if lonelyProcess.lastCall < process.lastCall {
							lonelyProcess = process
						}
					}
				}

				// this one is not busy
				processedApplications = append(processedApplications, application)
				// make the applicant happy
				//fmt.Println("sending process to applicant", process.id)
				if lonelyProcess != nil {
					busy[lonelyProcess.id] = true
					application <- processRentalApplicationResponse{
						process: lonelyProcess,
						err:     nil,
					}
					continue applicationLoop

				} else {
					fmt.Println("could not find lonely process")
				}

			}
			// clean up
			cleanedApplications := []chan processRentalApplicationResponse{}
		applicationLoopClean:
			for _, application := range applications {
				for _, processedApplication := range processedApplications {
					if processedApplication == application {
						continue applicationLoopClean
					}
				}
				cleanedApplications = append(cleanedApplications, application)
			}
			applications = cleanedApplications
			// fmt.Println(busy)
			//fmt.Println(time.Now().Sub(start))

		}
	}()

	return
}

func getFileChange(file string) int64 {
	fileInfo, err := os.Stat(file)
	if err != nil {
		return int64(0)
	}
	return fileInfo.ModTime().UnixNano()
}

func filterProcesses(processes []*process, filter func(*process) bool) (cleaned []*process) {
	survivors := []*process{}
	for _, process := range processes {
		if filter(process) {
			survivors = append(survivors, process)
		}
	}
	return survivors
}

func (f *Flock) getId() int {
	f.idCounter++
	return f.idCounter
}

func (f *Flock) borrowProcess() (p *process, err error) {
	// dummy implementation
	application := make(chan processRentalApplicationResponse)
	f.chanRentalApply <- application
	response := <-application
	return response.process, response.err
}

func (f *Flock) returnProcess(p *process) {
	//fmt.Println("returning process", p.id)
	f.chanRentalReturn <- p
}

func (f *Flock) CallRaw(jsonBytes []byte) (rawResult []byte, err error) {
	p, processErr := f.borrowProcess()
	if processErr != nil {
		err = processErr
		return
	}
	//fmt.Println("got a process", p.id)
	rawResult, callErr := p.rawCallJS(jsonBytes)
	f.returnProcess(p)
	return rawResult, callErr
}

func (f *Flock) Call(funcName string, args ...interface{}) (result CallResult, err error) {
	return
}
<|endoftext|>


<commit_before>package service

import (
	"sync"
	"time"

	"github.com/ReneGa/tweetcount-microservices/recorder/datamapper"
	"github.com/ReneGa/tweetcount-microservices/recorder/domain"
	"github.com/ReneGa/tweetcount-microservices/recorder/gateway"
)

type Tweets struct {
	sync.Mutex
	DataMapper             datamapper.TweetBucketsPerQuery
	Gateway                gateway.Tweets
	tweetConsumersForQuery map[string][]domain.Tweets
}

const BucketsWriteBufferSize = 16

func copyTweets(from chan domain.Tweet, to chan domain.Tweet) {
	for tweet := range from {
		to <- tweet
	}
}

func copyStoppableTweets(from domain.Tweets, to chan domain.Tweet, stop chan bool) {
	for {
		select {
		case tweet := <-from.Data:
			select {
			case to <- tweet:
			case <-stop:
				from.Stop <- true
				return

			}
		case <-stop:
			from.Stop <- true
			return
		}
	}
}

func (t *Tweets) removeConsumerForQuery(query string, i int) {
	consumers := t.tweetConsumersForQuery[query]
	t.tweetConsumersForQuery[query] = append(
		consumers[0:i],
		consumers[i+1:len(consumers)]...,
	)
}

func panicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}

func (t *Tweets) writeToBuckets(query string, tweetsToWriteToBuckets chan domain.Tweet) {

	var bucketWriter datamapper.TweetBucketWriter
	bucketWriterCreator, err := t.DataMapper.BucketWriterCreator(query)
	panicOnErr(err)

	for tweet := range tweetsToWriteToBuckets {

		if bucketWriter == nil {
			bucketWriter = bucketWriterCreator.CreateForTime(tweet.Time)
			panicOnErr(bucketWriter.Open())

		} else if bucketWriterCreator.ShouldCreateNew(tweet.Time) {
			panicOnErr(bucketWriter.Close())
			bucketWriter = bucketWriterCreator.CreateForTime(tweet.Time)
			panicOnErr(bucketWriter.Open())
		}

		panicOnErr(bucketWriter.Append(tweet))
	}
	panicOnErr(bucketWriter.Close())
}

func (t *Tweets) broadcastTweets(query string, tweetsForQuery domain.Tweets, tweetsToWriteToBuckets chan domain.Tweet) {
	defer close(tweetsToWriteToBuckets)
	for tweet := range tweetsForQuery.Data {
		tweetsToWriteToBuckets <- tweet
		t.Lock()
		for i, consumer := range t.tweetConsumersForQuery[query] {
			select {
			case consumer.Data <- tweet:
			case <-consumer.Stop:
				t.removeConsumerForQuery(query, i)
				if len(t.tweetConsumersForQuery[query]) == 0 {
					delete(t.tweetConsumersForQuery, query)
					t.Unlock()
					tweetsForQuery.Stop <- true
					return
				}
			}
		}
		t.Unlock()
	}
}

func (t *Tweets) freshTweets(query string) domain.Tweets {
	t.Lock()
	defer t.Unlock()

	if t.tweetConsumersForQuery == nil {
		t.tweetConsumersForQuery = map[string][]domain.Tweets{}
	}

	if _, ok := t.tweetConsumersForQuery[query]; !ok {
		t.tweetConsumersForQuery[query] = []domain.Tweets{}

		tweetsForQuery := t.Gateway.Tweets(query)
		tweetsToWriteToBuckets := make(chan domain.Tweet, BucketsWriteBufferSize)

		go t.writeToBuckets(query, tweetsToWriteToBuckets)
		go t.broadcastTweets(query, tweetsForQuery, tweetsToWriteToBuckets)
	}

	data := make(chan domain.Tweet)
	stop := make(chan bool)
	tweets := domain.Tweets{
		Data: data,
		Stop: stop,
	}

	t.tweetConsumersForQuery[query] = append(t.tweetConsumersForQuery[query], tweets)

	return tweets
}

func (t *Tweets) Tweets(query string, startTime time.Time) domain.Tweets {
	out := make(chan domain.Tweet)
	stop := make(chan bool)
	tweets := domain.Tweets{
		Data: out,
		Stop: stop,
	}
	freshTweets := t.freshTweets(query)
	replayTweets := make(chan domain.Tweet)
	go func() {
		panicOnErr(t.DataMapper.ReplayFrom(query, startTime, replayTweets))
	}()
	go func() {
		defer close(out)
		copyTweets(replayTweets, out)
		copyStoppableTweets(freshTweets, out, stop)
	}()
	return tweets
}
<commit_msg>Fix recorder removeConsumerForQuery method<commit_after>package service

import (
	"sync"
	"time"

	"github.com/ReneGa/tweetcount-microservices/recorder/datamapper"
	"github.com/ReneGa/tweetcount-microservices/recorder/domain"
	"github.com/ReneGa/tweetcount-microservices/recorder/gateway"
)

type Tweets struct {
	sync.Mutex
	DataMapper             datamapper.TweetBucketsPerQuery
	Gateway                gateway.Tweets
	tweetConsumersForQuery map[string][]domain.Tweets
}

const BucketsWriteBufferSize = 16

func copyTweets(from chan domain.Tweet, to chan domain.Tweet) {
	for tweet := range from {
		to <- tweet
	}
}

func copyStoppableTweets(from domain.Tweets, to chan domain.Tweet, stop chan bool) {
	for {
		select {
		case tweet := <-from.Data:
			select {
			case to <- tweet:
			case <-stop:
				from.Stop <- true
				return

			}
		case <-stop:
			from.Stop <- true
			return
		}
	}
}

func (t *Tweets) removeConsumerForQuery(query string, i int) {
	consumers := t.tweetConsumersForQuery[query]
	consumers[i] = consumers[len(consumers)-1]
	consumers = consumers[:len(consumers)-1]
	t.tweetConsumersForQuery[query] = consumers
}

func panicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}

func (t *Tweets) writeToBuckets(query string, tweetsToWriteToBuckets chan domain.Tweet) {

	var bucketWriter datamapper.TweetBucketWriter
	bucketWriterCreator, err := t.DataMapper.BucketWriterCreator(query)
	panicOnErr(err)

	for tweet := range tweetsToWriteToBuckets {

		if bucketWriter == nil {
			bucketWriter = bucketWriterCreator.CreateForTime(tweet.Time)
			panicOnErr(bucketWriter.Open())

		} else if bucketWriterCreator.ShouldCreateNew(tweet.Time) {
			panicOnErr(bucketWriter.Close())
			bucketWriter = bucketWriterCreator.CreateForTime(tweet.Time)
			panicOnErr(bucketWriter.Open())
		}

		panicOnErr(bucketWriter.Append(tweet))
	}
	panicOnErr(bucketWriter.Close())
}

func (t *Tweets) broadcastTweets(query string, tweetsForQuery domain.Tweets, tweetsToWriteToBuckets chan domain.Tweet) {
	defer close(tweetsToWriteToBuckets)
	for tweet := range tweetsForQuery.Data {
		tweetsToWriteToBuckets <- tweet
		t.Lock()
		for i, consumer := range t.tweetConsumersForQuery[query] {
			select {
			case consumer.Data <- tweet:
			case <-consumer.Stop:
				t.removeConsumerForQuery(query, i)
				if len(t.tweetConsumersForQuery[query]) == 0 {
					delete(t.tweetConsumersForQuery, query)
					t.Unlock()
					tweetsForQuery.Stop <- true
					return
				}
			}
		}
		t.Unlock()
	}
}

func (t *Tweets) freshTweets(query string) domain.Tweets {
	t.Lock()
	defer t.Unlock()

	if t.tweetConsumersForQuery == nil {
		t.tweetConsumersForQuery = map[string][]domain.Tweets{}
	}

	if _, ok := t.tweetConsumersForQuery[query]; !ok {
		t.tweetConsumersForQuery[query] = []domain.Tweets{}

		tweetsForQuery := t.Gateway.Tweets(query)
		tweetsToWriteToBuckets := make(chan domain.Tweet, BucketsWriteBufferSize)

		go t.writeToBuckets(query, tweetsToWriteToBuckets)
		go t.broadcastTweets(query, tweetsForQuery, tweetsToWriteToBuckets)
	}

	data := make(chan domain.Tweet)
	stop := make(chan bool)
	tweets := domain.Tweets{
		Data: data,
		Stop: stop,
	}

	t.tweetConsumersForQuery[query] = append(t.tweetConsumersForQuery[query], tweets)

	return tweets
}

func (t *Tweets) Tweets(query string, startTime time.Time) domain.Tweets {
	out := make(chan domain.Tweet)
	stop := make(chan bool)
	tweets := domain.Tweets{
		Data: out,
		Stop: stop,
	}
	freshTweets := t.freshTweets(query)
	replayTweets := make(chan domain.Tweet)
	go func() {
		panicOnErr(t.DataMapper.ReplayFrom(query, startTime, replayTweets))
	}()
	go func() {
		defer close(out)
		copyTweets(replayTweets, out)
		copyStoppableTweets(freshTweets, out, stop)
	}()
	return tweets
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package main

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

	"github.com/labstack/echo"
)

// Datacenter holds the datacenter response from datacenter-store
type Datacenter struct {
	ID              int    `json:"id"`
	GroupID         int    `json:"group_id"`
	Name            string `json:"name"`
	Type            string `json:"type"`
	Region          string `json:"region"`
	Username        string `json:"username"`
	Password        string `json:"password"`
	VCloudURL       string `json:"vcloud_url"`
	VseURL          string `json:"vse_url"`
	ExternalNetwork string `json:"external_network"`
}

// Validate the datacenter
func (d *Datacenter) Validate() error {
	if d.Name == "" {
		return errors.New("Datacenter name is empty")
	}

	if d.Type == "" {
		return errors.New("Datacenter type is empty")
	}

	if d.Username == "" {
		return errors.New("Datacenter username is empty")
	}

	if d.Password == "" {
		return errors.New("Datacenter password is empty")
	}

	if d.Type == "vcloud" && d.VCloudURL == "" {
		return errors.New("Datacenter vcloud url is empty")
	}

	return nil
}

// Map : maps a datacenter from a request's body and validates the input
func (d *Datacenter) Map(c echo.Context) *echo.HTTPError {
	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	err = json.Unmarshal(data, &d)
	if err != nil {
		return ErrBadReqBody
	}

	err = d.Validate()
	if err != nil {
		return ErrBadReqBody
	}

	return nil
}

// getDatacentersHandler : get all datacenters
func getDatacentersHandler(c echo.Context) error {
	au := authenticatedUser(c)
	query := fmt.Sprintf(`{"group_id": %d}`, au.GroupID)
	msg, err := n.Request("datacenter.find", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// getDatacenterHandler : get a datancenter by id
func getDatacenterHandler(c echo.Context) error {
	var query string
	au := authenticatedUser(c)

	if au.Admin {
		query = fmt.Sprintf(`{"id": %s}`, c.Param("datacenter"))
	} else {
		query = fmt.Sprintf(`{"id": %s, "group_id": %d}`, c.Param("datacenter"), au.GroupID)
	}

	msg, err := n.Request("datacenter.get", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// createDatacenterHandler : Endpoint to create a datacenter
func createDatacenterHandler(c echo.Context) error {
	var d Datacenter
	if d.Map(c) != nil {
		return ErrBadReqBody
	}

	au := authenticatedUser(c)
	d.GroupID = au.GroupID

	data, err := json.Marshal(d)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("datacenter.set", data, 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// updateDatacenterHandler : Updates a datacenter through its store
func updateDatacenterHandler(c echo.Context) error {
	var d Datacenter
	if d.Map(c) != nil {
		return ErrBadReqBody
	}

	au := authenticatedUser(c)
	if au.Admin != true || d.GroupID != au.GroupID {
		return ErrUnauthorized
	}

	data, err := json.Marshal(d)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("datacenter.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// deleteDatacenterHandler : Deletes a datancenter though its store
func deleteDatacenterHandler(c echo.Context) error {
	au := authenticatedUser(c)

	query := fmt.Sprintf(`{"id": %s, "group_id": %d}`, c.Param("datacenter"), au.GroupID)
	msg, err := n.Request("datacenter.del", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.String(http.StatusOK, "")
}
<commit_msg>do not check password is not mandatory anymore<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package main

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

	"github.com/labstack/echo"
)

// Datacenter holds the datacenter response from datacenter-store
type Datacenter struct {
	ID              int    `json:"id"`
	GroupID         int    `json:"group_id"`
	Name            string `json:"name"`
	Type            string `json:"type"`
	Region          string `json:"region"`
	Username        string `json:"username"`
	Password        string `json:"password"`
	VCloudURL       string `json:"vcloud_url"`
	VseURL          string `json:"vse_url"`
	ExternalNetwork string `json:"external_network"`
}

// Validate the datacenter
func (d *Datacenter) Validate() error {
	if d.Name == "" {
		return errors.New("Datacenter name is empty")
	}

	if d.Type == "" {
		return errors.New("Datacenter type is empty")
	}

	if d.Username == "" {
		return errors.New("Datacenter username is empty")
	}

	if d.Type == "vcloud" && d.VCloudURL == "" {
		return errors.New("Datacenter vcloud url is empty")
	}

	return nil
}

// Map : maps a datacenter from a request's body and validates the input
func (d *Datacenter) Map(c echo.Context) *echo.HTTPError {
	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	err = json.Unmarshal(data, &d)
	if err != nil {
		return ErrBadReqBody
	}

	err = d.Validate()
	if err != nil {
		return ErrBadReqBody
	}

	return nil
}

// getDatacentersHandler : get all datacenters
func getDatacentersHandler(c echo.Context) error {
	au := authenticatedUser(c)
	query := fmt.Sprintf(`{"group_id": %d}`, au.GroupID)
	msg, err := n.Request("datacenter.find", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// getDatacenterHandler : get a datancenter by id
func getDatacenterHandler(c echo.Context) error {
	var query string
	au := authenticatedUser(c)

	if au.Admin {
		query = fmt.Sprintf(`{"id": %s}`, c.Param("datacenter"))
	} else {
		query = fmt.Sprintf(`{"id": %s, "group_id": %d}`, c.Param("datacenter"), au.GroupID)
	}

	msg, err := n.Request("datacenter.get", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// createDatacenterHandler : Endpoint to create a datacenter
func createDatacenterHandler(c echo.Context) error {
	var d Datacenter
	if d.Map(c) != nil {
		return ErrBadReqBody
	}

	au := authenticatedUser(c)
	d.GroupID = au.GroupID

	data, err := json.Marshal(d)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("datacenter.set", data, 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// updateDatacenterHandler : Updates a datacenter through its store
func updateDatacenterHandler(c echo.Context) error {
	var d Datacenter
	if d.Map(c) != nil {
		return ErrBadReqBody
	}

	au := authenticatedUser(c)
	if au.Admin != true || d.GroupID != au.GroupID {
		return ErrUnauthorized
	}

	data, err := json.Marshal(d)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("datacenter.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// deleteDatacenterHandler : Deletes a datancenter though its store
func deleteDatacenterHandler(c echo.Context) error {
	au := authenticatedUser(c)

	query := fmt.Sprintf(`{"id": %s, "group_id": %d}`, c.Param("datacenter"), au.GroupID)
	msg, err := n.Request("datacenter.del", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.String(http.StatusOK, "")
}
<|endoftext|>


<commit_before>package controllers

import (
    "github.com/orc/db"
    "github.com/orc/mailer"
    "github.com/orc/sessions"
    "github.com/orc/utils"
    "net/http"
    "strconv"
    "time"
)

func (this *GridHandler) GetPersonRequestFromGroup() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }
    personId, err := strconv.Atoi(request["person_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    groupRegId, err := strconv.Atoi(request["group_reg_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    query := `SELECT users.id FROM users
        INNER JOIN faces ON faces.user_id = users.id
        INNER JOIN persons ON persons.face_id = faces.id
        WHERE persons.id = $1;`

    userId := db.Query(query, []interface{}{personId})[0].(map[string]interface{})["id"].(int)

    query = `SELECT forms.id as form_id, forms.name as form_name,
            params.id as param_id, params.name as param_name, params.required, params.editable,
            events.name as event_name, events.id as event_id,
            param_types.name as type, param_values.id as param_val_id, param_values.value
        FROM events_forms
        INNER JOIN events ON events.id = events_forms.event_id
        INNER JOIN forms ON forms.id = events_forms.form_id
        INNER JOIN params ON forms.id = params.form_id
        INNER JOIN param_types ON param_types.id = params.param_type_id
        INNER JOIN param_values ON params.id = param_values.param_id
        INNER JOIN reg_param_vals ON reg_param_vals.param_val_id = param_values.id
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN users ON users.id = faces.user_id
        INNER JOIN group_registrations ON group_registrations.event_id = events.id
        INNER JOIN groups ON group_registrations.group_id = groups.id
        INNER JOIN regs_groupregs ON regs_groupregs.reg_id = registrations.id
            AND regs_groupregs.groupreg_id = group_registrations.id
        WHERE group_registrations.id = $1 AND users.id = $2 ORDER BY forms.id, params.id;`

    utils.SendJSReply(
        map[string]interface{}{
            "result": "ok",
            "data": db.Query(query, []interface{}{groupRegId, userId}),
            "role": this.isAdmin()},
        this.Response)
}

func (this *GridHandler) GetPersonRequest() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    regId, err := strconv.Atoi(request["reg_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    query := `SELECT forms.id as form_id, forms.name as form_name,
            params.id as param_id, params.name as param_name, params.required, params.editable,
            events.name as event_name, events.id as event_id,
            param_types.name as type, param_values.id as param_val_id, param_values.value
        FROM events_forms
        INNER JOIN events ON events.id = events_forms.event_id
        INNER JOIN forms ON forms.id = events_forms.form_id
        INNER JOIN params ON forms.id = params.form_id
        INNER JOIN param_types ON param_types.id = params.param_type_id
        INNER JOIN param_values ON params.id = param_values.param_id
        INNER JOIN reg_param_vals ON reg_param_vals.param_val_id = param_values.id
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
            AND events.id = registrations.event_id
        WHERE registrations.id = $1 ORDER BY forms.id, params.id;`

    utils.SendJSReply(
        map[string]interface{}{
            "result": "ok",
            "data": db.Query(query, []interface{}{regId}),
            "role": this.isAdmin()},
        this.Response)
}

func (this *GridHandler) ConfirmOrRejectPersonRequest() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    if !this.isAdmin() {
        http.Redirect(this.Response, this.Request, "/", http.StatusForbidden)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    eventId, err := strconv.Atoi(request["event_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    regId, err := strconv.Atoi(request["reg_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    query := `SELECT param_values.value, users.id as user_id
        FROM reg_param_vals
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN param_values ON param_values.id = reg_param_vals.param_val_id
        INNER JOIN params ON params.id = param_values.param_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN users ON users.id = faces.user_id
        WHERE params.id in (4, 5, 6, 7) AND users.id in (
            SELECT users.id FROM registrations INNER JOIN events ON events.id = registrations.event_id
            INNER JOIN faces ON faces.id = registrations.face_id
            INNER JOIN users ON users.id = faces.user_id
            WHERE registrations.id = $1
        ) ORDER BY params.id;`

    data := db.Query(query, []interface{}{regId})

    if len(data) < 2 {
        utils.SendJSReply(
            map[string]interface{}{"result": "Нет регистрационных данных пользователя"},
            this.Response)
        return
    }

    email := data[0].(map[string]interface{})["value"].(string)

    to := data[1].(map[string]interface{})["value"].(string)
    to += " " + data[2].(map[string]interface{})["value"].(string)
    to += " " + data[3].(map[string]interface{})["value"].(string)

    event := db.Query(
        "SELECT name FROM events WHERE id=$1;",
        []interface{}{eventId})[0].(map[string]interface{})["name"].(string)

    if request["confirm"].(bool) {
        if eventId == 1 {
            utils.SendJSReply(map[string]interface{}{"result": "Эту заявку нельзя подтвердить письмом"}, this.Response)
        } else {
            if mailer.SendEmailToConfirmRejectPersonRequest(to, email, event, true) {
                utils.SendJSReply(map[string]interface{}{"result": "Письмо с подтверждением заявки отправлено"}, this.Response)
            } else {
                utils.SendJSReply(map[string]interface{}{"result": "Ошибка. Письмо с подтверждением заявки не отправлено"}, this.Response)
            }
        }

    } else {
        if eventId == 1 {
            utils.SendJSReply(map[string]interface{}{"result": "Эту заявку нельзя отклонить письмом"}, this.Response)
        } else {
            query := `DELETE
                FROM param_values USING reg_param_vals
                WHERE param_values.id in (SELECT reg_param_vals.param_val_id WHERE reg_param_vals.reg_id = $1);`
            db.Query(query, []interface{}{regId})

            query = `DELETE FROM registrations WHERE id = $1;`
            db.Query(query, []interface{}{regId})

            if mailer.SendEmailToConfirmRejectPersonRequest(to, email, event, false) {
                utils.SendJSReply(map[string]interface{}{"result": "Письмо с отклонением заявки отправлено"}, this.Response)
            } else {
                utils.SendJSReply(map[string]interface{}{"result": "Ошибка. Письмо с отклонением заявки не отправлено"}, this.Response)
            }
        }
    }
}

func (this *GridHandler) EditParams() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    date := time.Now().Format("2006-01-02T15:04:05Z00:00")

    for _, v := range request["data"].([]interface{}) {
        paramValId, err := strconv.Atoi(v.(map[string]interface{})["param_val_id"].(string))
        if err != nil {
            utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
            return
        }

        query := `SELECT params.name, params.required, params.editable
            FROM params
            INNER JOIN param_values ON param_values.param_id = params.id
            WHERE param_values.id = $1;`
        result := db.Query(query, []interface{}{paramValId})

        name := result[0].(map[string]interface{})["name"].(string)
        required := result[0].(map[string]interface{})["required"].(bool)
        editable := result[0].(map[string]interface{})["editable"].(bool)
        value := v.(map[string]interface{})["value"].(string)

        if required && utils.MatchRegexp("^[ \t\v\r\n\f]{0,}$", value) {
            utils.SendJSReply(map[string]interface{}{"result": "Заполните параметр '"+name+"'"}, this.Response)
            return
        }

        if !this.isAdmin() && !editable {
            continue
        }

        if value == "" {
            value = " "
        }

        paramValue := this.GetModel("param_values")
        paramValue.LoadModelData(map[string]interface{}{"value": value, "date": date})
        paramValue.LoadWherePart(map[string]interface{}{"id": paramValId})
        db.QueryUpdate_(paramValue).Scan()
    }

    utils.SendJSReply(map[string]interface{}{"result": "Изменения сохранены"}, this.Response)
}
<commit_msg>!AddPerson!: add persons who are not user of system<commit_after>package controllers

import (
    "github.com/orc/db"
    "github.com/orc/mailer"
    "github.com/orc/sessions"
    "github.com/orc/utils"
    "net/http"
    "strconv"
    "time"
)

func (this *GridHandler) GetPersonRequestFromGroup() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    faceId, err := strconv.Atoi(request["face_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    groupRegId, err := strconv.Atoi(request["group_reg_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    query := `SELECT forms.id as form_id, forms.name as form_name,
            params.id as param_id, params.name as param_name, params.required, params.editable,
            events.name as event_name, events.id as event_id,
            param_types.name as type, param_values.id as param_val_id, param_values.value
        FROM events_forms
        INNER JOIN events ON events.id = events_forms.event_id
        INNER JOIN forms ON forms.id = events_forms.form_id
        INNER JOIN params ON forms.id = params.form_id
        INNER JOIN param_types ON param_types.id = params.param_type_id
        INNER JOIN param_values ON params.id = param_values.param_id
        INNER JOIN reg_param_vals ON reg_param_vals.param_val_id = param_values.id
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN group_registrations ON group_registrations.event_id = events.id
        INNER JOIN groups ON group_registrations.group_id = groups.id
        INNER JOIN regs_groupregs ON regs_groupregs.reg_id = registrations.id
            AND regs_groupregs.groupreg_id = group_registrations.id
        WHERE group_registrations.id = $1 AND faces.id = $2 ORDER BY forms.id, params.id;`

    utils.SendJSReply(
        map[string]interface{}{
            "result": "ok",
            "data": db.Query(query, []interface{}{groupRegId, faceId}),
            "role": this.isAdmin()},
        this.Response)
}

func (this *GridHandler) GetPersonRequest() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    regId, err := strconv.Atoi(request["reg_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    query := `SELECT forms.id as form_id, forms.name as form_name,
            params.id as param_id, params.name as param_name, params.required, params.editable,
            events.name as event_name, events.id as event_id,
            param_types.name as type, param_values.id as param_val_id, param_values.value
        FROM events_forms
        INNER JOIN events ON events.id = events_forms.event_id
        INNER JOIN forms ON forms.id = events_forms.form_id
        INNER JOIN params ON forms.id = params.form_id
        INNER JOIN param_types ON param_types.id = params.param_type_id
        INNER JOIN param_values ON params.id = param_values.param_id
        INNER JOIN reg_param_vals ON reg_param_vals.param_val_id = param_values.id
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
            AND events.id = registrations.event_id
        WHERE registrations.id = $1 ORDER BY forms.id, params.id;`

    utils.SendJSReply(
        map[string]interface{}{
            "result": "ok",
            "data": db.Query(query, []interface{}{regId}),
            "role": this.isAdmin()},
        this.Response)
}

func (this *GridHandler) ConfirmOrRejectPersonRequest() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    if !this.isAdmin() {
        http.Redirect(this.Response, this.Request, "/", http.StatusForbidden)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    eventId, err := strconv.Atoi(request["event_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    regId, err := strconv.Atoi(request["reg_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    query := `SELECT param_values.value, users.id as user_id
        FROM reg_param_vals
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN param_values ON param_values.id = reg_param_vals.param_val_id
        INNER JOIN params ON params.id = param_values.param_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN users ON users.id = faces.user_id
        WHERE params.id in (4, 5, 6, 7) AND users.id in (
            SELECT users.id FROM registrations INNER JOIN events ON events.id = registrations.event_id
            INNER JOIN faces ON faces.id = registrations.face_id
            INNER JOIN users ON users.id = faces.user_id
            WHERE registrations.id = $1
        ) ORDER BY params.id;`

    data := db.Query(query, []interface{}{regId})

    if len(data) < 2 {
        utils.SendJSReply(
            map[string]interface{}{"result": "Нет регистрационных данных пользователя"},
            this.Response)
        return
    }

    email := data[0].(map[string]interface{})["value"].(string)

    to := data[1].(map[string]interface{})["value"].(string)
    to += " " + data[2].(map[string]interface{})["value"].(string)
    to += " " + data[3].(map[string]interface{})["value"].(string)

    event := db.Query(
        "SELECT name FROM events WHERE id=$1;",
        []interface{}{eventId})[0].(map[string]interface{})["name"].(string)

    if request["confirm"].(bool) {
        if eventId == 1 {
            utils.SendJSReply(map[string]interface{}{"result": "Эту заявку нельзя подтвердить письмом"}, this.Response)
        } else {
            if mailer.SendEmailToConfirmRejectPersonRequest(to, email, event, true) {
                utils.SendJSReply(map[string]interface{}{"result": "Письмо с подтверждением заявки отправлено"}, this.Response)
            } else {
                utils.SendJSReply(map[string]interface{}{"result": "Ошибка. Письмо с подтверждением заявки не отправлено"}, this.Response)
            }
        }

    } else {
        if eventId == 1 {
            utils.SendJSReply(map[string]interface{}{"result": "Эту заявку нельзя отклонить письмом"}, this.Response)
        } else {
            query := `DELETE
                FROM param_values USING reg_param_vals
                WHERE param_values.id in (SELECT reg_param_vals.param_val_id WHERE reg_param_vals.reg_id = $1);`
            db.Query(query, []interface{}{regId})

            query = `DELETE FROM registrations WHERE id = $1;`
            db.Query(query, []interface{}{regId})

            if mailer.SendEmailToConfirmRejectPersonRequest(to, email, event, false) {
                utils.SendJSReply(map[string]interface{}{"result": "Письмо с отклонением заявки отправлено"}, this.Response)
            } else {
                utils.SendJSReply(map[string]interface{}{"result": "Ошибка. Письмо с отклонением заявки не отправлено"}, this.Response)
            }
        }
    }
}

func (this *GridHandler) EditParams() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    date := time.Now().Format("2006-01-02T15:04:05Z00:00")

    for _, v := range request["data"].([]interface{}) {
        paramValId, err := strconv.Atoi(v.(map[string]interface{})["param_val_id"].(string))
        if err != nil {
            utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
            return
        }

        query := `SELECT params.name, params.required, params.editable
            FROM params
            INNER JOIN param_values ON param_values.param_id = params.id
            WHERE param_values.id = $1;`
        result := db.Query(query, []interface{}{paramValId})

        name := result[0].(map[string]interface{})["name"].(string)
        required := result[0].(map[string]interface{})["required"].(bool)
        editable := result[0].(map[string]interface{})["editable"].(bool)
        value := v.(map[string]interface{})["value"].(string)

        if required && utils.MatchRegexp("^[ \t\v\r\n\f]{0,}$", value) {
            utils.SendJSReply(map[string]interface{}{"result": "Заполните параметр '"+name+"'"}, this.Response)
            return
        }

        if !this.isAdmin() && !editable {
            continue
        }

        if value == "" {
            value = " "
        }

        paramValue := this.GetModel("param_values")
        paramValue.LoadModelData(map[string]interface{}{"value": value, "date": date})
        paramValue.LoadWherePart(map[string]interface{}{"id": paramValId})
        db.QueryUpdate_(paramValue).Scan()
    }

    utils.SendJSReply(map[string]interface{}{"result": "Изменения сохранены"}, this.Response)
}

func (this *Handler) AddPerson() {
    if !sessions.CheckSession(this.Response, this.Request) {
        http.Redirect(this.Response, this.Request, "/", http.StatusUnauthorized)
        return
    }

    request, err := utils.ParseJS(this.Request, this.Response)
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    groupId, err := strconv.Atoi(request["group_id"].(string))
    if err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    var faceId int
    face := this.GetModel("faces")
    db.QueryInsert_(face, "RETURNING id").Scan(&faceId)

    persons := this.GetModel("persons")
    persons.LoadModelData(map[string]interface{}{"face_id": faceId, "group_id": groupId, "status": true, "token": " "})
    db.QueryInsert_(persons, "").Scan()

    var regId int
    registration := this.GetModel("registrations")
    registration.LoadModelData(map[string]interface{}{"face_id": faceId, "event_id": 1})
    db.QueryInsert_(registration, "RETURNING id").Scan(&regId)

    if err = this.InsertUserParams(regId, request["data"].([]interface{})); err != nil {
        utils.SendJSReply(map[string]interface{}{"result": err.Error()}, this.Response)
        return
    }

    utils.SendJSReply(map[string]interface{}{"result": "ok"}, this.Response)
}
<|endoftext|>


<commit_before>package registry

import (
	"github.com/docker/distribution/manifest/schema1"
	dockerregistry "github.com/heroku/docker-registry-client/registry"
)

type herokuWrapper struct {
	*dockerregistry.Registry
}

// Convert between types. dockerregistry returns the *same* type but from a
// vendored library so go freaks out. Eugh.
// TODO: The only thing we care about here for now is history. Frankly it might
// be easier to convert it to JSON and back.
func (h herokuWrapper) Manifest(repository, reference string) ([]schema1.History, error) {
	manifest, err := h.Registry.Manifest(repository, reference)
	if err != nil || manifest == nil {
		return manifest, err
	}
	var result []schema1.History
	for _, item := range manifest.History {
		result = append(result, schema1.History{item.V1Compatibility})
	}
	return result, err
}
<commit_msg>fix typo<commit_after>package registry

import (
	"github.com/docker/distribution/manifest/schema1"
	dockerregistry "github.com/heroku/docker-registry-client/registry"
)

type herokuWrapper struct {
	*dockerregistry.Registry
}

// Convert between types. dockerregistry returns the *same* type but from a
// vendored library so go freaks out. Eugh.
// TODO: The only thing we care about here for now is history. Frankly it might
// be easier to convert it to JSON and back.
func (h herokuWrapper) Manifest(repository, reference string) ([]schema1.History, error) {
	manifest, err := h.Registry.Manifest(repository, reference)
	if err != nil || manifest == nil {
		return nil, err
	}
	var result []schema1.History
	for _, item := range manifest.History {
		result = append(result, schema1.History{item.V1Compatibility})
	}
	return result, err
}
<|endoftext|>