<commit_before>package models

import (
	"errors"
	"fmt"
	"strings"

	envmanModels "github.com/bitrise-io/envman/models"
	stepmanModels "github.com/bitrise-io/stepman/models"
)

// Normalize ...
func (config *BitriseDataModel) Normalize() error {
	for _, workflow := range config.Workflows {
		if err := workflow.Normalize(); err != nil {
			return err
		}
	}
	for _, env := range config.App.Environments {
		if err := env.Normalize(); err != nil {
			return err
		}
	}
	return nil
}

func containsWorkflowName(title string, workflowStack []string) bool {
	for _, t := range workflowStack {
		if t == title {
			return true
		}
	}
	return false
}

func removeWorkflowName(title string, workflowStack []string) []string {
	newStack := []string{}
	for _, t := range workflowStack {
		if t != title {
			newStack = append(newStack, t)
		}
	}
	return newStack
}

func checkWorkflowReferenceCycle(workflowID string, workflow WorkflowModel, bitriseConfig BitriseDataModel, workflowStack []string) error {
	if containsWorkflowName(workflowID, workflowStack) {
		stackStr := ""
		for _, aWorkflowID := range workflowStack {
			stackStr += aWorkflowID + " -> "
		}
		stackStr += workflowID
		return fmt.Errorf("Workflow reference cycle found: %s", stackStr)
	}
	workflowStack = append(workflowStack, workflowID)

	for _, beforeWorkflowName := range workflow.BeforeRun {
		beforeWorkflow, exist := bitriseConfig.Workflows[beforeWorkflowName]
		if !exist {
			return errors.New("Workflow not exist wit name " + beforeWorkflowName)
		}

		err := checkWorkflowReferenceCycle(beforeWorkflowName, beforeWorkflow, bitriseConfig, workflowStack)
		if err != nil {
			return err
		}
	}

	for _, afterWorkflowName := range workflow.AfterRun {
		afterWorkflow, exist := bitriseConfig.Workflows[afterWorkflowName]
		if !exist {
			return errors.New("Workflow not exist wit name " + afterWorkflowName)
		}

		err := checkWorkflowReferenceCycle(afterWorkflowName, afterWorkflow, bitriseConfig, workflowStack)
		if err != nil {
			return err
		}
	}

	workflowStack = removeWorkflowName(workflowID, workflowStack)

	return nil
}

// Validate ...
func (config *BitriseDataModel) Validate() error {
	for ID, workflow := range config.Workflows {
		if err := workflow.Validate(ID); err != nil {
			return err
		}
		if err := checkWorkflowReferenceCycle(ID, workflow, *config, []string{}); err != nil {
			return err
		}
	}
	return nil
}

// FillMissingDefaults ...
func (config *BitriseDataModel) FillMissingDefaults() error {
	for title, workflow := range config.Workflows {
		if err := workflow.FillMissingDefaults(title); err != nil {
			return err
		}
	}
	for _, env := range config.App.Environments {
		if err := env.FillMissingDefaults(); err != nil {
			return err
		}
	}
	return nil
}

// Normalize ...
func (workflow *WorkflowModel) Normalize() error {
	for _, env := range workflow.Environments {
		if err := env.Normalize(); err != nil {
			return err
		}
	}
	return nil
}

// FillMissingDefaults ...
func (workflow *WorkflowModel) FillMissingDefaults(title string) error {
	for _, env := range workflow.Environments {
		if err := env.FillMissingDefaults(); err != nil {
			return err
		}
	}
	if workflow.Title == "" {
		workflow.Title = title
	}
	return nil
}

// Validate ...
func (workflow *WorkflowModel) Validate(title string) error {
	// Validate envs
	for _, env := range workflow.Environments {
		if err := env.Validate(); err != nil {
			return err
		}
	}
	return nil
}

// MergeEnvironmentWith ...
func MergeEnvironmentWith(env *envmanModels.EnvironmentItemModel, otherEnv envmanModels.EnvironmentItemModel) error {
	// merge key-value
	key, _, err := env.GetKeyValuePair()
	if err != nil {
		return err
	}

	otherKey, otherValue, err := otherEnv.GetKeyValuePair()
	if err != nil {
		return err
	}

	if otherKey != key {
		return errors.New("Env keys are diferent")
	}

	(*env)[key] = otherValue

	//merge options
	options, err := env.GetOptions()
	if err != nil {
		return err
	}

	otherOptions, err := otherEnv.GetOptions()
	if err != nil {
		return err
	}
	if otherOptions.Title != nil {
		if options.Title == nil {
			options.Title = new(string)
		}
		*options.Title = *otherOptions.Title
	}
	if otherOptions.Description != nil {
		if options.Description == nil {
			options.Description = new(string)
		}
		*options.Description = *otherOptions.Description
	}
	if len(otherOptions.ValueOptions) > 0 {
		options.ValueOptions = otherOptions.ValueOptions
	}
	if otherOptions.IsRequired != nil {
		if options.IsRequired == nil {
			options.IsRequired = new(bool)
		}
		*options.IsRequired = *otherOptions.IsRequired
	}
	if otherOptions.IsExpand != nil {
		if options.IsExpand == nil {
			options.IsExpand = new(bool)
		}
		*options.IsExpand = *otherOptions.IsExpand
	}
	if otherOptions.IsDontChangeValue != nil {
		if options.IsDontChangeValue == nil {
			options.IsDontChangeValue = new(bool)
		}
		*options.IsDontChangeValue = *otherOptions.IsDontChangeValue
	}
	(*env)[envmanModels.OptionsKey] = options
	return nil
}

// MergeStepWith ...
func MergeStepWith(step, otherStep stepmanModels.StepModel) (stepmanModels.StepModel, error) {
	if err := step.FillMissingDefaults(); err != nil {
		return stepmanModels.StepModel{}, err
	}

	if otherStep.Title != nil {
		if step.Title == nil {
			step.Title = new(string)
		}
		*step.Title = *otherStep.Title
	}
	if otherStep.Description != nil {
		if step.Description == nil {
			step.Description = new(string)
		}
		*step.Description = *otherStep.Description
	}
	if otherStep.Summary != nil {
		if step.Summary == nil {
			step.Summary = new(string)
		}
		*step.Summary = *otherStep.Summary
	}
	if otherStep.Website != nil {
		if step.Website == nil {
			step.Website = new(string)
		}
		*step.Website = *otherStep.Website
	}
	if otherStep.SourceCodeURL != nil {
		if step.SourceCodeURL == nil {
			step.SourceCodeURL = new(string)
		}
		*step.SourceCodeURL = *otherStep.SourceCodeURL
	}
	if otherStep.SupportURL != nil {
		if step.SupportURL == nil {
			step.SupportURL = new(string)
		}
		*step.SupportURL = *otherStep.SupportURL
	}
	if otherStep.Source.Git != nil {
		if step.Source.Git == nil {
			step.Source.Git = new(string)
		}
		*step.Source.Git = *otherStep.Source.Git
	}
	if len(otherStep.Dependencies) > 0 {
		step.Dependencies = otherStep.Dependencies
	}
	if len(otherStep.HostOsTags) > 0 {
		step.HostOsTags = otherStep.HostOsTags
	}
	if len(otherStep.ProjectTypeTags) > 0 {
		step.ProjectTypeTags = otherStep.ProjectTypeTags
	}
	if len(otherStep.TypeTags) > 0 {
		step.TypeTags = otherStep.TypeTags
	}
	if otherStep.IsRequiresAdminUser != nil {
		if step.IsRequiresAdminUser == nil {
			step.IsRequiresAdminUser = new(bool)
		}
		*step.IsRequiresAdminUser = *otherStep.IsRequiresAdminUser
	}
	if otherStep.IsAlwaysRun != nil {
		if step.IsAlwaysRun == nil {
			step.IsAlwaysRun = new(bool)
		}
		*step.IsAlwaysRun = *otherStep.IsAlwaysRun
	}
	if otherStep.IsSkippable != nil {
		if step.IsSkippable == nil {
			step.IsSkippable = new(bool)
		}
		*step.IsSkippable = *otherStep.IsSkippable
	}
	if otherStep.RunIf != nil {
		if step.RunIf == nil {
			step.RunIf = new(string)
		}
		*step.RunIf = *otherStep.RunIf
	}

	for _, input := range step.Inputs {
		key, _, err := input.GetKeyValuePair()
		if err != nil {
			return stepmanModels.StepModel{}, err
		}
		otherInput, found := getInputByKey(otherStep, key)
		if found {
			err := MergeEnvironmentWith(&input, otherInput)
			if err != nil {
				return stepmanModels.StepModel{}, err
			}
		}
	}

	for _, output := range step.Outputs {
		key, _, err := output.GetKeyValuePair()
		if err != nil {
			return stepmanModels.StepModel{}, err
		}
		otherOutput, found := getOutputByKey(otherStep, key)
		if found {
			err := MergeEnvironmentWith(&output, otherOutput)
			if err != nil {
				return stepmanModels.StepModel{}, err
			}
		}
	}

	return step, nil
}

func getInputByKey(step stepmanModels.StepModel, key string) (envmanModels.EnvironmentItemModel, bool) {
	for _, input := range step.Inputs {
		k, _, err := input.GetKeyValuePair()
		if err != nil {
			return envmanModels.EnvironmentItemModel{}, false
		}

		if k == key {
			return input, true
		}
	}
	return envmanModels.EnvironmentItemModel{}, false
}

func getOutputByKey(step stepmanModels.StepModel, key string) (envmanModels.EnvironmentItemModel, bool) {
	for _, output := range step.Outputs {
		k, _, err := output.GetKeyValuePair()
		if err != nil {
			return envmanModels.EnvironmentItemModel{}, false
		}

		if k == key {
			return output, true
		}
	}
	return envmanModels.EnvironmentItemModel{}, false
}

// GetStepIDStepDataPair ...
func GetStepIDStepDataPair(stepListItem StepListItemModel) (string, stepmanModels.StepModel, error) {
	if len(stepListItem) > 1 {
		return "", stepmanModels.StepModel{}, errors.New("StepListItem contains more than 1 key-value pair!")
	}
	for key, value := range stepListItem {
		return key, value, nil
	}
	return "", stepmanModels.StepModel{}, errors.New("StepListItem does not contain a key-value pair!")
}

// CreateStepIDDataFromString ...
// compositeVersionStr examples:
//  * local path:
//    * path::~/path/to/step/dir
//  * direct git url and branch or tag:
//    * git::https://github.com/bitrise-io/steps-timestamp.git@master
//  * full ID with steplib, stepid and version:
//    * https://github.com/bitrise-io/bitrise-steplib::script@2.0.0
//  * only stepid and version (requires a default steplib source to be provided):
//    * script@2.0.0
//  * only stepid, latest version will be used (requires a default steplib source to be provided):
//    * script
func CreateStepIDDataFromString(compositeVersionStr, defaultStepLibSource string) (StepIDData, error) {
	// first, determine the steplib-source/type
	stepSrc := ""
	stepIDAndVersionOrURIStr := ""
	libsourceStepSplits := strings.Split(compositeVersionStr, "::")
	if len(libsourceStepSplits) == 2 {
		// long/verbose ID mode, ex: step-lib-src::step-id@1.0.0
		stepSrc = libsourceStepSplits[0]
		stepIDAndVersionOrURIStr = libsourceStepSplits[1]
	} else if len(libsourceStepSplits) == 1 {
		// missing steplib-src mode, ex: step-id@1.0.0
		//  in this case if we have a default StepLibSource we'll use that
		stepIDAndVersionOrURIStr = libsourceStepSplits[0]
	} else {
		return StepIDData{}, errors.New("No StepLib found, neither default provided (" + compositeVersionStr + ")")
	}

	if stepSrc == "" {
		if defaultStepLibSource == "" {
			return StepIDData{}, errors.New("No default StepLib source, in this case the composite ID should contain the source, separated with a '::' separator from the step ID (" + compositeVersionStr + ")")
		}
		stepSrc = defaultStepLibSource
	}

	// now determine the ID-or-URI and the version (if provided)
	stepIDOrURI := ""
	stepVersion := ""
	stepidVersionOrURISplits := strings.Split(stepIDAndVersionOrURIStr, "@")
	if len(stepidVersionOrURISplits) >= 2 {
		splitsCnt := len(stepidVersionOrURISplits)
		allButLastSplits := stepidVersionOrURISplits[:splitsCnt-1]
		// the ID or URI is all components except the last @version component
		//  which will be the version itself
		// for example in case it's a git direct URI like:
		//  git@github.com:bitrise-io/steps-timestamp.git@develop
		// which contains 2 at (@) signs only the last should be the version,
		//  the first one is part of the URI
		stepIDOrURI = strings.Join(allButLastSplits, "@")
		// version is simply the last component
		stepVersion = stepidVersionOrURISplits[splitsCnt-1]
	} else if len(stepidVersionOrURISplits) == 1 {
		stepIDOrURI = stepidVersionOrURISplits[0]
	} else {
		return StepIDData{}, errors.New("Step ID and version should be separated with a '@' separator (" + stepIDAndVersionOrURIStr + ")")
	}

	if stepIDOrURI == "" {
		return StepIDData{}, errors.New("No ID found at all (" + compositeVersionStr + ")")
	}

	return StepIDData{
		SteplibSource: stepSrc,
		IDorURI:       stepIDOrURI,
		Version:       stepVersion,
	}, nil
}

// IsBuildFailed ...
func (buildRes BuildRunResultsModel) IsBuildFailed() bool {
	return len(buildRes.FailedSteps) > 0
}

// Append ...
func (buildRes *BuildRunResultsModel) Append(res BuildRunResultsModel) {
	for _, success := range res.SuccessSteps {
		buildRes.SuccessSteps = append(buildRes.SuccessSteps, success)
	}
	for _, failed := range res.FailedSteps {
		buildRes.FailedSteps = append(buildRes.FailedSteps, failed)
	}
	for _, notImportant := range res.FailedNotImportantSteps {
		buildRes.FailedNotImportantSteps = append(buildRes.FailedNotImportantSteps, notImportant)
	}
	for _, skipped := range res.SkippedSteps {
		buildRes.SkippedSteps = append(buildRes.SkippedSteps, skipped)
	}
}
<commit_msg>stepman migration<commit_after>package models

import (
	"errors"
	"fmt"
	"strings"

	envmanModels "github.com/bitrise-io/envman/models"
	stepmanModels "github.com/bitrise-io/stepman/models"
)

// Normalize ...
func (config *BitriseDataModel) Normalize() error {
	for _, workflow := range config.Workflows {
		if err := workflow.Normalize(); err != nil {
			return err
		}
	}
	for _, env := range config.App.Environments {
		if err := env.Normalize(); err != nil {
			return err
		}
	}
	return nil
}

func containsWorkflowName(title string, workflowStack []string) bool {
	for _, t := range workflowStack {
		if t == title {
			return true
		}
	}
	return false
}

func removeWorkflowName(title string, workflowStack []string) []string {
	newStack := []string{}
	for _, t := range workflowStack {
		if t != title {
			newStack = append(newStack, t)
		}
	}
	return newStack
}

func checkWorkflowReferenceCycle(workflowID string, workflow WorkflowModel, bitriseConfig BitriseDataModel, workflowStack []string) error {
	if containsWorkflowName(workflowID, workflowStack) {
		stackStr := ""
		for _, aWorkflowID := range workflowStack {
			stackStr += aWorkflowID + " -> "
		}
		stackStr += workflowID
		return fmt.Errorf("Workflow reference cycle found: %s", stackStr)
	}
	workflowStack = append(workflowStack, workflowID)

	for _, beforeWorkflowName := range workflow.BeforeRun {
		beforeWorkflow, exist := bitriseConfig.Workflows[beforeWorkflowName]
		if !exist {
			return errors.New("Workflow not exist wit name " + beforeWorkflowName)
		}

		err := checkWorkflowReferenceCycle(beforeWorkflowName, beforeWorkflow, bitriseConfig, workflowStack)
		if err != nil {
			return err
		}
	}

	for _, afterWorkflowName := range workflow.AfterRun {
		afterWorkflow, exist := bitriseConfig.Workflows[afterWorkflowName]
		if !exist {
			return errors.New("Workflow not exist wit name " + afterWorkflowName)
		}

		err := checkWorkflowReferenceCycle(afterWorkflowName, afterWorkflow, bitriseConfig, workflowStack)
		if err != nil {
			return err
		}
	}

	workflowStack = removeWorkflowName(workflowID, workflowStack)

	return nil
}

// Validate ...
func (config *BitriseDataModel) Validate() error {
	for ID, workflow := range config.Workflows {
		if err := workflow.Validate(ID); err != nil {
			return err
		}
		if err := checkWorkflowReferenceCycle(ID, workflow, *config, []string{}); err != nil {
			return err
		}
	}
	return nil
}

// FillMissingDefaults ...
func (config *BitriseDataModel) FillMissingDefaults() error {
	for title, workflow := range config.Workflows {
		if err := workflow.FillMissingDefaults(title); err != nil {
			return err
		}
	}
	for _, env := range config.App.Environments {
		if err := env.FillMissingDefaults(); err != nil {
			return err
		}
	}
	return nil
}

// Normalize ...
func (workflow *WorkflowModel) Normalize() error {
	for _, env := range workflow.Environments {
		if err := env.Normalize(); err != nil {
			return err
		}
	}
	return nil
}

// FillMissingDefaults ...
func (workflow *WorkflowModel) FillMissingDefaults(title string) error {
	for _, env := range workflow.Environments {
		if err := env.FillMissingDefaults(); err != nil {
			return err
		}
	}
	if workflow.Title == "" {
		workflow.Title = title
	}
	return nil
}

// Validate ...
func (workflow *WorkflowModel) Validate(title string) error {
	// Validate envs
	for _, env := range workflow.Environments {
		if err := env.Validate(); err != nil {
			return err
		}
	}
	return nil
}

// MergeEnvironmentWith ...
func MergeEnvironmentWith(env *envmanModels.EnvironmentItemModel, otherEnv envmanModels.EnvironmentItemModel) error {
	// merge key-value
	key, _, err := env.GetKeyValuePair()
	if err != nil {
		return err
	}

	otherKey, otherValue, err := otherEnv.GetKeyValuePair()
	if err != nil {
		return err
	}

	if otherKey != key {
		return errors.New("Env keys are diferent")
	}

	(*env)[key] = otherValue

	//merge options
	options, err := env.GetOptions()
	if err != nil {
		return err
	}

	otherOptions, err := otherEnv.GetOptions()
	if err != nil {
		return err
	}
	if otherOptions.Title != nil {
		if options.Title == nil {
			options.Title = new(string)
		}
		*options.Title = *otherOptions.Title
	}
	if otherOptions.Description != nil {
		if options.Description == nil {
			options.Description = new(string)
		}
		*options.Description = *otherOptions.Description
	}
	if len(otherOptions.ValueOptions) > 0 {
		options.ValueOptions = otherOptions.ValueOptions
	}
	if otherOptions.IsRequired != nil {
		if options.IsRequired == nil {
			options.IsRequired = new(bool)
		}
		*options.IsRequired = *otherOptions.IsRequired
	}
	if otherOptions.IsExpand != nil {
		if options.IsExpand == nil {
			options.IsExpand = new(bool)
		}
		*options.IsExpand = *otherOptions.IsExpand
	}
	if otherOptions.IsDontChangeValue != nil {
		if options.IsDontChangeValue == nil {
			options.IsDontChangeValue = new(bool)
		}
		*options.IsDontChangeValue = *otherOptions.IsDontChangeValue
	}
	(*env)[envmanModels.OptionsKey] = options
	return nil
}

// MergeStepWith ...
func MergeStepWith(step, otherStep stepmanModels.StepModel) (stepmanModels.StepModel, error) {
	if err := step.FillMissingDefaults(); err != nil {
		return stepmanModels.StepModel{}, err
	}

	if otherStep.Title != nil {
		if step.Title == nil {
			step.Title = new(string)
		}
		*step.Title = *otherStep.Title
	}
	if otherStep.Description != nil {
		if step.Description == nil {
			step.Description = new(string)
		}
		*step.Description = *otherStep.Description
	}
	if otherStep.Summary != nil {
		if step.Summary == nil {
			step.Summary = new(string)
		}
		*step.Summary = *otherStep.Summary
	}
	if otherStep.Website != nil {
		if step.Website == nil {
			step.Website = new(string)
		}
		*step.Website = *otherStep.Website
	}
	if otherStep.SourceCodeURL != nil {
		if step.SourceCodeURL == nil {
			step.SourceCodeURL = new(string)
		}
		*step.SourceCodeURL = *otherStep.SourceCodeURL
	}
	if otherStep.SupportURL != nil {
		if step.SupportURL == nil {
			step.SupportURL = new(string)
		}
		*step.SupportURL = *otherStep.SupportURL
	}
	if otherStep.Source.Git != "" {
		step.Source.Git = otherStep.Source.Git
	}
	if otherStep.Source.Commit != "" {
		step.Source.Commit = otherStep.Source.Commit
	}
	if len(otherStep.Dependencies) > 0 {
		step.Dependencies = otherStep.Dependencies
	}
	if len(otherStep.HostOsTags) > 0 {
		step.HostOsTags = otherStep.HostOsTags
	}
	if len(otherStep.ProjectTypeTags) > 0 {
		step.ProjectTypeTags = otherStep.ProjectTypeTags
	}
	if len(otherStep.TypeTags) > 0 {
		step.TypeTags = otherStep.TypeTags
	}
	if otherStep.IsRequiresAdminUser != nil {
		if step.IsRequiresAdminUser == nil {
			step.IsRequiresAdminUser = new(bool)
		}
		*step.IsRequiresAdminUser = *otherStep.IsRequiresAdminUser
	}
	if otherStep.IsAlwaysRun != nil {
		if step.IsAlwaysRun == nil {
			step.IsAlwaysRun = new(bool)
		}
		*step.IsAlwaysRun = *otherStep.IsAlwaysRun
	}
	if otherStep.IsSkippable != nil {
		if step.IsSkippable == nil {
			step.IsSkippable = new(bool)
		}
		*step.IsSkippable = *otherStep.IsSkippable
	}
	if otherStep.RunIf != nil {
		if step.RunIf == nil {
			step.RunIf = new(string)
		}
		*step.RunIf = *otherStep.RunIf
	}

	for _, input := range step.Inputs {
		key, _, err := input.GetKeyValuePair()
		if err != nil {
			return stepmanModels.StepModel{}, err
		}
		otherInput, found := getInputByKey(otherStep, key)
		if found {
			err := MergeEnvironmentWith(&input, otherInput)
			if err != nil {
				return stepmanModels.StepModel{}, err
			}
		}
	}

	for _, output := range step.Outputs {
		key, _, err := output.GetKeyValuePair()
		if err != nil {
			return stepmanModels.StepModel{}, err
		}
		otherOutput, found := getOutputByKey(otherStep, key)
		if found {
			err := MergeEnvironmentWith(&output, otherOutput)
			if err != nil {
				return stepmanModels.StepModel{}, err
			}
		}
	}

	return step, nil
}

func getInputByKey(step stepmanModels.StepModel, key string) (envmanModels.EnvironmentItemModel, bool) {
	for _, input := range step.Inputs {
		k, _, err := input.GetKeyValuePair()
		if err != nil {
			return envmanModels.EnvironmentItemModel{}, false
		}

		if k == key {
			return input, true
		}
	}
	return envmanModels.EnvironmentItemModel{}, false
}

func getOutputByKey(step stepmanModels.StepModel, key string) (envmanModels.EnvironmentItemModel, bool) {
	for _, output := range step.Outputs {
		k, _, err := output.GetKeyValuePair()
		if err != nil {
			return envmanModels.EnvironmentItemModel{}, false
		}

		if k == key {
			return output, true
		}
	}
	return envmanModels.EnvironmentItemModel{}, false
}

// GetStepIDStepDataPair ...
func GetStepIDStepDataPair(stepListItem StepListItemModel) (string, stepmanModels.StepModel, error) {
	if len(stepListItem) > 1 {
		return "", stepmanModels.StepModel{}, errors.New("StepListItem contains more than 1 key-value pair!")
	}
	for key, value := range stepListItem {
		return key, value, nil
	}
	return "", stepmanModels.StepModel{}, errors.New("StepListItem does not contain a key-value pair!")
}

// CreateStepIDDataFromString ...
// compositeVersionStr examples:
//  * local path:
//    * path::~/path/to/step/dir
//  * direct git url and branch or tag:
//    * git::https://github.com/bitrise-io/steps-timestamp.git@master
//  * full ID with steplib, stepid and version:
//    * https://github.com/bitrise-io/bitrise-steplib::script@2.0.0
//  * only stepid and version (requires a default steplib source to be provided):
//    * script@2.0.0
//  * only stepid, latest version will be used (requires a default steplib source to be provided):
//    * script
func CreateStepIDDataFromString(compositeVersionStr, defaultStepLibSource string) (StepIDData, error) {
	// first, determine the steplib-source/type
	stepSrc := ""
	stepIDAndVersionOrURIStr := ""
	libsourceStepSplits := strings.Split(compositeVersionStr, "::")
	if len(libsourceStepSplits) == 2 {
		// long/verbose ID mode, ex: step-lib-src::step-id@1.0.0
		stepSrc = libsourceStepSplits[0]
		stepIDAndVersionOrURIStr = libsourceStepSplits[1]
	} else if len(libsourceStepSplits) == 1 {
		// missing steplib-src mode, ex: step-id@1.0.0
		//  in this case if we have a default StepLibSource we'll use that
		stepIDAndVersionOrURIStr = libsourceStepSplits[0]
	} else {
		return StepIDData{}, errors.New("No StepLib found, neither default provided (" + compositeVersionStr + ")")
	}

	if stepSrc == "" {
		if defaultStepLibSource == "" {
			return StepIDData{}, errors.New("No default StepLib source, in this case the composite ID should contain the source, separated with a '::' separator from the step ID (" + compositeVersionStr + ")")
		}
		stepSrc = defaultStepLibSource
	}

	// now determine the ID-or-URI and the version (if provided)
	stepIDOrURI := ""
	stepVersion := ""
	stepidVersionOrURISplits := strings.Split(stepIDAndVersionOrURIStr, "@")
	if len(stepidVersionOrURISplits) >= 2 {
		splitsCnt := len(stepidVersionOrURISplits)
		allButLastSplits := stepidVersionOrURISplits[:splitsCnt-1]
		// the ID or URI is all components except the last @version component
		//  which will be the version itself
		// for example in case it's a git direct URI like:
		//  git@github.com:bitrise-io/steps-timestamp.git@develop
		// which contains 2 at (@) signs only the last should be the version,
		//  the first one is part of the URI
		stepIDOrURI = strings.Join(allButLastSplits, "@")
		// version is simply the last component
		stepVersion = stepidVersionOrURISplits[splitsCnt-1]
	} else if len(stepidVersionOrURISplits) == 1 {
		stepIDOrURI = stepidVersionOrURISplits[0]
	} else {
		return StepIDData{}, errors.New("Step ID and version should be separated with a '@' separator (" + stepIDAndVersionOrURIStr + ")")
	}

	if stepIDOrURI == "" {
		return StepIDData{}, errors.New("No ID found at all (" + compositeVersionStr + ")")
	}

	return StepIDData{
		SteplibSource: stepSrc,
		IDorURI:       stepIDOrURI,
		Version:       stepVersion,
	}, nil
}

// IsBuildFailed ...
func (buildRes BuildRunResultsModel) IsBuildFailed() bool {
	return len(buildRes.FailedSteps) > 0
}

// Append ...
func (buildRes *BuildRunResultsModel) Append(res BuildRunResultsModel) {
	for _, success := range res.SuccessSteps {
		buildRes.SuccessSteps = append(buildRes.SuccessSteps, success)
	}
	for _, failed := range res.FailedSteps {
		buildRes.FailedSteps = append(buildRes.FailedSteps, failed)
	}
	for _, notImportant := range res.FailedNotImportantSteps {
		buildRes.FailedNotImportantSteps = append(buildRes.FailedNotImportantSteps, notImportant)
	}
	for _, skipped := range res.SkippedSteps {
		buildRes.SkippedSteps = append(buildRes.SkippedSteps, skipped)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
)

func RunBenchmark(db Db, csvFilename string) error {
	fp, err := os.Open(csvFilename)
	if err != nil {
		return err
	}
	defer fp.Close()

	bufReader := bufio.NewReader(fp)
	csvReader := csv.NewReader(bufReader)

	header, err := csvReader.Read()
	if err == io.EOF {
		return fmt.Errorf("Missing csv header")
	} else if err != nil {
		return fmt.Errorf("Error reading csv header")
	}

	// TODO ensure we have at least one value?

	colMap := make(map[int]string, len(header))
	for colIdx, colName := range header {
		colMap[colIdx] = colName
	}

	recordIndexIds := make([]int64, 0)

	log.Println("Indexing ...")

	for {
		row, err := csvReader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("Error reading csv contents")
		}
		record := make(map[string]float32, len(row))
		for fieldIdx, fieldValue := range row {
			recordKey, ok := colMap[fieldIdx]
			if !ok {
				// if we don't have header mappings, skip
				break
			}
			val64, err := strconv.ParseFloat(fieldValue, 32)
			if err != nil {
				continue
			}
			val32 := float32(val64)
			record[recordKey] = val32
		}
		if len(record) > 0 {
			id := db.Index(record)
			recordIndexIds = append(recordIndexIds, id)
		}
	}

	log.Println("Indexing ... done")

	nResults := 10
	record := map[string]float32{
		"age":                    21,
		"depart_for_work":        0,
		"fertility":              2,
		"weekly_work_hours":      35,
		"last_week_work_hours":   0,
		"wages":                  197111,
		"self_employed_income":   0,
		"poverty_percentage":     501,
		"carpool_riders":         0,
		"sex":                    1,
		"traveltime_to_work":     0,
		"military_service_years": 0,
	}

	log.Println("Querying ...")
	results := db.Query(nResults, record)

	fmt.Printf("Found %d results\n", len(results))

	log.Println("Querying ... done")

	// the db is indexed now ... need to measure the query times now
	// using some standard queries?

	return nil
}
<commit_msg>During benchmarking, batch index<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
)

func RunBenchmark(db Db, csvFilename string) error {
	fp, err := os.Open(csvFilename)
	if err != nil {
		return err
	}
	defer fp.Close()

	bufReader := bufio.NewReader(fp)
	csvReader := csv.NewReader(bufReader)

	header, err := csvReader.Read()
	if err == io.EOF {
		return fmt.Errorf("Missing csv header")
	} else if err != nil {
		return fmt.Errorf("Error reading csv header")
	}

	// TODO ensure we have at least one value?

	colMap := make(map[int]string, len(header))
	for colIdx, colName := range header {
		colMap[colIdx] = colName
	}

	// recordIndexIds := make([]int64, 0)

	log.Println("Indexing ...")

	recordGroupSize := 100000
	recordGroup := make([]map[string]float32, recordGroupSize)
	curGroupSize := 0

	for {
		row, err := csvReader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("Error reading csv contents")
		}
		record := make(map[string]float32, len(row))
		for fieldIdx, fieldValue := range row {
			recordKey, ok := colMap[fieldIdx]
			if !ok {
				// if we don't have header mappings, skip
				break
			}
			val64, err := strconv.ParseFloat(fieldValue, 32)
			if err != nil {
				continue
			}
			val32 := float32(val64)
			record[recordKey] = val32
		}
		if len(record) > 0 {
			// indexing one at a time
			// id := db.Index(record)
			// recordIndexIds = append(recordIndexIds, id)

			recordGroup[curGroupSize] = record
			curGroupSize++
			if curGroupSize == recordGroupSize {
				db.BulkIndex(recordGroup)
				curGroupSize = 0
			}
		}
	}
	if curGroupSize > 0 {
		finalRecords := make([]map[string]float32, curGroupSize)
		copy(finalRecords, recordGroup)
		db.BulkIndex(finalRecords)
	}

	log.Println("Indexing ... done")

	nResults := 10
	record := map[string]float32{
		"age":                    21,
		"depart_for_work":        0,
		"fertility":              2,
		"weekly_work_hours":      35,
		"last_week_work_hours":   0,
		"wages":                  197111,
		"self_employed_income":   0,
		"poverty_percentage":     501,
		"carpool_riders":         0,
		"sex":                    1,
		"traveltime_to_work":     0,
		"military_service_years": 0,
	}

	log.Println("Querying ...")
	results := db.Query(nResults, record)

	fmt.Printf("Found %d results\n", len(results))

	log.Println("Querying ... done")

	// the db is indexed now ... need to measure the query times now
	// using some standard queries?

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsproxy_test

import (
	"testing"

	"github.com/jacobsa/gcsfuse/gcsproxy"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

func TestOgletest(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking object proxy
////////////////////////////////////////////////////////////////////////

// A wrapper around ObjectProxy that calls CheckInvariants whenever invariants
// should hold. For catching logic errors early in the test.
type checkingObjectProxy struct {
	wrapped *gcsproxy.ObjectProxy
}

func (op *checkingObjectProxy) NoteLatest(o *storage.Object) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.NoteLatest(o)
}

func (op *checkingObjectProxy) Size() (uint64, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Size()
}

func (op *checkingObjectProxy) ReadAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.ReadAt(b, o)
}

func (op *checkingObjectProxy) WriteAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.WriteAt(b, o)
}

func (op *checkingObjectProxy) Truncate(n uint64) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Truncate(n)
}

func (op *checkingObjectProxy) Sync(ctx context.Context) (*storage.Object, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Sync(ctx)
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ObjectProxyTest struct {
	objectName string
	bucket     mock_gcs.MockBucket
	op         checkingObjectProxy
}

var _ SetUpInterface = &ObjectProxyTest{}

func init() { RegisterTestSuite(&ObjectProxyTest{}) }

func (t *ObjectProxyTest) SetUp(ti *TestInfo) {
	t.objectName = "some/object"
	t.bucket = mock_gcs.NewMock(ti.MockController, "bucket")
	t.checkingObjectProxy.wrapped = gcsproxy.NewObjectProxy(
		t.bucket,
		t.objectName)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *ObjectProxyTest) DoesFoo() {
	AssertTrue(false, "TODO")
}
<commit_msg>Fixed some build errors.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsproxy_test

import (
	"testing"

	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

func TestOgletest(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking object proxy
////////////////////////////////////////////////////////////////////////

// A wrapper around ObjectProxy that calls CheckInvariants whenever invariants
// should hold. For catching logic errors early in the test.
type checkingObjectProxy struct {
	wrapped *gcsproxy.ObjectProxy
}

func (op *checkingObjectProxy) NoteLatest(o *storage.Object) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.NoteLatest(o)
}

func (op *checkingObjectProxy) Size() (uint64, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Size()
}

func (op *checkingObjectProxy) ReadAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.ReadAt(b, o)
}

func (op *checkingObjectProxy) WriteAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.WriteAt(b, o)
}

func (op *checkingObjectProxy) Truncate(n uint64) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Truncate(n)
}

func (op *checkingObjectProxy) Sync(ctx context.Context) (*storage.Object, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Sync(ctx)
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ObjectProxyTest struct {
	objectName string
	bucket     mock_gcs.MockBucket
	op         checkingObjectProxy
}

var _ SetUpInterface = &ObjectProxyTest{}

func init() { RegisterTestSuite(&ObjectProxyTest{}) }

func (t *ObjectProxyTest) SetUp(ti *TestInfo) {
	t.objectName = "some/object"
	t.bucket = mock_gcs.NewMockBucket(ti.MockController, "bucket")

	var err error
	t.op.wrapped, err = gcsproxy.NewObjectProxy(
		t.bucket,
		t.objectName)

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

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *ObjectProxyTest) DoesFoo() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package reload

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"sync"
	"time"

	"github.com/caddyserver/caddy"
	"github.com/caddyserver/caddy/caddyfile"
)

// reload periodically checks if the Corefile has changed, and reloads if so
const (
	unused    = 0
	maybeUsed = 1
	used      = 2
)

type reload struct {
	dur  time.Duration
	u    int
	mtx  sync.RWMutex
	quit chan bool
}

func (r *reload) setUsage(u int) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.u = u
}

func (r *reload) usage() int {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.u
}

func (r *reload) setInterval(i time.Duration) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.dur = i
}

func (r *reload) interval() time.Duration {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.dur
}

func parse(corefile caddy.Input) ([]byte, error) {
	serverBlocks, err := caddyfile.Parse(corefile.Path(), bytes.NewReader(corefile.Body()), nil)
	if err != nil {
		return nil, err
	}
	return json.Marshal(serverBlocks)
}

func hook(event caddy.EventName, info interface{}) error {
	if event != caddy.InstanceStartupEvent {
		return nil
	}

	// if reload is removed from the Corefile, then the hook
	// is still registered but setup is never called again
	// so we need a flag to tell us not to reload
	if r.usage() == unused {
		return nil
	}

	// this should be an instance. ok to panic if not
	instance := info.(*caddy.Instance)
	parsedCorefile, err := parse(instance.Caddyfile())
	if err != nil {
		return err
	}

	md5sum := md5.Sum(parsedCorefile)
	log.Infof("Running configuration MD5 = %x\n", md5sum)

	go func() {
		tick := time.NewTicker(r.interval())

		for {
			select {
			case <-tick.C:
				corefile, err := caddy.LoadCaddyfile(instance.Caddyfile().ServerType())
				if err != nil {
					continue
				}
				parsedCorefile, err := parse(corefile)
				if err != nil {
					log.Warningf("Corefile parse failed: %s", err)
					continue
				}
				s := md5.Sum(parsedCorefile)
				if s != md5sum {
					// Let not try to restart with the same file, even though it is wrong.
					md5sum = s
					// now lets consider that plugin will not be reload, unless appear in next config file
					// change status iof usage will be reset in setup if the plugin appears in config file
					r.setUsage(maybeUsed)
					_, err := instance.Restart(corefile)
					if err != nil {
						log.Errorf("Corefile changed but reload failed: %s", err)
						FailedCount.Add(1)
						continue
					}
					// we are done, if the plugin was not set used, then it is not.
					if r.usage() == maybeUsed {
						r.setUsage(unused)
					}
					return
				}
			case <-r.quit:
				return
			}
		}
	}()

	return nil
}
<commit_msg>fix typo (#3180)<commit_after>package reload

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"sync"
	"time"

	"github.com/caddyserver/caddy"
	"github.com/caddyserver/caddy/caddyfile"
)

// reload periodically checks if the Corefile has changed, and reloads if so
const (
	unused    = 0
	maybeUsed = 1
	used      = 2
)

type reload struct {
	dur  time.Duration
	u    int
	mtx  sync.RWMutex
	quit chan bool
}

func (r *reload) setUsage(u int) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.u = u
}

func (r *reload) usage() int {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.u
}

func (r *reload) setInterval(i time.Duration) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.dur = i
}

func (r *reload) interval() time.Duration {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.dur
}

func parse(corefile caddy.Input) ([]byte, error) {
	serverBlocks, err := caddyfile.Parse(corefile.Path(), bytes.NewReader(corefile.Body()), nil)
	if err != nil {
		return nil, err
	}
	return json.Marshal(serverBlocks)
}

func hook(event caddy.EventName, info interface{}) error {
	if event != caddy.InstanceStartupEvent {
		return nil
	}

	// if reload is removed from the Corefile, then the hook
	// is still registered but setup is never called again
	// so we need a flag to tell us not to reload
	if r.usage() == unused {
		return nil
	}

	// this should be an instance. ok to panic if not
	instance := info.(*caddy.Instance)
	parsedCorefile, err := parse(instance.Caddyfile())
	if err != nil {
		return err
	}

	md5sum := md5.Sum(parsedCorefile)
	log.Infof("Running configuration MD5 = %x\n", md5sum)

	go func() {
		tick := time.NewTicker(r.interval())

		for {
			select {
			case <-tick.C:
				corefile, err := caddy.LoadCaddyfile(instance.Caddyfile().ServerType())
				if err != nil {
					continue
				}
				parsedCorefile, err := parse(corefile)
				if err != nil {
					log.Warningf("Corefile parse failed: %s", err)
					continue
				}
				s := md5.Sum(parsedCorefile)
				if s != md5sum {
					// Let not try to restart with the same file, even though it is wrong.
					md5sum = s
					// now lets consider that plugin will not be reload, unless appear in next config file
					// change status of usage will be reset in setup if the plugin appears in config file
					r.setUsage(maybeUsed)
					_, err := instance.Restart(corefile)
					if err != nil {
						log.Errorf("Corefile changed but reload failed: %s", err)
						FailedCount.Add(1)
						continue
					}
					// we are done, if the plugin was not set used, then it is not.
					if r.usage() == maybeUsed {
						r.setUsage(unused)
					}
					return
				}
			case <-r.quit:
				return
			}
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2015 Stefan Luecke
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors: Stefan Luecke <glaxx@glaxx.net>
 */

package backend

import (
	//	"database/sql"
	log "github.com/Sirupsen/logrus"
	"github.com/emicklei/go-restful"
	//"html/template"
	//	"github.com/fatih/structs"
	dmp "github.com/sergi/go-diff/diffmatchpatch"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"strconv"
	//"strings"
	"time"
)

type Item struct {
	ID          bson.ObjectId `bson:"_id,omitempty" json:"-"`
	EID         uint64        `json:"Id"`
	Name        string        `bson:",omitempty"`
	Description string        `bson:",omitempty"`
	Contains    []uint64      `bson:",omitempty"`
	Owner       string        `bson:",omitempty"`
	Maintainer  string        `bson:",omitempty"`
	Usage       string        `bson:",omitempty"`
	Discard     string        `bson:",omitempty"`
}

func (i *Item) NewItemHistory(it *Item, user string) *ItemHistory {
	res := new(ItemHistory)
	res.Item = make(map[string]interface{})
	res.Item["eid"] = i.EID
	res.User = user

	if it == nil {
		res.Item["deleted"] = true
		return res
	}
	//res.Item["_id"] = i.ID
	if i.Name != it.Name {
		res.Item["name"] = it.Name
	}
	if i.Description != it.Description {
		d := dmp.New()
		d.DiffTimeout = 200 * time.Millisecond
		res.Item["description"] = d.DiffMain(i.Description, it.Description, true)
	}
	condiff := uint64Diff(i.Contains, it.Contains)
	if len(condiff) > 0 {
		res.Item["contains"] = condiff
	}
	if i.Owner != it.Owner {
		res.Item["owner"] = it.Owner
	}
	if i.Maintainer != it.Maintainer {
		res.Item["maintainer"] = it.Maintainer
	}
	if i.Usage != it.Usage {
		res.Item["usage"] = it.Usage
	}
	if i.Discard != it.Discard {
		res.Item["discard"] = it.Discard
	}
	return res
}

func uint64Diff(u1, u2 []uint64) map[string]dmp.Operation {
	// mgo.bson does only support strings as keys
	res := make(map[string]dmp.Operation)
	for i := 0; i != len(u1); i++ {
		if !uint64Contains(u2, u1[i]) {
			res[strconv.FormatUint(u1[i], 10)] = dmp.DiffDelete
		}
	}
	for i := 0; i != len(u2); i++ {
		if !uint64Contains(u1, u2[i]) {
			res[strconv.FormatUint(u2[i], 10)] = dmp.DiffInsert
		}
	}
	return res
}

func uint64Contains(sl []uint64, u uint64) bool {
	for i := 0; i != len(sl); i++ {
		if sl[i] == u {
			return true
		}
	}
	return false
}

type ItemHistory struct {
	ID        bson.ObjectId `bson:"_id,omitempty" json:"-"`
	Timestamp time.Time     `bson:"-" json:",omitempty"`
	User      string
	Item      map[string]interface{}
}

func NewItemService() *restful.WebService {
	service := new(restful.WebService)
	service.
		Path("/item").
		Doc("Item related services").
		ApiVersion("0.1").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	service.Route(service.GET("/{id}").
		Param(restful.PathParameter("id", "Item ID")).
		Doc("Returns a single item identified by its ID").
		//Returns(http.StatusOK, "Item request successful", Item{}).
		To(GetItemById).
		Writes(Item{}).
		Do(returnsInternalServerError, returnsNotFound, returnsBadRequest))

	service.Route(service.GET("/{id}/log").
		Param(restful.PathParameter("id", "Item ID")).
		Doc("Returns the items changelog").
		To(GetItemLog).
		Writes(ItemHistory{}).
		Do(returnsInternalServerError, returnsNotFound, returnsBadRequest))

	service.Route(service.GET("").
		Doc("List all available items (this may be replaced by a paginated version)").
		To(ListItem).
		Writes([]Item{}).
		Do(returnsInternalServerError))

	service.Route(service.PUT("").
		Filter(basicAuthFilter).
		Doc("Update a item.").
		To(UpdateItem).
		Reads(Item{}).
		Do(returnsInternalServerError, returnsNotFound, returnsUpdateSuccessful, returnsBadRequest))

	service.Route(service.POST("").
		Filter(basicAuthFilter).
		Doc("Insert a item into the databse").
		To(CreateItem).
		Reads(Item{}).
		Returns(http.StatusOK, "Insert successful", "/item/{id}").
		Do(returnsInternalServerError, returnsBadRequest))

	service.Route(service.DELETE("/{id}").
		Filter(basicAuthFilter).
		Param(restful.PathParameter("id", "Item ID")).
		Doc("Delete a item").
		To(DeleteItem).
		Do(returnsInternalServerError, returnsNotFound, returnsDeleteSuccessful, returnsBadRequest))
	return service
}

func GetItemById(request *restful.Request, response *restful.Response) {
	sid := request.PathParameter("id")
	log.WithFields(log.Fields{"Requested ID": sid, "Path": request.SelectedRoutePath()}).Debug("Got Request")
	id, err := strconv.ParseUint(sid, 10, 64)
	if err != nil {
		response.WriteErrorString(http.StatusBadRequest, ERROR_INVALID_ID)
		log.WithFields(log.Fields{"Error Msg": err}).
			Info(ERROR_INVALID_ID)
		return
	}
	itm, err := getItemById(id)
	if err != nil {
		response.WriteErrorString(http.StatusBadRequest, ERROR_INVALID_ID)
		log.WithFields(log.Fields{"Error Msg": err}).
			Info(ERROR_INVALID_ID)
		return
	}
	response.WriteEntity(itm)
}

func getItemById(id uint64) (Item, error) {
	res := Item{}
	err := iCol.Find(bson.M{"eid": id}).One(&res)
	return res, err
}

func GetItemLog(request *restful.Request, response *restful.Response) {
	sid := request.PathParameter("id")
	id, err := strconv.ParseUint(sid, 10, 64)
	if err != nil {
		response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
		log.Info(err)
		return
	}
	history, err := getItemLog(id)
	if err != nil {
		response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
		log.Info(err)
		return
	}
	response.WriteEntity(history)
}

func getItemLog(id uint64) ([]ItemHistory, error) {
	res := make([]ItemHistory, 0)
	err := ihCol.Find(bson.M{"item.eid": id}).All(&res)
	for i := 0; i != len(res); i++ {
		res[i].Timestamp = res[i].ID.Time()
	}
	return res, err
}

func CreateItem(request *restful.Request, response *restful.Response) {
	itm := new(Item)
	err := request.ReadEntity(itm)
	if err != nil {
		response.WriteErrorString(http.StatusBadRequest, ERROR_INVALID_INPUT)
		log.WithFields(log.Fields{"Error Msg": err}).Info(ERROR_INVALID_INPUT)
		return
	}

	id, err := createItem(itm)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.WithFields(log.Fields{"Error Msg": err}).Warn(ERROR_INTERNAL)
		return
	}
	response.WriteEntity("/item/" + strconv.FormatUint(id, 10))
}

func createItem(itm *Item) (uint64, error) {
	itm.EID = idgen.GenerateID()
	log.WithFields(log.Fields{"ID": itm.EID}).Debug("Generated ID")
	err := iCol.Insert(itm)
	return itm.EID, err
}

func ListItem(request *restful.Request, response *restful.Response) {
	itm, err := listItem()
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.WithFields(log.Fields{"Error Msg": err}).Warn(ERROR_INTERNAL)
		return
	}
	response.WriteEntity(itm)
}

func listItem() ([]Item, error) {
	itm := make([]Item, 0)
	err := iCol.Find(nil).All(&itm)
	return itm, err
}

func UpdateItem(request *restful.Request, response *restful.Response) {
	itm := new(Item)
	err := request.ReadEntity(itm)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.WithFields(log.Fields{"Error Msg": err}).Warn(ERROR_INTERNAL)
		return
	}
	i, err := getItemById(itm.EID)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.Warn(err)
		return
	}
	h := i.NewItemHistory(itm, request.Attribute("User").(string))

	err = updateItem(itm, h)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.Warn(err)
		return
	}
	response.WriteEntity(true)
	return
}

func updateItem(itm *Item, ih *ItemHistory) error {
	err := ihCol.Insert(ih)
	if err != nil {
		return err
	}
	return iCol.Update(bson.M{"eid": itm.EID}, itm)
}

func checkItemExistance(itm *Item) bool {
	temp := Item{}
	err := iCol.Find(bson.M{"eid": itm.EID}).One(&temp)
	if err != nil {
		if err.Error() == "not found" {
			return false
		} else {
			log.WithFields(log.Fields{"Err": err}).
				Debug("TODO: check more error codes here")
			return true
		}
	}
	return true
}

func DeleteItem(request *restful.Request, response *restful.Response) {
	sid := request.PathParameter("id")
	id, err := strconv.ParseUint(sid, 10, 64)
	if err != nil {
		//TODO
		log.WithFields(log.Fields{"Error Msg": err}).Info(ERROR_INVALID_ID)
		response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
		return
	}

	i, err := getItemById(id)
	if err != nil {
		if err.Error() == "not found" {
			log.WithFields(log.Fields{"ID": id}).Info(ERROR_INVALID_ID)
			response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
			return
		}
		log.WithFields(log.Fields{"Error Msg": err}).Info(ERROR_INTERNAL)
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		return
	}

	h := i.NewItemHistory(nil, request.Attribute("User").(string))
	err = deleteItem(&i, h)
	if err != nil {
		log.Warn(err)
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		return
	}
	response.WriteEntity(true)
}

func deleteItem(itm *Item, ih *ItemHistory) error {
	err := ihCol.Insert(ih)
	if err != nil {
		return err
	}
	return iCol.Remove(bson.M{"eid": itm.EID})
}
<commit_msg>Fixed typo<commit_after>/*
 *    Copyright (C) 2015 Stefan Luecke
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors: Stefan Luecke <glaxx@glaxx.net>
 */

package backend

import (
	//	"database/sql"
	log "github.com/Sirupsen/logrus"
	"github.com/emicklei/go-restful"
	//"html/template"
	//	"github.com/fatih/structs"
	dmp "github.com/sergi/go-diff/diffmatchpatch"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"strconv"
	//"strings"
	"time"
)

type Item struct {
	ID          bson.ObjectId `bson:"_id,omitempty" json:"-"`
	EID         uint64        `json:"Id"`
	Name        string        `bson:",omitempty"`
	Description string        `bson:",omitempty"`
	Contains    []uint64      `bson:",omitempty"`
	Owner       string        `bson:",omitempty"`
	Maintainer  string        `bson:",omitempty"`
	Usage       string        `bson:",omitempty"`
	Discard     string        `bson:",omitempty"`
}

func (i *Item) NewItemHistory(it *Item, user string) *ItemHistory {
	res := new(ItemHistory)
	res.Item = make(map[string]interface{})
	res.Item["eid"] = i.EID
	res.User = user

	if it == nil {
		res.Item["deleted"] = true
		return res
	}
	//res.Item["_id"] = i.ID
	if i.Name != it.Name {
		res.Item["name"] = it.Name
	}
	if i.Description != it.Description {
		d := dmp.New()
		d.DiffTimeout = 200 * time.Millisecond
		res.Item["description"] = d.DiffMain(i.Description, it.Description, true)
	}
	condiff := uint64Diff(i.Contains, it.Contains)
	if len(condiff) > 0 {
		res.Item["contains"] = condiff
	}
	if i.Owner != it.Owner {
		res.Item["owner"] = it.Owner
	}
	if i.Maintainer != it.Maintainer {
		res.Item["maintainer"] = it.Maintainer
	}
	if i.Usage != it.Usage {
		res.Item["usage"] = it.Usage
	}
	if i.Discard != it.Discard {
		res.Item["discard"] = it.Discard
	}
	return res
}

func uint64Diff(u1, u2 []uint64) map[string]dmp.Operation {
	// mgo.bson does only support strings as keys
	res := make(map[string]dmp.Operation)
	for i := 0; i != len(u1); i++ {
		if !uint64Contains(u2, u1[i]) {
			res[strconv.FormatUint(u1[i], 10)] = dmp.DiffDelete
		}
	}
	for i := 0; i != len(u2); i++ {
		if !uint64Contains(u1, u2[i]) {
			res[strconv.FormatUint(u2[i], 10)] = dmp.DiffInsert
		}
	}
	return res
}

func uint64Contains(sl []uint64, u uint64) bool {
	for i := 0; i != len(sl); i++ {
		if sl[i] == u {
			return true
		}
	}
	return false
}

type ItemHistory struct {
	ID        bson.ObjectId `bson:"_id,omitempty" json:"-"`
	Timestamp time.Time     `bson:"-" json:",omitempty"`
	User      string
	Item      map[string]interface{}
}

func NewItemService() *restful.WebService {
	service := new(restful.WebService)
	service.
		Path("/item").
		Doc("Item related services").
		ApiVersion("0.1").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	service.Route(service.GET("/{id}").
		Param(restful.PathParameter("id", "Item ID")).
		Doc("Returns a single item identified by its ID").
		//Returns(http.StatusOK, "Item request successful", Item{}).
		To(GetItemById).
		Writes(Item{}).
		Do(returnsInternalServerError, returnsNotFound, returnsBadRequest))

	service.Route(service.GET("/{id}/log").
		Param(restful.PathParameter("id", "Item ID")).
		Doc("Returns the items changelog").
		To(GetItemLog).
		Writes(ItemHistory{}).
		Do(returnsInternalServerError, returnsNotFound, returnsBadRequest))

	service.Route(service.GET("").
		Doc("List all available items (this may be replaced by a paginated version)").
		To(ListItem).
		Writes([]Item{}).
		Do(returnsInternalServerError))

	service.Route(service.PUT("").
		Filter(basicAuthFilter).
		Doc("Update a item.").
		To(UpdateItem).
		Reads(Item{}).
		Do(returnsInternalServerError, returnsNotFound, returnsUpdateSuccessful, returnsBadRequest))

	service.Route(service.POST("").
		Filter(basicAuthFilter).
		Doc("Insert a item into the database").
		To(CreateItem).
		Reads(Item{}).
		Returns(http.StatusOK, "Insert successful", "/item/{id}").
		Do(returnsInternalServerError, returnsBadRequest))

	service.Route(service.DELETE("/{id}").
		Filter(basicAuthFilter).
		Param(restful.PathParameter("id", "Item ID")).
		Doc("Delete a item").
		To(DeleteItem).
		Do(returnsInternalServerError, returnsNotFound, returnsDeleteSuccessful, returnsBadRequest))
	return service
}

func GetItemById(request *restful.Request, response *restful.Response) {
	sid := request.PathParameter("id")
	log.WithFields(log.Fields{"Requested ID": sid, "Path": request.SelectedRoutePath()}).Debug("Got Request")
	id, err := strconv.ParseUint(sid, 10, 64)
	if err != nil {
		response.WriteErrorString(http.StatusBadRequest, ERROR_INVALID_ID)
		log.WithFields(log.Fields{"Error Msg": err}).
			Info(ERROR_INVALID_ID)
		return
	}
	itm, err := getItemById(id)
	if err != nil {
		response.WriteErrorString(http.StatusBadRequest, ERROR_INVALID_ID)
		log.WithFields(log.Fields{"Error Msg": err}).
			Info(ERROR_INVALID_ID)
		return
	}
	response.WriteEntity(itm)
}

func getItemById(id uint64) (Item, error) {
	res := Item{}
	err := iCol.Find(bson.M{"eid": id}).One(&res)
	return res, err
}

func GetItemLog(request *restful.Request, response *restful.Response) {
	sid := request.PathParameter("id")
	id, err := strconv.ParseUint(sid, 10, 64)
	if err != nil {
		response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
		log.Info(err)
		return
	}
	history, err := getItemLog(id)
	if err != nil {
		response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
		log.Info(err)
		return
	}
	response.WriteEntity(history)
}

func getItemLog(id uint64) ([]ItemHistory, error) {
	res := make([]ItemHistory, 0)
	err := ihCol.Find(bson.M{"item.eid": id}).All(&res)
	for i := 0; i != len(res); i++ {
		res[i].Timestamp = res[i].ID.Time()
	}
	return res, err
}

func CreateItem(request *restful.Request, response *restful.Response) {
	itm := new(Item)
	err := request.ReadEntity(itm)
	if err != nil {
		response.WriteErrorString(http.StatusBadRequest, ERROR_INVALID_INPUT)
		log.WithFields(log.Fields{"Error Msg": err}).Info(ERROR_INVALID_INPUT)
		return
	}

	id, err := createItem(itm)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.WithFields(log.Fields{"Error Msg": err}).Warn(ERROR_INTERNAL)
		return
	}
	response.WriteEntity("/item/" + strconv.FormatUint(id, 10))
}

func createItem(itm *Item) (uint64, error) {
	itm.EID = idgen.GenerateID()
	log.WithFields(log.Fields{"ID": itm.EID}).Debug("Generated ID")
	err := iCol.Insert(itm)
	return itm.EID, err
}

func ListItem(request *restful.Request, response *restful.Response) {
	itm, err := listItem()
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.WithFields(log.Fields{"Error Msg": err}).Warn(ERROR_INTERNAL)
		return
	}
	response.WriteEntity(itm)
}

func listItem() ([]Item, error) {
	itm := make([]Item, 0)
	err := iCol.Find(nil).All(&itm)
	return itm, err
}

func UpdateItem(request *restful.Request, response *restful.Response) {
	itm := new(Item)
	err := request.ReadEntity(itm)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.WithFields(log.Fields{"Error Msg": err}).Warn(ERROR_INTERNAL)
		return
	}
	i, err := getItemById(itm.EID)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.Warn(err)
		return
	}
	h := i.NewItemHistory(itm, request.Attribute("User").(string))

	err = updateItem(itm, h)
	if err != nil {
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		log.Warn(err)
		return
	}
	response.WriteEntity(true)
	return
}

func updateItem(itm *Item, ih *ItemHistory) error {
	err := ihCol.Insert(ih)
	if err != nil {
		return err
	}
	return iCol.Update(bson.M{"eid": itm.EID}, itm)
}

func checkItemExistance(itm *Item) bool {
	temp := Item{}
	err := iCol.Find(bson.M{"eid": itm.EID}).One(&temp)
	if err != nil {
		if err.Error() == "not found" {
			return false
		} else {
			log.WithFields(log.Fields{"Err": err}).
				Debug("TODO: check more error codes here")
			return true
		}
	}
	return true
}

func DeleteItem(request *restful.Request, response *restful.Response) {
	sid := request.PathParameter("id")
	id, err := strconv.ParseUint(sid, 10, 64)
	if err != nil {
		//TODO
		log.WithFields(log.Fields{"Error Msg": err}).Info(ERROR_INVALID_ID)
		response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
		return
	}

	i, err := getItemById(id)
	if err != nil {
		if err.Error() == "not found" {
			log.WithFields(log.Fields{"ID": id}).Info(ERROR_INVALID_ID)
			response.WriteErrorString(http.StatusNotFound, ERROR_INVALID_ID)
			return
		}
		log.WithFields(log.Fields{"Error Msg": err}).Info(ERROR_INTERNAL)
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		return
	}

	h := i.NewItemHistory(nil, request.Attribute("User").(string))
	err = deleteItem(&i, h)
	if err != nil {
		log.Warn(err)
		response.WriteErrorString(http.StatusInternalServerError, ERROR_INTERNAL)
		return
	}
	response.WriteEntity(true)
}

func deleteItem(itm *Item, ih *ItemHistory) error {
	err := ihCol.Insert(ih)
	if err != nil {
		return err
	}
	return iCol.Remove(bson.M{"eid": itm.EID})
}
<|endoftext|>


<commit_before>// This example demonstrates how to authenticate with Spotify.
// In order to run this example yourself, you'll need to:
//
//  1. Register an application at: https://developer.spotify.com/my-applications/
//       - Use "http://localhost:8080/callback" as the redirect URI
//  2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
//  3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
package main

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"strconv"

	"github.com/coolbry95/partydj/backend/pool"
	"github.com/pressly/chi"
	"github.com/zmb3/spotify"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "https://linode.shellcode.in/callback"

var (
	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserLibraryModify, spotify.ScopePlaylistModifyPrivate,
		spotify.ScopePlaylistModifyPublic)
	ch    = make(chan spotify.Client)
	state = "stateless"
)

type DI struct {
	client spotify.Client
	pool   pool.Pool
}

var PoolShortIDToLongID map[int]string
var UserIDToPoolID map[string]string

func main() {
	// Initialize the pool (single for demonstration)
	var d *DI
	d = new(DI)

	// setup the router and paths
	r := chi.NewRouter()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL.String())
	})
	r.Get("/callback", completeAuth)
	r.Get("/getpool", d.getPool)
	r.Post("/createpool", d.createPool)
	r.Post("/add_song/:poolID/:songID", d.addSong)
	r.Post("/upvote/:poolID/:songID", d.upVote)
	r.Post("/downvote/:poolID/:songID", d.downVote)
	r.Post("/join_pool", d.joinPool)

	r.FileServer("/files", http.Dir("/home/coolbry95/gosrc/src/github.com/coolbry95/partydj/website"))

	// Authenticate the users spotify account
	url := auth.AuthURL(state)
	fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)

	go http.ListenAndServe(":6060", r)

	// wait for auth to complete
	d.client = <-ch
	d.pool.PlaylistID = spotify.ID("0nXlYUH7zBAzubO9Yub4rR") // change to spartahack playlist

	// Initialize the short id to long id map
	PoolShortIDToLongID = make(map[int]string)
	// hard code the one join digit for this sample pool to 121
	PoolShortIDToLongID[121] = "0nXlYUH7zBAzubO9Yub4rR"

	// initialize the user map
	UserIDToPoolID = make(map[string]string)

	userID, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	d.pool.UserID = userID.ID

	block := make(chan struct{})
	<-block
}

func (d *DI) joinPool(w http.ResponseWriter, r *http.Request) {
	userIDNumber := r.PostFormValue("userId")
	poolIDNumberStr := r.PostFormValue("poolShortId")

	// Both must be present
	if len(userIDNumber) == 0 || len(poolIDNumberStr) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("One of the parameters were empty: ", "userId:",
			userIDNumber, "poolShortId:", poolIDNumberStr)
		return
	}

	poolIDNumber, convErr := strconv.Atoi(poolIDNumberStr)

	// Make sure the poolIDNumber is a valid integer, otherwise
	// respond with Status partial content
	if convErr != nil {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("Conversion error: ", convErr)
		return
	}

	// Check if pool exists, if not respond with status not found
	if _, ok := PoolShortIDToLongID[poolIDNumber]; ok != true {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	UserIDToPoolID[userIDNumber] = poolIDNumberStr
	// Let the user know the request was accepted
	w.WriteHeader(http.StatusOK)
}

func (d *DI) addSong(w http.ResponseWriter, r *http.Request) {
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	s := &pool.Song{ID: spotify.ID(songID)}
	heap.Push(&d.pool, s)
}

func (d *DI) upVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")
	userID := r.PostFormValue("userId")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.UpVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.WriteHeader(http.StatusOK)
}

func (d *DI) downVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")
	userID := r.PostFormValue("userId")
	//poolID := chi.URLParam(r, "poolID")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.DownVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func (d *DI) createPool(w http.ResponseWriter, r *http.Request) {
}

func (d *DI) getPool(w http.ResponseWriter, r *http.Request) {
	userid, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	// TODO: instead of using existing playlist we require a new playlist to be created
	// playlist, err := d.client.CreatePlaylistForUser(userid.ID, playlistName, true)
	playlist, err := d.client.GetPlaylistTracks(userid.ID, "0nXlYUH7zBAzubO9Yub4rR")
	//spartahackPlaylistName := "Sparthack Sample Playlist!"
	//newPlaylist, err := d.client.CreatePlaylistForUser(userid.ID, spartahackPlaylistName, true)
	if err != nil {
		log.Println(err)
	}

	d.pool.SongHeap = make([]*pool.Song, 0, 100)
	for i, track := range playlist.Tracks {
		d.pool.SongHeap = append(d.pool.SongHeap, pool.TrackToSong(&track.Track, i))
	}

	//TODO: only call this function only after the the current song finishes
	songTime := heap.Pop(&d.pool).(*pool.Song).Duration + 15
	timerr := time.NewTimer(time.Duration(songTime) * time.Second)
	go func() {
		<-timerr.C
		d.pool.AddNextSong(&d.client)
	}()

	json.NewEncoder(w).Encode(d.pool)
	return
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}

	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}

	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	ch <- client
	http.Redirect(w, r, "/getpool", 301)
}
<commit_msg>pushed song back on heap<commit_after>// This example demonstrates how to authenticate with Spotify.
// In order to run this example yourself, you'll need to:
//
//  1. Register an application at: https://developer.spotify.com/my-applications/
//       - Use "http://localhost:8080/callback" as the redirect URI
//  2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
//  3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
package main

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"strconv"

	"github.com/coolbry95/partydj/backend/pool"
	"github.com/pressly/chi"
	"github.com/zmb3/spotify"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "https://linode.shellcode.in/callback"

var (
	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserLibraryModify, spotify.ScopePlaylistModifyPrivate,
		spotify.ScopePlaylistModifyPublic)
	ch    = make(chan spotify.Client)
	state = "stateless"
)

type DI struct {
	client spotify.Client
	pool   pool.Pool
}

var PoolShortIDToLongID map[int]string
var UserIDToPoolID map[string]string

func main() {
	// Initialize the pool (single for demonstration)
	var d *DI
	d = new(DI)

	// setup the router and paths
	r := chi.NewRouter()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL.String())
	})
	r.Get("/callback", completeAuth)
	r.Get("/getpool", d.getPool)
	r.Post("/createpool", d.createPool)
	r.Post("/add_song/:poolID/:songID", d.addSong)
	r.Post("/upvote/:poolID/:songID", d.upVote)
	r.Post("/downvote/:poolID/:songID", d.downVote)
	r.Post("/join_pool", d.joinPool)

	r.FileServer("/files", http.Dir("/home/coolbry95/gosrc/src/github.com/coolbry95/partydj/website"))

	// Authenticate the users spotify account
	url := auth.AuthURL(state)
	fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)

	go http.ListenAndServe(":6060", r)

	// wait for auth to complete
	d.client = <-ch
	d.pool.PlaylistID = spotify.ID("0nXlYUH7zBAzubO9Yub4rR") // change to spartahack playlist

	// Initialize the short id to long id map
	PoolShortIDToLongID = make(map[int]string)
	// hard code the one join digit for this sample pool to 121
	PoolShortIDToLongID[121] = "0nXlYUH7zBAzubO9Yub4rR"

	// initialize the user map
	UserIDToPoolID = make(map[string]string)

	userID, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	d.pool.UserID = userID.ID

	block := make(chan struct{})
	<-block
}

func (d *DI) joinPool(w http.ResponseWriter, r *http.Request) {
	userIDNumber := r.PostFormValue("userId")
	poolIDNumberStr := r.PostFormValue("poolShortId")

	// Both must be present
	if len(userIDNumber) == 0 || len(poolIDNumberStr) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("One of the parameters were empty: ", "userId:",
			userIDNumber, "poolShortId:", poolIDNumberStr)
		return
	}

	poolIDNumber, convErr := strconv.Atoi(poolIDNumberStr)

	// Make sure the poolIDNumber is a valid integer, otherwise
	// respond with Status partial content
	if convErr != nil {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("Conversion error: ", convErr)
		return
	}

	// Check if pool exists, if not respond with status not found
	if _, ok := PoolShortIDToLongID[poolIDNumber]; ok != true {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	UserIDToPoolID[userIDNumber] = poolIDNumberStr
	// Let the user know the request was accepted
	w.WriteHeader(http.StatusOK)
}

func (d *DI) addSong(w http.ResponseWriter, r *http.Request) {
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	s := &pool.Song{ID: spotify.ID(songID)}
	heap.Push(&d.pool, s)
}

func (d *DI) upVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")
	userID := r.PostFormValue("userId")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.UpVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.WriteHeader(http.StatusOK)
}

func (d *DI) downVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")
	userID := r.PostFormValue("userId")
	//poolID := chi.URLParam(r, "poolID")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.DownVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func (d *DI) createPool(w http.ResponseWriter, r *http.Request) {
}

func (d *DI) getPool(w http.ResponseWriter, r *http.Request) {
	userid, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	// TODO: instead of using existing playlist we require a new playlist to be created
	// playlist, err := d.client.CreatePlaylistForUser(userid.ID, playlistName, true)
	playlist, err := d.client.GetPlaylistTracks(userid.ID, "0nXlYUH7zBAzubO9Yub4rR")
	//spartahackPlaylistName := "Sparthack Sample Playlist!"
	//newPlaylist, err := d.client.CreatePlaylistForUser(userid.ID, spartahackPlaylistName, true)
	if err != nil {
		log.Println(err)
	}

	d.pool.SongHeap = make([]*pool.Song, 0, 100)
	for i, track := range playlist.Tracks {
		d.pool.SongHeap = append(d.pool.SongHeap, pool.TrackToSong(&track.Track, i))
	}

	//TODO: only call this function only after the the current song finishes
	song := heap.Pop(&d.pool).(*pool.Song)
	heap.Push(&d.pool, song)
	songTime := song.Duration + 15

	timerr := time.NewTimer(time.Duration(songTime) * time.Second)
	go func() {
		<-timerr.C
		d.pool.AddNextSong(&d.client)
	}()

	json.NewEncoder(w).Encode(d.pool)
	return
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}

	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}

	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	ch <- client
	http.Redirect(w, r, "/getpool", 301)
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/grafana/metrictank/api/middleware"
	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/api/response"
	"github.com/grafana/metrictank/consolidation"
	"github.com/grafana/metrictank/mdata"
	"github.com/grafana/metrictank/util"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/raintank/worldping-api/pkg/log"
	schema "gopkg.in/raintank/schema.v1"
)

type orgID string

type status string

const (
	statusSuccess status = "success"
	statusError   status = "error"
)

type errorType string

const (
	errorNone        errorType = ""
	errorTimeout     errorType = "timeout"
	errorCanceled    errorType = "canceled"
	errorExec        errorType = "execution"
	errorBadData     errorType = "bad_data"
	errorInternal    errorType = "internal"
	errorUnavailable errorType = "unavailable"
)

type prometheusQueryResult struct {
	Status    status      `json:"status"`
	Data      interface{} `json:"data,omitempty"`
	ErrorType errorType   `json:"errorType,omitempty"`
	Error     string      `json:"error,omitempty"`
}

type prometheusQueryData struct {
	ResultType promql.ValueType `json:"resultType"`
	Result     promql.Value     `json:"result"`
}

type querier struct {
	Server
	from  uint32
	to    uint32
	OrgID int
	ctx   context.Context
}

func (s *Server) labelValues(ctx *middleware.Context) {
	name := ctx.Params(":name")

	if !model.LabelNameRE.MatchString(name) {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("invalid label name: %v", name)))
		return
	}

	q, err := s.Querier(context.WithValue(context.Background(), orgID("org-id"), ctx.OrgId), 0, 0)

	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("unable to create queryable: %v", err)))
		return
	}
	defer q.Close()

	vals, err := q.LabelValues(name)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("error: %v", err)))
		return
	}

	response.Write(ctx, response.NewJson(200, prometheusQueryResult{Status: "success", Data: vals}, ""))
	return
}

func (s *Server) queryRange(ctx *middleware.Context, request models.PrometheusQueryRange) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status: statusError,
			Error:  fmt.Sprintf("could not parse start time: %v", err),
		}, ""))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status: statusError,
			Error:  fmt.Sprintf("could not parse end time: %v", err),
		}, ""))
		return
	}

	step, err := parseDuration(request.Step)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status: statusError,
			Error:  fmt.Sprintf("could not parse step duration: %v", err),
		}, ""))
		return
	}

	if step <= 0 {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status: statusError,
			Error:  fmt.Sprintf("step value is less than or equal to zero: %v", step),
		}, ""))
		return
	}

	qry, err := s.PromQueryEngine.NewRangeQuery(request.Query, start, end, step)

	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status: statusError,
			Error:  fmt.Sprintf("query failed: %v", err),
		}, ""))
		return
	}

	newCtx := context.WithValue(ctx.Req.Context(), orgID("org-id"), ctx.OrgId)
	res := qry.Exec(newCtx)

	if res.Err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status: statusError,
			Error:  fmt.Sprintf("query failed: %v", res.Err),
		}, ""))
		return
	}

	response.Write(ctx, response.NewJson(200,
		prometheusQueryResult{
			Data: prometheusQueryData{
				ResultType: res.Value.Type(),
				Result:     res.Value,
			},
			Status: statusSuccess,
		},
		"",
	))
}

func (s *Server) querySeries(ctx *middleware.Context, request models.PrometheusSeriesQuery) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse start time: %v", err)))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse end time: %v", err)))
		return
	}

	_, err = s.Querier(ctx.Req.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusInternalServerError, fmt.Sprintf("query failed: %v", err)))
		return
	}

	response.Write(ctx, response.NewError(200, "test"))
}

func (s *Server) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	return &querier{
		*s,
		uint32(mint / 1000), //Convert from NS to S
		uint32(maxt / 1000), //TODO abstract this out into a seperate function that is more accurate
		ctx.Value(orgID("org-id")).(int),
		ctx,
	}, nil
}

func parseTime(s string) (time.Time, error) {
	if t, err := strconv.ParseFloat(s, 64); err == nil {
		s, ns := math.Modf(t)
		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
	}
	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
		return t, nil
	}
	return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
}

func parseDuration(s string) (time.Duration, error) {
	if d, err := strconv.ParseFloat(s, 64); err == nil {
		ts := d * float64(time.Second)
		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
			return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
		}
		return time.Duration(ts), nil
	}
	if d, err := model.ParseDuration(s); err == nil {
		return time.Duration(d), nil
	}
	return 0, fmt.Errorf("cannot parse %q to a valid duration", s)
}

// Select returns a set of series that matches the given label matchers.
func (q *querier) Select(matchers ...*labels.Matcher) (storage.SeriesSet, error) {
	minFrom := uint32(math.MaxUint32)
	var maxTo uint32
	var target string
	var reqs []models.Req

	expressions := []string{}
	for _, matcher := range matchers {
		if matcher.Name == model.MetricNameLabel {
			matcher.Name = "name"
		}
		expressions = append(expressions, fmt.Sprintf("%s%s%s", matcher.Name, matcher.Type, matcher.Value))
	}

	series, err := q.clusterFindByTag(q.ctx, q.OrgID, expressions, 0)
	if err != nil {
		return nil, err
	}

	minFrom = util.Min(minFrom, q.from)
	maxTo = util.Max(maxTo, q.to)
	for _, s := range series {
		for _, metric := range s.Series {
			for _, archive := range metric.Defs {
				consReq := consolidation.None
				fn := mdata.Aggregations.Get(archive.AggId).AggregationMethod[0]
				cons := consolidation.Consolidator(fn)

				newReq := models.NewReq(archive.Id, archive.NameWithTags(), target, q.from, q.to, math.MaxUint32, uint32(archive.Interval), cons, consReq, s.Node, archive.SchemaId, archive.AggId)
				fmt.Printf("From: %v\n To:%v\n", newReq.From, newReq.To)
				reqs = append(reqs, newReq)
			}
		}
	}

	select {
	case <-q.ctx.Done():
		//request canceled
		return nil, fmt.Errorf("request canceled")
	default:
	}

	reqRenderSeriesCount.Value(len(reqs))
	if len(reqs) == 0 {
		return nil, fmt.Errorf("no series found")
	}

	// note: if 1 series has a movingAvg that requires a long time range extension, it may push other reqs into another archive. can be optimized later
	reqs, _, _, err = alignRequests(uint32(time.Now().Unix()), minFrom, maxTo, reqs)
	if err != nil {
		log.Error(3, "HTTP Render alignReq error: %s", err)
		return nil, err
	}
	fmt.Println(reqs)

	out, err := q.getTargets(q.ctx, reqs)
	if err != nil {
		log.Error(3, "HTTP Render %s", err.Error())
		return nil, err
	}

	seriesSet, err := SeriesToSeriesSet(out)

	return seriesSet, err
}

// LabelValues returns all potential values for a label name.
func (q *querier) LabelValues(name string) ([]string, error) {
	result, err := q.MetricIndex.FindTagValues(q.OrgID, name, "", []string{}, 0, 100000)
	if err != nil {
		return nil, err
	}
	if result == nil {
		result = []string{}
	}
	return result, nil
}

// Close releases the resources of the Querier.
func (q *querier) Close() error {
	return nil
}

func SeriesToSeriesSet(out []models.Series) (*models.PrometheusSeriesSet, error) {
	series := []storage.Series{}
	for _, metric := range out {
		fmt.Println(metric)
		series = append(series, models.NewPrometheusSeries(buildTagSet(metric.Target), dataPointsToPrometheusSamplePairs(metric.Datapoints)))
	}
	return models.NewPrometheusSeriesSet(series), nil
}

func dataPointsToPrometheusSamplePairs(data []schema.Point) []model.SamplePair {
	samples := []model.SamplePair{}
	for _, point := range data {
		if math.IsNaN(point.Val) {
			continue
		}
		samples = append(samples, model.SamplePair{
			Timestamp: model.Time(int64(point.Ts) * 1000),
			Value:     model.SampleValue(point.Val),
		})
	}
	return samples
}

// Turns graphite target name into prometheus graphite name
// TODO models.Series should provide a map of tags but the one returned from getTargets doesn't
func buildTagSet(name string) map[string]string {
	labelMap := map[string]string{}
	tags := strings.Split(name, ";")
	labelMap["__name__"] = tags[0]
	for _, lbl := range tags[1:] {
		kv := strings.Split(lbl, "=")
		labelMap[kv[0]] = kv[1]
	}
	return labelMap
}
<commit_msg>fix misspell and add error type to messages<commit_after>package api

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/grafana/metrictank/api/middleware"
	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/api/response"
	"github.com/grafana/metrictank/consolidation"
	"github.com/grafana/metrictank/mdata"
	"github.com/grafana/metrictank/util"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/raintank/worldping-api/pkg/log"
	schema "gopkg.in/raintank/schema.v1"
)

type orgID string

type status string

const (
	statusSuccess status = "success"
	statusError   status = "error"
)

type errorType string

const (
	errorNone        errorType = ""
	errorTimeout     errorType = "timeout"
	errorCanceled    errorType = "canceled"
	errorExec        errorType = "execution"
	errorBadData     errorType = "bad_data"
	errorInternal    errorType = "internal"
	errorUnavailable errorType = "unavailable"
)

type prometheusQueryResult struct {
	Status    status      `json:"status"`
	Data      interface{} `json:"data,omitempty"`
	ErrorType errorType   `json:"errorType,omitempty"`
	Error     string      `json:"error,omitempty"`
}

type prometheusQueryData struct {
	ResultType promql.ValueType `json:"resultType"`
	Result     promql.Value     `json:"result"`
}

type querier struct {
	Server
	from  uint32
	to    uint32
	OrgID int
	ctx   context.Context
}

func (s *Server) labelValues(ctx *middleware.Context) {
	name := ctx.Params(":name")

	if !model.LabelNameRE.MatchString(name) {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("invalid label name: %v", name)))
		return
	}

	q, err := s.Querier(context.WithValue(context.Background(), orgID("org-id"), ctx.OrgId), 0, 0)

	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("unable to create queryable: %v", err)))
		return
	}
	defer q.Close()

	vals, err := q.LabelValues(name)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("error: %v", err)))
		return
	}

	response.Write(ctx, response.NewJson(200, prometheusQueryResult{Status: "success", Data: vals}, ""))
	return
}

func (s *Server) queryRange(ctx *middleware.Context, request models.PrometheusQueryRange) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse start time: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse end time: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	step, err := parseDuration(request.Step)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse step duration: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	if step <= 0 {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("step value is less than or equal to zero: %v", step),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	qry, err := s.PromQueryEngine.NewRangeQuery(request.Query, start, end, step)

	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("query failed: %v", err),
			ErrorType: errorExec,
		}, ""))
		return
	}

	newCtx := context.WithValue(ctx.Req.Context(), orgID("org-id"), ctx.OrgId)
	res := qry.Exec(newCtx)

	if res.Err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("query failed: %v", res.Err),
			ErrorType: errorExec,
		}, ""))
		return
	}

	response.Write(ctx, response.NewJson(200,
		prometheusQueryResult{
			Data: prometheusQueryData{
				ResultType: res.Value.Type(),
				Result:     res.Value,
			},
			Status: statusSuccess,
		},
		"",
	))
}

func (s *Server) querySeries(ctx *middleware.Context, request models.PrometheusSeriesQuery) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse start time: %v", err)))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse end time: %v", err)))
		return
	}

	_, err = s.Querier(ctx.Req.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusInternalServerError, fmt.Sprintf("query failed: %v", err)))
		return
	}

	response.Write(ctx, response.NewError(200, "test"))
}

func (s *Server) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	return &querier{
		*s,
		uint32(mint / 1000), //Convert from NS to S
		uint32(maxt / 1000), //TODO abstract this out into a separate function that is more accurate
		ctx.Value(orgID("org-id")).(int),
		ctx,
	}, nil
}

func parseTime(s string) (time.Time, error) {
	if t, err := strconv.ParseFloat(s, 64); err == nil {
		s, ns := math.Modf(t)
		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
	}
	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
		return t, nil
	}
	return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
}

func parseDuration(s string) (time.Duration, error) {
	if d, err := strconv.ParseFloat(s, 64); err == nil {
		ts := d * float64(time.Second)
		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
			return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
		}
		return time.Duration(ts), nil
	}
	if d, err := model.ParseDuration(s); err == nil {
		return time.Duration(d), nil
	}
	return 0, fmt.Errorf("cannot parse %q to a valid duration", s)
}

// Select returns a set of series that matches the given label matchers.
func (q *querier) Select(matchers ...*labels.Matcher) (storage.SeriesSet, error) {
	minFrom := uint32(math.MaxUint32)
	var maxTo uint32
	var target string
	var reqs []models.Req

	expressions := []string{}
	for _, matcher := range matchers {
		if matcher.Name == model.MetricNameLabel {
			matcher.Name = "name"
		}
		expressions = append(expressions, fmt.Sprintf("%s%s%s", matcher.Name, matcher.Type, matcher.Value))
	}

	series, err := q.clusterFindByTag(q.ctx, q.OrgID, expressions, 0)
	if err != nil {
		return nil, err
	}

	minFrom = util.Min(minFrom, q.from)
	maxTo = util.Max(maxTo, q.to)
	for _, s := range series {
		for _, metric := range s.Series {
			for _, archive := range metric.Defs {
				consReq := consolidation.None
				fn := mdata.Aggregations.Get(archive.AggId).AggregationMethod[0]
				cons := consolidation.Consolidator(fn)

				newReq := models.NewReq(archive.Id, archive.NameWithTags(), target, q.from, q.to, math.MaxUint32, uint32(archive.Interval), cons, consReq, s.Node, archive.SchemaId, archive.AggId)
				fmt.Printf("From: %v\n To:%v\n", newReq.From, newReq.To)
				reqs = append(reqs, newReq)
			}
		}
	}

	select {
	case <-q.ctx.Done():
		//request canceled
		return nil, fmt.Errorf("request canceled")
	default:
	}

	reqRenderSeriesCount.Value(len(reqs))
	if len(reqs) == 0 {
		return nil, fmt.Errorf("no series found")
	}

	// note: if 1 series has a movingAvg that requires a long time range extension, it may push other reqs into another archive. can be optimized later
	reqs, _, _, err = alignRequests(uint32(time.Now().Unix()), minFrom, maxTo, reqs)
	if err != nil {
		log.Error(3, "HTTP Render alignReq error: %s", err)
		return nil, err
	}
	fmt.Println(reqs)

	out, err := q.getTargets(q.ctx, reqs)
	if err != nil {
		log.Error(3, "HTTP Render %s", err.Error())
		return nil, err
	}

	seriesSet, err := SeriesToSeriesSet(out)

	return seriesSet, err
}

// LabelValues returns all potential values for a label name.
func (q *querier) LabelValues(name string) ([]string, error) {
	result, err := q.MetricIndex.FindTagValues(q.OrgID, name, "", []string{}, 0, 100000)
	if err != nil {
		return nil, err
	}
	if result == nil {
		result = []string{}
	}
	return result, nil
}

// Close releases the resources of the Querier.
func (q *querier) Close() error {
	return nil
}

func SeriesToSeriesSet(out []models.Series) (*models.PrometheusSeriesSet, error) {
	series := []storage.Series{}
	for _, metric := range out {
		fmt.Println(metric)
		series = append(series, models.NewPrometheusSeries(buildTagSet(metric.Target), dataPointsToPrometheusSamplePairs(metric.Datapoints)))
	}
	return models.NewPrometheusSeriesSet(series), nil
}

func dataPointsToPrometheusSamplePairs(data []schema.Point) []model.SamplePair {
	samples := []model.SamplePair{}
	for _, point := range data {
		if math.IsNaN(point.Val) {
			continue
		}
		samples = append(samples, model.SamplePair{
			Timestamp: model.Time(int64(point.Ts) * 1000),
			Value:     model.SampleValue(point.Val),
		})
	}
	return samples
}

// Turns graphite target name into prometheus graphite name
// TODO models.Series should provide a map of tags but the one returned from getTargets doesn't
func buildTagSet(name string) map[string]string {
	labelMap := map[string]string{}
	tags := strings.Split(name, ";")
	labelMap["__name__"] = tags[0]
	for _, lbl := range tags[1:] {
		kv := strings.Split(lbl, "=")
		labelMap[kv[0]] = kv[1]
	}
	return labelMap
}
<|endoftext|>