<commit_before>// Package config contains the model and loader of the goreleaser configuration
// file.
package config

import (
	"io/ioutil"

	yaml "gopkg.in/yaml.v1"
)

// Repo represents any kind of repo (github, gitlab, etc)
type Repo struct {
	Owner string
	Name  string
}

// String of the repo, e.g. owner/name
func (r Repo) String() string {
	return r.Owner + "/" + r.Name
}

// Homebrew contains the brew section
type Homebrew struct {
	Repo         string // deprecated!
	GitHub       Repo
	Folder       string
	Caveats      string
	Plist        string
	Install      string
	Dependencies []string
	Conflicts    []string
}

// Hooks define actions to run before and/or after something
type Hooks struct {
	Pre  string
	Post string
}

// Build contains the build configuration section
type Build struct {
	Goos       []string
	Goarch     []string
	Main       string
	Ldflags    string
	Flags      string
	BinaryName string `yaml:"binary_name"` // deprecated
	Binary     string
	Hooks      Hooks
}

// Archive config used for the archive
type Archive struct {
	Format       string
	NameTemplate string `yaml:"name_template"`
	Replacements map[string]string
	Files        []string
}

// Release config used for the GitHub release
type Release struct {
	Repo   string // deprecated!
	GitHub Repo
}

// FPM config
type FPM struct {
	Formats      []string
	Dependencies []string
	Conflicts    []string
	Options      FPMOptions
}

// FPM config options
type FPMOptions struct {
	Vendor      string
	URL         string
	Maintainer  string
	Description string
	License     string
}

// Project includes all project configuration
type Project struct {
	Release Release
	Brew    Homebrew
	Build   Build
	Archive Archive
	FPM     FPM `yaml:"fpm"`

	// test only property indicating the path to the dist folder
	Dist string `yaml:"-"`
}

// Load config file
func Load(file string) (config Project, err error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return config, err
	}
	err = yaml.Unmarshal(data, &config)
	return
}
<commit_msg>removed more deprecated code<commit_after>// Package config contains the model and loader of the goreleaser configuration
// file.
package config

import (
	"io/ioutil"

	yaml "gopkg.in/yaml.v1"
)

// Repo represents any kind of repo (github, gitlab, etc)
type Repo struct {
	Owner string
	Name  string
}

// String of the repo, e.g. owner/name
func (r Repo) String() string {
	return r.Owner + "/" + r.Name
}

// Homebrew contains the brew section
type Homebrew struct {
	GitHub       Repo
	Folder       string
	Caveats      string
	Plist        string
	Install      string
	Dependencies []string
	Conflicts    []string
}

// Hooks define actions to run before and/or after something
type Hooks struct {
	Pre  string
	Post string
}

// Build contains the build configuration section
type Build struct {
	Goos    []string
	Goarch  []string
	Main    string
	Ldflags string
	Flags   string
	Binary  string
	Hooks   Hooks
}

// Archive config used for the archive
type Archive struct {
	Format       string
	NameTemplate string `yaml:"name_template"`
	Replacements map[string]string
	Files        []string
}

// Release config used for the GitHub release
type Release struct {
	GitHub Repo
}

// FPM config
type FPM struct {
	Formats      []string
	Dependencies []string
	Conflicts    []string
	Options      FPMOptions
}

// FPMOptions config options
type FPMOptions struct {
	Vendor      string
	URL         string
	Maintainer  string
	Description string
	License     string
}

// Project includes all project configuration
type Project struct {
	Release Release
	Brew    Homebrew
	Build   Build
	Archive Archive
	FPM     FPM `yaml:"fpm"`

	// test only property indicating the path to the dist folder
	Dist string `yaml:"-"`
}

// Load config file
func Load(file string) (config Project, err error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return config, err
	}
	err = yaml.Unmarshal(data, &config)
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 Jesse Allen. All rights reserved
// Released under the MIT license found in the LICENSE file.

package rest_test

import (
	"bytes"
	"math/rand"
	"mime"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"sync"
	"testing"
	"testing/quick"

	"github.com/lazyengineering/faststatus"
	"github.com/lazyengineering/faststatus/rest"
)

func TestHandlerOnlyValidPaths(t *testing.T) {
	var s, _ = rest.New()
	invalidPathIsNotFound := func(method, path string) bool {
		w := httptest.NewRecorder()
		r := httptest.NewRequest(method, path, nil)
		s.ServeHTTP(w, r)

		if w.Code != http.StatusNotFound {
			t.Logf("%s %q got %03d, expected 404", method, path, w.Code)
			return false
		}
		return true
	}
	err := quick.Check(invalidPathIsNotFound, &quick.Config{
		Values: func(args []reflect.Value, gen *rand.Rand) {
			args[0] = reflect.ValueOf(possibleMethods[gen.Intn(len(possibleMethods))])
			args[1] = reflect.ValueOf(genInvalidPath(gen.Intn(100)+1, gen))
		},
	})
	if err != nil {
		t.Fatalf("unexpected response to bad client request: %+v", err)
	}
}

func TestHandlerOnlyValidPathsAndMethods(t *testing.T) {
	var s, _ = rest.New()
	isNotAllowed := func(path string) func(string) bool {
		return func(method string) bool {
			w := httptest.NewRecorder()
			r := httptest.NewRequest(method, path, nil)
			s.ServeHTTP(w, r)

			validMethods := validMethodsByPath[path]
			var isValid bool
			for _, validMethod := range validMethods {
				if validMethod == method {
					isValid = true
					break
				}
			}
			if !isValid && w.Code != http.StatusMethodNotAllowed {
				t.Logf("%s %q got %03d, expected 405", method, path, w.Code)
				return false
			}
			if isValid && w.Code == http.StatusMethodNotAllowed {
				t.Logf("%s %q got %03d, expected not 405", method, path, w.Code)
				return false
			}
			return true
		}
	}
	genMethod := func(args []reflect.Value, gen *rand.Rand) {
		args[0] = reflect.ValueOf(possibleMethods[gen.Intn(len(possibleMethods))])
	}
	for path := range validMethodsByPath {
		if err := quick.Check(isNotAllowed(path), &quick.Config{Values: genMethod}); err != nil {
			t.Fatalf("unexpected response to bad client request: %+v", err)
		}
	}
}

var possibleMethods = []string{
	http.MethodGet,
	http.MethodHead,
	http.MethodPut,
	http.MethodPost,
	http.MethodPatch,
	http.MethodDelete,
}
var validMethodsByPath = map[string][]string{
	"/new": []string{http.MethodGet, http.MethodHead},
}

func TestHandlerGetNew(t *testing.T) {
	const defaultContentType = "text/plain"
	var s, _ = rest.New()
	var mu sync.Mutex
	var usedIDs = make(map[faststatus.ID]struct{})
	getsNewResource := func() bool {
		w := httptest.NewRecorder()
		r := httptest.NewRequest(http.MethodGet, "/new", nil)
		s.ServeHTTP(w, r)

		if w.Code != http.StatusOK {
			t.Logf(
				"returned Status Code %03d, expected %03d",
				w.Code,
				http.StatusOK,
			)
			return false
		}

		gotType, _, err := mime.ParseMediaType(w.HeaderMap.Get("Content-Type"))
		if err != nil {
			t.Logf("error parsing content type: %+v", err)
			return false
		}
		if gotType != defaultContentType {
			t.Logf("Content-Type %q, expected %q", gotType, defaultContentType)
			return false
		}

		var (
			gotResource  faststatus.Resource
			zeroResource faststatus.Resource
		)
		err = (&gotResource).UnmarshalText(w.Body.Bytes())
		if err != nil {
			t.Logf("error unmarshaling Resource from text body: %+v", err)
			return false
		}

		if bytes.Equal(gotResource.ID[:], zeroResource.ID[:]) {
			t.Logf("ID should be non-zero")
			return false
		}

		if _, exists := usedIDs[gotResource.ID]; exists {
			t.Logf("ID should be unique")
			return false
		}
		mu.Lock()
		usedIDs[gotResource.ID] = struct{}{}
		mu.Unlock()

		gotResource.ID = zeroResource.ID
		if !gotResource.Equal(zeroResource) {
			t.Logf("with ID set to zero-value got %+v, expected %+v", gotResource, zeroResource)
			return false
		}

		return true
	}
	if err := quick.Check(getsNewResource, nil); err != nil {
		t.Fatalf("GET /new does not get a new Resource: %+v", err)
	}
}

var possibleMethods = []string{
	http.MethodGet,
	http.MethodHead,
	http.MethodPut,
	http.MethodPost,
	http.MethodPatch,
	http.MethodDelete,
}

func validMethodsByPath(path string) ([]string, bool) {
	if path == "/new" {
		return []string{http.MethodGet, http.MethodHead}, true
	}
	return nil, false
}

func genInvalidPath(maxLen int, r *rand.Rand) string {
	for {
		path := genPath(maxLen, r)
		_, ok := validMethodsByPath(path)
		if !ok {
			return path
		}
	}
}

func genPath(maxLen int, r *rand.Rand) string {
	scratch := make([]byte, 1000)
	r.Read(scratch)
	var path = "/" + strings.Map(func(char rune) rune {
		switch {
		case char >= '0' && char <= '9',
			char >= 'A' && char <= 'Z',
			char >= 'a' && char <= 'z',
			char == '!',
			char == '$',
			char >= 0x27 && char <= '/':
			return char
		default:
			return -1
		}
	}, string(scratch))
	return path[:maxLen%len(path)]
}
<commit_msg>Update the wrong method test<commit_after>// Copyright 2017 Jesse Allen. All rights reserved
// Released under the MIT license found in the LICENSE file.

package rest_test

import (
	"bytes"
	"math/rand"
	"mime"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"sync"
	"testing"
	"testing/quick"

	"github.com/lazyengineering/faststatus"
	"github.com/lazyengineering/faststatus/rest"
)

func TestHandlerOnlyValidPaths(t *testing.T) {
	var s, _ = rest.New()
	invalidPathIsNotFound := func(method, path string) bool {
		w := httptest.NewRecorder()
		r := httptest.NewRequest(method, path, nil)
		s.ServeHTTP(w, r)

		if w.Code != http.StatusNotFound {
			t.Logf("%s %q got %03d, expected 404", method, path, w.Code)
			return false
		}
		return true
	}
	err := quick.Check(invalidPathIsNotFound, &quick.Config{
		Values: func(args []reflect.Value, gen *rand.Rand) {
			args[0] = reflect.ValueOf(possibleMethods[gen.Intn(len(possibleMethods))])
			args[1] = reflect.ValueOf(genInvalidPath(gen.Intn(100)+1, gen))
		},
	})
	if err != nil {
		t.Fatalf("unexpected response to bad client request: %+v", err)
	}
}

func TestHandlerOnlyValidPathsAndMethods(t *testing.T) {
	var s, _ = rest.New()
	isNotAllowed := func(method, path string) bool {
		w := httptest.NewRecorder()
		r := httptest.NewRequest(method, path, nil)
		s.ServeHTTP(w, r)

		validMethods, _ := validMethodsByPath(path)
		var isValid bool
		for _, validMethod := range validMethods {
			if validMethod == method {
				isValid = true
				break
			}
		}
		if !isValid && w.Code != http.StatusMethodNotAllowed {
			t.Logf("%s %q got %03d, expected 405", method, path, w.Code)
			return false
		}
		if isValid && w.Code == http.StatusMethodNotAllowed {
			t.Logf("%s %q got %03d, expected not 405", method, path, w.Code)
			return false
		}
		return true
	}
	gen := func(args []reflect.Value, r *rand.Rand) {
		args[0] = reflect.ValueOf(possibleMethods[r.Intn(len(possibleMethods))])
		args[1] = reflect.ValueOf(genValidPath(r))
	}
	if err := quick.Check(isNotAllowed, &quick.Config{Values: gen}); err != nil {
		t.Fatalf("unexpected response to bad client request: %+v", err)
	}
}

var possibleMethods = []string{
	http.MethodGet,
	http.MethodHead,
	http.MethodPut,
	http.MethodPost,
	http.MethodPatch,
	http.MethodDelete,
}
var validMethodsByPath = map[string][]string{
	"/new": []string{http.MethodGet, http.MethodHead},
}

func TestHandlerGetNew(t *testing.T) {
	const defaultContentType = "text/plain"
	var s, _ = rest.New()
	var mu sync.Mutex
	var usedIDs = make(map[faststatus.ID]struct{})
	getsNewResource := func() bool {
		w := httptest.NewRecorder()
		r := httptest.NewRequest(http.MethodGet, "/new", nil)
		s.ServeHTTP(w, r)

		if w.Code != http.StatusOK {
			t.Logf(
				"returned Status Code %03d, expected %03d",
				w.Code,
				http.StatusOK,
			)
			return false
		}

		gotType, _, err := mime.ParseMediaType(w.HeaderMap.Get("Content-Type"))
		if err != nil {
			t.Logf("error parsing content type: %+v", err)
			return false
		}
		if gotType != defaultContentType {
			t.Logf("Content-Type %q, expected %q", gotType, defaultContentType)
			return false
		}

		var (
			gotResource  faststatus.Resource
			zeroResource faststatus.Resource
		)
		err = (&gotResource).UnmarshalText(w.Body.Bytes())
		if err != nil {
			t.Logf("error unmarshaling Resource from text body: %+v", err)
			return false
		}

		if bytes.Equal(gotResource.ID[:], zeroResource.ID[:]) {
			t.Logf("ID should be non-zero")
			return false
		}

		if _, exists := usedIDs[gotResource.ID]; exists {
			t.Logf("ID should be unique")
			return false
		}
		mu.Lock()
		usedIDs[gotResource.ID] = struct{}{}
		mu.Unlock()

		gotResource.ID = zeroResource.ID
		if !gotResource.Equal(zeroResource) {
			t.Logf("with ID set to zero-value got %+v, expected %+v", gotResource, zeroResource)
			return false
		}

		return true
	}
	if err := quick.Check(getsNewResource, nil); err != nil {
		t.Fatalf("GET /new does not get a new Resource: %+v", err)
	}
}

var possibleMethods = []string{
	http.MethodGet,
	http.MethodHead,
	http.MethodPut,
	http.MethodPost,
	http.MethodPatch,
	http.MethodDelete,
}

func validMethodsByPath(path string) ([]string, bool) {
	if path == "/new" {
		return []string{http.MethodGet, http.MethodHead}, true
	}
	return nil, false
}

func genValidPath(r *rand.Rand) string {
	pathFuncs := []func() string{
		func() string { return "/new" },
	}
	return pathFuncs[r.Intn(len(pathFuncs))]()
}

func genInvalidPath(maxLen int, r *rand.Rand) string {
	for {
		path := genPath(maxLen, r)
		_, ok := validMethodsByPath(path)
		if !ok {
			return path
		}
	}
}

func genPath(maxLen int, r *rand.Rand) string {
	scratch := make([]byte, 1000)
	r.Read(scratch)
	var path = "/" + strings.Map(func(char rune) rune {
		switch {
		case char >= '0' && char <= '9',
			char >= 'A' && char <= 'Z',
			char >= 'a' && char <= 'z',
			char == '!',
			char == '$',
			char >= 0x27 && char <= '/':
			return char
		default:
			return -1
		}
	}, string(scratch))
	return path[:maxLen%len(path)]
}
<|endoftext|>


<commit_before>// Copyright 2015 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package task

import (
	"errors"
	"fmt"
	"github.com/skyrings/skyring-common/conf"
	"github.com/skyrings/skyring-common/db"
	"github.com/skyrings/skyring-common/models"
	"github.com/skyrings/skyring-common/tools/logger"
	"github.com/skyrings/skyring-common/tools/uuid"
	"gopkg.in/mgo.v2/bson"
	"sync"
)

var (
	TaskManager Manager
)

type Manager struct {
	tasks map[uuid.UUID]*Task
}

func (manager *Manager) Run(owner string, name string, f func(t *Task), startedFunc func(t *Task), completedFunc func(t *Task), statusFunc func(t *Task, s *models.Status)) (uuid.UUID, error) {
	if id, err := uuid.New(); err == nil {
		task := Task{
			Mutex:            &sync.Mutex{},
			ID:               *id,
			Owner:            owner,
			Name:             name,
			DoneCh:           make(chan bool, 1),
			StatusList:       []models.Status{},
			StopCh:           make(chan bool, 0),
			Func:             f,
			StartedCbkFunc:   startedFunc,
			CompletedCbkFunc: completedFunc,
			StatusCbkFunc:    statusFunc,
		}
		task.Run()
		manager.tasks[*id] = &task
		go func() {
			select {
			case <-task.DoneCh:
				return
			case <-task.StopCh:
				task.UpdateStatus("Force Stop. Task: %v explicitly stopped.", task.ID)
				task.Done(models.TASK_STATUS_TIMED_OUT)
				task.StopCh <- true
				return
			}
		}()
		return *id, nil
	} else {
		return uuid.UUID{}, err
	}
}

func (manager *Manager) IsDone(id uuid.UUID) (b bool, err error) {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": id}).One(&task); err != nil {
		logger.Get().Error("task id %s not found", id)
		err = errors.New(fmt.Sprintf("task id %s not found", id))
	} else {
		b = task.Completed
	}
	return
}

func (manager *Manager) IsStarted(id uuid.UUID) (b bool, err error) {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": id}).One(&task); err != nil {
		logger.Get().Error("task id %s not found", id)
		err = errors.New(fmt.Sprintf("task id %s not found", id))
	} else {
		b = task.Started
	}
	return
}

func (manager *Manager) GetStatus(id uuid.UUID) (status []models.Status, err error) {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": id}).One(&task); err != nil {
		logger.Get().Error("task id %s not found", id)
		err = errors.New(fmt.Sprintf("task id %s not found", id))
	} else {
		status = task.StatusList
	}
	return
}

func (manager *Manager) Remove(id uuid.UUID) {
	delete(manager.tasks, id)
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	_ = coll.Remove(bson.M{"id": id})
}

func (manager *Manager) Stop(id uuid.UUID) (bool, error) {
	if task, ok := manager.tasks[id]; ok {
		task.StopCh <- true
		return true, nil
	} else {
		return false, fmt.Errorf("Failed to stop task: %v", id)
	}
}

func (manager *Manager) List() []uuid.UUID {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var tasks []models.AppTask
	if err := coll.Find(nil).All(&tasks); err != nil {
		return []uuid.UUID{}
	}
	ids := make([]uuid.UUID, 0, len(tasks))
	for _, task := range tasks {
		ids = append(ids, task.Id)
	}
	return ids
}

func NewManager() Manager {
	TaskManager = Manager{make(map[uuid.UUID]*Task)}
	return TaskManager
}

func GetTaskManager() *Manager {
	return &TaskManager
}

func (manager *Manager) RemoveTask(id uuid.UUID) {
	delete(manager.tasks, id)
}
<commit_msg>skyring-common: Changed the task explicit stop message<commit_after>// Copyright 2015 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package task

import (
	"errors"
	"fmt"
	"github.com/skyrings/skyring-common/conf"
	"github.com/skyrings/skyring-common/db"
	"github.com/skyrings/skyring-common/models"
	"github.com/skyrings/skyring-common/tools/logger"
	"github.com/skyrings/skyring-common/tools/uuid"
	"gopkg.in/mgo.v2/bson"
	"sync"
)

var (
	TaskManager Manager
)

type Manager struct {
	tasks map[uuid.UUID]*Task
}

func (manager *Manager) Run(owner string, name string, f func(t *Task), startedFunc func(t *Task), completedFunc func(t *Task), statusFunc func(t *Task, s *models.Status)) (uuid.UUID, error) {
	if id, err := uuid.New(); err == nil {
		task := Task{
			Mutex:            &sync.Mutex{},
			ID:               *id,
			Owner:            owner,
			Name:             name,
			DoneCh:           make(chan bool, 1),
			StatusList:       []models.Status{},
			StopCh:           make(chan bool, 0),
			Func:             f,
			StartedCbkFunc:   startedFunc,
			CompletedCbkFunc: completedFunc,
			StatusCbkFunc:    statusFunc,
		}
		task.Run()
		manager.tasks[*id] = &task
		go func() {
			select {
			case <-task.DoneCh:
				return
			case <-task.StopCh:
				task.UpdateStatus("Force Stop. Task: %v explicitly stopped due to timeout.", task.ID)
				task.Done(models.TASK_STATUS_TIMED_OUT)
				task.StopCh <- true
				return
			}
		}()
		return *id, nil
	} else {
		return uuid.UUID{}, err
	}
}

func (manager *Manager) IsDone(id uuid.UUID) (b bool, err error) {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": id}).One(&task); err != nil {
		logger.Get().Error("task id %s not found", id)
		err = errors.New(fmt.Sprintf("task id %s not found", id))
	} else {
		b = task.Completed
	}
	return
}

func (manager *Manager) IsStarted(id uuid.UUID) (b bool, err error) {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": id}).One(&task); err != nil {
		logger.Get().Error("task id %s not found", id)
		err = errors.New(fmt.Sprintf("task id %s not found", id))
	} else {
		b = task.Started
	}
	return
}

func (manager *Manager) GetStatus(id uuid.UUID) (status []models.Status, err error) {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": id}).One(&task); err != nil {
		logger.Get().Error("task id %s not found", id)
		err = errors.New(fmt.Sprintf("task id %s not found", id))
	} else {
		status = task.StatusList
	}
	return
}

func (manager *Manager) Remove(id uuid.UUID) {
	delete(manager.tasks, id)
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	_ = coll.Remove(bson.M{"id": id})
}

func (manager *Manager) Stop(id uuid.UUID) (bool, error) {
	if task, ok := manager.tasks[id]; ok {
		task.StopCh <- true
		return true, nil
	} else {
		return false, fmt.Errorf("Failed to stop task: %v", id)
	}
}

func (manager *Manager) List() []uuid.UUID {
	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var tasks []models.AppTask
	if err := coll.Find(nil).All(&tasks); err != nil {
		return []uuid.UUID{}
	}
	ids := make([]uuid.UUID, 0, len(tasks))
	for _, task := range tasks {
		ids = append(ids, task.Id)
	}
	return ids
}

func NewManager() Manager {
	TaskManager = Manager{make(map[uuid.UUID]*Task)}
	return TaskManager
}

func GetTaskManager() *Manager {
	return &TaskManager
}

func (manager *Manager) RemoveTask(id uuid.UUID) {
	delete(manager.tasks, id)
}
<|endoftext|>


<commit_before>package sconsify

import (
	"errors"
	"fmt"
	"math/rand"
	"sort"
)

type Playlists struct {
	playlists         map[string]*Playlist
	currentIndexTrack int
	currentPlaylist   string
	playMode          int
	premadeTracks     []*Track
}

const (
	NormalMode     = iota
	RandomMode     = iota
	AllRandomMode  = iota
	SequentialMode = iota
)

func InitPlaylists() *Playlists {
	playlists := &Playlists{
		playlists: make(map[string]*Playlist),
		playMode:  NormalMode,
	}
	return playlists
}

func (playlists *Playlists) Get(name string) *Playlist {
	return playlists.playlists[name]
}

func (playlists *Playlists) Playlists() int {
	return len(playlists.playlists)
}

func (playlists *Playlists) AddPlaylist(name string, playlist *Playlist) {
	playlists.playlists[name] = playlist
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) Merge(newPlaylist *Playlists) {
	for key, value := range newPlaylist.playlists {
		playlists.playlists[key] = value
	}
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) GetNames() []string {
	names := make([]string, playlists.Playlists())
	i := 0
	for name, _ := range playlists.playlists {
		names[i] = name
		i++
	}
	return names
}

func (playlists *Playlists) Tracks() int {
	numberOfTracks := 0
	for _, playlist := range playlists.playlists {
		numberOfTracks += playlist.Tracks()
	}
	return numberOfTracks
}

func (playlists *Playlists) PremadeTracks() int {
	return len(playlists.premadeTracks)
}

func (playlists *Playlists) buildPlaylistForNewMode() error {
	if playlists.isNormalMode() {
		playlists.premadeTracks = nil
		return nil
	}

	var numberOfTracks int
	var playlist *Playlist
	if playlists.isRandomMode() {
		playlist = playlists.Get(playlists.currentPlaylist)
		if playlist != nil {
			numberOfTracks = playlist.Tracks()
		}
	} else {
		// all random and sequential
		numberOfTracks = playlists.Tracks()
	}

	if numberOfTracks == 0 {
		return errors.New("No tracks selected")
	}

	playlists.premadeTracks = make([]*Track, numberOfTracks)
	if playlists.isRandomMode() {
		playlists.buildRandomModeTracks(playlist, numberOfTracks)
	} else if playlists.isAllRandomMode() {
		playlists.buildAllRandomModeTracks(numberOfTracks)
	} else {
		// sequential
		playlists.buildSequentialModeTracks()
	}

	playlists.currentIndexTrack = -1

	return nil
}

func (playlists *Playlists) buildRandomModeTracks(playlist *Playlist, numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for i := 0; i < playlist.Tracks(); i++ {
		playlists.premadeTracks[perm[index]] = playlist.Track(i)
		index++
	}
}

func (playlists *Playlists) buildAllRandomModeTracks(numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for _, playlist := range playlists.playlists {
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[perm[index]] = playlist.Track(i)
			index++
		}
	}
}

func (playlists *Playlists) buildSequentialModeTracks() {
	names := playlists.GetNames()
	sort.Strings(names)

	index := 0
	for _, name := range names {
		playlist := playlists.playlists[name]
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[index] = playlist.Track(i)
			index++
		}
	}
}

func getRandomPermutation(numberOfTracks int) []int {
	return rand.Perm(numberOfTracks)
}

func (playlists *Playlists) GetModeAsString() string {
	if playlists.playMode == RandomMode {
		return "[Random] "
	}
	if playlists.playMode == AllRandomMode {
		return "[All Random] "
	}
	return ""
}

func (playlists *Playlists) SetCurrents(currentPlaylist string, currentIndexTrack int) error {
	if playlist, ok := playlists.playlists[currentPlaylist]; ok {
		if playlist.Tracks() > currentIndexTrack {
			playlists.currentPlaylist = currentPlaylist
			playlists.currentIndexTrack = currentIndexTrack
			return nil
		}
	}
	return errors.New(fmt.Sprintf("Invalid index [%v] track or current playlist [%v]", currentIndexTrack, currentPlaylist))
}

func (playlists *Playlists) GetNext() (*Track, bool) {
	repeating := false
	if playlists.premadeTracks != nil {
		playlists.currentIndexTrack++
		if playlists.currentIndexTrack >= len(playlists.premadeTracks) {
			playlists.currentIndexTrack = 0
			repeating = true
		}
		return playlists.premadeTracks[playlists.currentIndexTrack], repeating
	}

	playlist := playlists.Get(playlists.currentPlaylist)
	if playlist != nil {
		playlists.currentIndexTrack = playlist.GetNextTrack(playlists.currentIndexTrack)
		return playlist.Track(playlists.currentIndexTrack), repeating
	}

	return nil, false
}

func (playlists *Playlists) SetMode(mode int) {
	playlists.playMode = mode
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) InvertMode(mode int) int {
	if mode == playlists.playMode {
		playlists.SetMode(NormalMode)
	} else {
		playlists.SetMode(mode)
	}
	return playlists.playMode
}

func (playlists *Playlists) HasPlaylistSelected() bool {
	return playlists.currentPlaylist != ""
}

func (playlists *Playlists) isAllRandomMode() bool {
	return playlists.playMode == AllRandomMode
}

func (playlists *Playlists) isRandomMode() bool {
	return playlists.playMode == RandomMode
}

func (playlists *Playlists) isNormalMode() bool {
	return playlists.playMode == NormalMode
}
<commit_msg>Don't need to redeclare iota<commit_after>package sconsify

import (
	"errors"
	"fmt"
	"math/rand"
	"sort"
)

type Playlists struct {
	playlists         map[string]*Playlist
	currentIndexTrack int
	currentPlaylist   string
	playMode          int
	premadeTracks     []*Track
}

const (
	NormalMode = iota
	RandomMode
	AllRandomMode
	SequentialMode
)

func InitPlaylists() *Playlists {
	playlists := &Playlists{
		playlists: make(map[string]*Playlist),
		playMode:  NormalMode,
	}
	return playlists
}

func (playlists *Playlists) Get(name string) *Playlist {
	return playlists.playlists[name]
}

func (playlists *Playlists) Playlists() int {
	return len(playlists.playlists)
}

func (playlists *Playlists) AddPlaylist(name string, playlist *Playlist) {
	playlists.playlists[name] = playlist
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) Merge(newPlaylist *Playlists) {
	for key, value := range newPlaylist.playlists {
		playlists.playlists[key] = value
	}
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) GetNames() []string {
	names := make([]string, playlists.Playlists())
	i := 0
	for name, _ := range playlists.playlists {
		names[i] = name
		i++
	}
	return names
}

func (playlists *Playlists) Tracks() int {
	numberOfTracks := 0
	for _, playlist := range playlists.playlists {
		numberOfTracks += playlist.Tracks()
	}
	return numberOfTracks
}

func (playlists *Playlists) PremadeTracks() int {
	return len(playlists.premadeTracks)
}

func (playlists *Playlists) buildPlaylistForNewMode() error {
	if playlists.isNormalMode() {
		playlists.premadeTracks = nil
		return nil
	}

	var numberOfTracks int
	var playlist *Playlist
	if playlists.isRandomMode() {
		playlist = playlists.Get(playlists.currentPlaylist)
		if playlist != nil {
			numberOfTracks = playlist.Tracks()
		}
	} else {
		// all random and sequential
		numberOfTracks = playlists.Tracks()
	}

	if numberOfTracks == 0 {
		return errors.New("No tracks selected")
	}

	playlists.premadeTracks = make([]*Track, numberOfTracks)
	if playlists.isRandomMode() {
		playlists.buildRandomModeTracks(playlist, numberOfTracks)
	} else if playlists.isAllRandomMode() {
		playlists.buildAllRandomModeTracks(numberOfTracks)
	} else {
		// sequential
		playlists.buildSequentialModeTracks()
	}

	playlists.currentIndexTrack = -1

	return nil
}

func (playlists *Playlists) buildRandomModeTracks(playlist *Playlist, numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for i := 0; i < playlist.Tracks(); i++ {
		playlists.premadeTracks[perm[index]] = playlist.Track(i)
		index++
	}
}

func (playlists *Playlists) buildAllRandomModeTracks(numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for _, playlist := range playlists.playlists {
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[perm[index]] = playlist.Track(i)
			index++
		}
	}
}

func (playlists *Playlists) buildSequentialModeTracks() {
	names := playlists.GetNames()
	sort.Strings(names)

	index := 0
	for _, name := range names {
		playlist := playlists.playlists[name]
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[index] = playlist.Track(i)
			index++
		}
	}
}

func getRandomPermutation(numberOfTracks int) []int {
	return rand.Perm(numberOfTracks)
}

func (playlists *Playlists) GetModeAsString() string {
	if playlists.playMode == RandomMode {
		return "[Random] "
	}
	if playlists.playMode == AllRandomMode {
		return "[All Random] "
	}
	return ""
}

func (playlists *Playlists) SetCurrents(currentPlaylist string, currentIndexTrack int) error {
	if playlist, ok := playlists.playlists[currentPlaylist]; ok {
		if playlist.Tracks() > currentIndexTrack {
			playlists.currentPlaylist = currentPlaylist
			playlists.currentIndexTrack = currentIndexTrack
			return nil
		}
	}
	return errors.New(fmt.Sprintf("Invalid index [%v] track or current playlist [%v]", currentIndexTrack, currentPlaylist))
}

func (playlists *Playlists) GetNext() (*Track, bool) {
	repeating := false
	if playlists.premadeTracks != nil {
		playlists.currentIndexTrack++
		if playlists.currentIndexTrack >= len(playlists.premadeTracks) {
			playlists.currentIndexTrack = 0
			repeating = true
		}
		return playlists.premadeTracks[playlists.currentIndexTrack], repeating
	}

	playlist := playlists.Get(playlists.currentPlaylist)
	if playlist != nil {
		playlists.currentIndexTrack = playlist.GetNextTrack(playlists.currentIndexTrack)
		return playlist.Track(playlists.currentIndexTrack), repeating
	}

	return nil, false
}

func (playlists *Playlists) SetMode(mode int) {
	playlists.playMode = mode
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) InvertMode(mode int) int {
	if mode == playlists.playMode {
		playlists.SetMode(NormalMode)
	} else {
		playlists.SetMode(mode)
	}
	return playlists.playMode
}

func (playlists *Playlists) HasPlaylistSelected() bool {
	return playlists.currentPlaylist != ""
}

func (playlists *Playlists) isAllRandomMode() bool {
	return playlists.playMode == AllRandomMode
}

func (playlists *Playlists) isRandomMode() bool {
	return playlists.playMode == RandomMode
}

func (playlists *Playlists) isNormalMode() bool {
	return playlists.playMode == NormalMode
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudWatchLogStream_basic(t *testing.T) {
	var ls cloudwatchlogs.LogStream
	rName := acctest.RandString(15)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSCloudWatchLogStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudWatchLogStreamConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudWatchLogStreamExists("aws_cloudwatch_log_stream.foobar", &ls),
				),
			},
		},
	})
}

func TestAccAWSCloudWatchLogStream_disappears(t *testing.T) {
	var ls cloudwatchlogs.LogStream
	rName := acctest.RandString(15)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSCloudWatchLogStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudWatchLogStreamConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudWatchLogStreamExists("aws_cloudwatch_log_stream.foobar", &ls),
					testAccCheckCloudWatchLogStreamDisappears(&ls, rName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckCloudWatchLogStreamDisappears(ls *cloudwatchlogs.LogStream, lgn string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
		opts := &cloudwatchlogs.DeleteLogStreamInput{
			LogGroupName:  aws.String(lgn),
			LogStreamName: ls.LogStreamName,
		}
		if _, err := conn.DeleteLogStream(opts); err != nil {
			return err
		}
		return nil
	}
}

func testAccCheckCloudWatchLogStreamExists(n string, ls *cloudwatchlogs.LogStream) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		logGroupName := rs.Primary.Attributes["log_group_name"]
		conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
		logGroup, exists, err := lookupCloudWatchLogStream(conn, rs.Primary.ID, logGroupName, nil)
		if err != nil {
			return err
		}
		if !exists {
			return fmt.Errorf("Bad: LogStream %q does not exist", rs.Primary.ID)
		}

		*ls = *logGroup

		return nil
	}
}

func testAccCheckAWSCloudWatchLogStreamDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_cloudwatch_log_stream" {
			continue
		}

		logGroupName := rs.Primary.Attributes["log_group_name"]
		_, exists, err := lookupCloudWatchLogStream(conn, rs.Primary.ID, logGroupName, nil)
		if err != nil {
			return nil
		}

		if exists {
			return fmt.Errorf("Bad: LogStream still exists: %q", rs.Primary.ID)
		}

	}

	return nil
}

func TestValidateCloudWatchLogStreamName(t *testing.T) {
	validNames := []string{
		"test-log-stream",
		"my_sample_log_stream",
		"012345678",
		"logstream/1234",
	}
	for _, v := range validNames {
		_, errors := validateCloudWatchLogStreamName(v, "name")
		if len(errors) != 0 {
			t.Fatalf("%q should be a valid CloudWatch LogStream name: %q", v, errors)
		}
	}

	invalidNames := []string{
		acctest.RandString(513),
		"",
		"stringwith:colon",
	}
	for _, v := range invalidNames {
		_, errors := validateCloudWatchLogStreamName(v, "name")
		if len(errors) == 0 {
			t.Fatalf("%q should be an invalid CloudWatch LogStream name", v)
		}
	}
}

func testAccAWSCloudWatchLogStreamConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_cloudwatch_log_group" "foobar" {
    name = "%s"
}

resource "aws_cloudwatch_log_stream" "foobar" {
    name = "%s"
    log_group_name = "${aws_cloudwatch_log_group.foobar.id}"
}
`, rName, rName)
}
<commit_msg>add failing test<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudWatchLogStream_basic(t *testing.T) {
	var ls cloudwatchlogs.LogStream
	rName := acctest.RandString(15)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSCloudWatchLogStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudWatchLogStreamConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudWatchLogStreamExists("aws_cloudwatch_log_stream.foobar", &ls),
				),
			},
		},
	})
}

func TestAccAWSCloudWatchLogStream_disappears(t *testing.T) {
	var ls cloudwatchlogs.LogStream
	rName := acctest.RandString(15)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSCloudWatchLogStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudWatchLogStreamConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudWatchLogStreamExists("aws_cloudwatch_log_stream.foobar", &ls),
					testAccCheckCloudWatchLogStreamDisappears(&ls, rName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckCloudWatchLogStreamDisappears(ls *cloudwatchlogs.LogStream, lgn string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
		opts := &cloudwatchlogs.DeleteLogStreamInput{
			LogGroupName:  aws.String(lgn),
			LogStreamName: ls.LogStreamName,
		}
		if _, err := conn.DeleteLogStream(opts); err != nil {
			return err
		}
		return nil
	}
}

func TestAccAWSCloudWatchLogStream_disappears2(t *testing.T) {
	var ls cloudwatchlogs.LogStream
	rName := acctest.RandString(15)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSCloudWatchLogStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudWatchLogStreamConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudWatchLogStreamExists("aws_cloudwatch_log_stream.foobar", &ls),
					testAccCheckCloudWatchLogStreamDisappears2(&ls, rName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckCloudWatchLogStreamDisappears2(ls *cloudwatchlogs.LogStream, lgn string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
		opts := &cloudwatchlogs.DeleteLogGroupInput{
			LogGroupName: aws.String(lgn),
		}
		if _, err := conn.DeleteLogGroup(opts); err != nil {
			return err
		}
		return nil
	}
}

func testAccCheckCloudWatchLogStreamExists(n string, ls *cloudwatchlogs.LogStream) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		logGroupName := rs.Primary.Attributes["log_group_name"]
		conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
		logGroup, exists, err := lookupCloudWatchLogStream(conn, rs.Primary.ID, logGroupName, nil)
		if err != nil {
			return err
		}
		if !exists {
			return fmt.Errorf("Bad: LogStream %q does not exist", rs.Primary.ID)
		}

		*ls = *logGroup

		return nil
	}
}

func testAccCheckAWSCloudWatchLogStreamDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_cloudwatch_log_stream" {
			continue
		}

		logGroupName := rs.Primary.Attributes["log_group_name"]
		_, exists, err := lookupCloudWatchLogStream(conn, rs.Primary.ID, logGroupName, nil)
		if err != nil {
			return nil
		}

		if exists {
			return fmt.Errorf("Bad: LogStream still exists: %q", rs.Primary.ID)
		}

	}

	return nil
}

func TestValidateCloudWatchLogStreamName(t *testing.T) {
	validNames := []string{
		"test-log-stream",
		"my_sample_log_stream",
		"012345678",
		"logstream/1234",
	}
	for _, v := range validNames {
		_, errors := validateCloudWatchLogStreamName(v, "name")
		if len(errors) != 0 {
			t.Fatalf("%q should be a valid CloudWatch LogStream name: %q", v, errors)
		}
	}

	invalidNames := []string{
		acctest.RandString(513),
		"",
		"stringwith:colon",
	}
	for _, v := range invalidNames {
		_, errors := validateCloudWatchLogStreamName(v, "name")
		if len(errors) == 0 {
			t.Fatalf("%q should be an invalid CloudWatch LogStream name", v)
		}
	}
}

func testAccAWSCloudWatchLogStreamConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_cloudwatch_log_group" "foobar" {
    name = "%s"
}

resource "aws_cloudwatch_log_stream" "foobar" {
    name = "%s"
    log_group_name = "${aws_cloudwatch_log_group.foobar.id}"
}
`, rName, rName)
}
<|endoftext|>


<commit_before>package middleware

import "github.com/webx-top/echo"

func Validate(validator echo.Validator, skipper ...echo.Skipper) echo.MiddlewareFunc {
	var skip echo.Skipper
	if len(skipper) > 0 {
		skip = skipper[0]
	} else {
		skip = echo.DefaultSkipper
	}
	return func(h echo.Handler) echo.Handler {
		return echo.HandlerFunc(func(c echo.Context) error {
			if skip(c) {
				return h.Handle(c)
			}
			c.SetValidator(validator)
			return h.Handle(c)
		})
	}
}
<commit_msg>update<commit_after>package middleware

import "github.com/webx-top/echo"

func Validate(generator func() echo.Validator, skipper ...echo.Skipper) echo.MiddlewareFunc {
	var skip echo.Skipper
	if len(skipper) > 0 {
		skip = skipper[0]
	} else {
		skip = echo.DefaultSkipper
	}
	return func(h echo.Handler) echo.Handler {
		return echo.HandlerFunc(func(c echo.Context) error {
			if skip(c) {
				return h.Handle(c)
			}
			c.SetValidator(generator())
			return h.Handle(c)
		})
	}
}
<|endoftext|>


<commit_before>package mockery

import (
	"github.com/stretchr/testify/assert"
	"os"
	"path"
	"regexp"
	"testing"
)

type GatheringVisitor struct {
	Interfaces []*Interface
}

func (this *GatheringVisitor) VisitWalk(iface *Interface) error {
	this.Interfaces = append(this.Interfaces, iface)
	return nil
}

func NewGatheringVisitor() *GatheringVisitor {
	return &GatheringVisitor{
		Interfaces: make([]*Interface, 0, 1024),
	}
}

func TestWalkerHere(t *testing.T) {
	wd, err := os.Getwd()
	assert.NoError(t, err)
	w := Walker{
		BaseDir:   wd,
		Recursive: true,
		LimitOne:  false,
		Filter:    regexp.MustCompile(".*"),
	}

	gv := NewGatheringVisitor()

	w.Walk(gv)

	assert.Equal(t, 18, len(gv.Interfaces))
	first := gv.Interfaces[0]
	assert.Equal(t, "AsyncProducer", first.Name)
	assert.Equal(t, path.Join(wd, "fixtures/async.go"), first.Path)
}

func TestWalkerRegexp(t *testing.T) {
	wd, err := os.Getwd()
	assert.NoError(t, err)
	w := Walker{
		BaseDir:   wd,
		Recursive: true,
		LimitOne:  false,
		Filter:    regexp.MustCompile(".*AsyncProducer*."),
	}

	gv := NewGatheringVisitor()

	w.Walk(gv)

	assert.Equal(t, 1, len(gv.Interfaces))
	first := gv.Interfaces[0]
	assert.Equal(t, "AsyncProducer", first.Name)
	assert.Equal(t, path.Join(wd, "fixtures/async.go"), first.Path)
}
<commit_msg>Make test a bit less race-y<commit_after>package mockery

import (
	"os"
	"path"
	"regexp"
	"testing"

	"github.com/stretchr/testify/assert"
)

type GatheringVisitor struct {
	Interfaces []*Interface
}

func (this *GatheringVisitor) VisitWalk(iface *Interface) error {
	this.Interfaces = append(this.Interfaces, iface)
	return nil
}

func NewGatheringVisitor() *GatheringVisitor {
	return &GatheringVisitor{
		Interfaces: make([]*Interface, 0, 1024),
	}
}

func TestWalkerHere(t *testing.T) {
	wd, err := os.Getwd()
	assert.NoError(t, err)
	w := Walker{
		BaseDir:   wd,
		Recursive: true,
		LimitOne:  false,
		Filter:    regexp.MustCompile(".*"),
	}

	gv := NewGatheringVisitor()

	w.Walk(gv)

	assert.True(t, len(gv.Interfaces) > 10)
	first := gv.Interfaces[0]
	assert.Equal(t, "AsyncProducer", first.Name)
	assert.Equal(t, path.Join(wd, "fixtures/async.go"), first.Path)
}

func TestWalkerRegexp(t *testing.T) {
	wd, err := os.Getwd()
	assert.NoError(t, err)
	w := Walker{
		BaseDir:   wd,
		Recursive: true,
		LimitOne:  false,
		Filter:    regexp.MustCompile(".*AsyncProducer*."),
	}

	gv := NewGatheringVisitor()

	w.Walk(gv)

	assert.Equal(t, 1, len(gv.Interfaces))
	first := gv.Interfaces[0]
	assert.Equal(t, "AsyncProducer", first.Name)
	assert.Equal(t, path.Join(wd, "fixtures/async.go"), first.Path)
}
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	. "github.com/mickael-kerjean/filestash/server/common"
	"net/http"
	"path/filepath"
)

func ApiHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		header := res.Header()
		header.Set("Content-Type", "application/json")
		header.Set("Cache-Control", "no-cache")
		fn(ctx, res, req)
	}
}

func SecureAjax(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		if req.Header.Get("X-Requested-With") != "XmlHttpRequest" {
			Log.Warning("Intrusion detection: %s - %s", req.RemoteAddr, req.URL.String())
			SendErrorResult(res, ErrNotAllowed)
			return
		}
		fn(ctx, res, req)
	}
}

func StaticHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		header := res.Header()
		header.Set("Content-Type", GetMimeType(filepath.Ext(req.URL.Path)))
		header.Set("Cache-Control", "max-age=2592000")
		fn(ctx, res, req)
	}
}

func IndexHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		header := res.Header()
		header.Set("Content-Type", "text/html")
		header.Set("Cache-Control", "no-cache")
		header.Set("Referrer-Policy", "same-origin")
		header.Set("X-Content-Type-Options", "nosniff")
		header.Set("X-XSS-Protection", "1; mode=block")
		header.Set("X-Powered-By", fmt.Sprintf("Filestash/%s.%s <https://filestash.app>", APP_VERSION, BUILD_DATE))

		cspHeader := "default-src 'none'; "
		cspHeader += "style-src 'self' 'unsafe-inline'; "
		cspHeader += "font-src 'self' data:; "
		cspHeader += "manifest-src 'self'; "
		cspHeader += "script-src 'self' 'sha256-JNAde5CZQqXtYRLUk8CGgyJXo6C7Zs1lXPPClLM1YM4=' 'sha256-9/gQeQaAmVkFStl6tfCbHXn8mr6PgtxlH+hEp685lzY=' 'sha256-ER9LZCe8unYk8AJJ2qopE+rFh7OUv8QG5q3h6jZeoSk='; "
		cspHeader += "img-src 'self' blob: data: https://maps.wikimedia.org; "
		cspHeader += "connect-src 'self'; "
		cspHeader += "object-src 'self'; "
		cspHeader += "media-src 'self' blob:; "
		cspHeader += "worker-src 'self' blob:; "
		cspHeader += "form-action 'self'; base-uri 'self'; "
		cspHeader += "frame-src 'self'; "
		header.Set("Content-Security-Policy", cspHeader)
		fn(ctx, res, req)
	}
}

func SecureHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		if host := Config.Get("general.host").String(); host != "" {
			if req.Host != host && req.Host != fmt.Sprintf("%s:443", host) {
				Log.Error("Request coming from \"%s\" was blocked, only traffic from \"%s\" is allowed. You can change this from the admin console under configure -> host", req.Host, host)
				SendErrorResult(res, ErrNotAllowed)
				return
			}
		}
		header := res.Header()
		if Config.Get("general.force_ssl").Bool() {
			header.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
		}
		header.Set("X-Content-Type-Options", "nosniff")
		header.Set("X-XSS-Protection", "1; mode=block")
		fn(ctx, res, req)
	}
}
<commit_msg>clean (code): cleanup<commit_after>package middleware

import (
	"fmt"
	. "github.com/mickael-kerjean/filestash/server/common"
	"net/http"
	"path/filepath"
)

func ApiHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		header := res.Header()
		header.Set("Content-Type", "application/json")
		header.Set("Cache-Control", "no-cache")
		fn(ctx, res, req)
	}
}

func StaticHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		header := res.Header()
		header.Set("Content-Type", GetMimeType(filepath.Ext(req.URL.Path)))
		header.Set("Cache-Control", "max-age=2592000")
		fn(ctx, res, req)
	}
}

func IndexHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		header := res.Header()
		header.Set("Content-Type", "text/html")
		header.Set("Cache-Control", "no-cache")
		header.Set("Referrer-Policy", "same-origin")
		header.Set("X-Content-Type-Options", "nosniff")
		header.Set("X-XSS-Protection", "1; mode=block")
		header.Set("X-Powered-By", fmt.Sprintf("Filestash/%s.%s <https://filestash.app>", APP_VERSION, BUILD_DATE))

		cspHeader := "default-src 'none'; "
		cspHeader += "style-src 'self' 'unsafe-inline'; "
		cspHeader += "font-src 'self' data:; "
		cspHeader += "manifest-src 'self'; "
		cspHeader += "script-src 'self' 'sha256-JNAde5CZQqXtYRLUk8CGgyJXo6C7Zs1lXPPClLM1YM4=' 'sha256-9/gQeQaAmVkFStl6tfCbHXn8mr6PgtxlH+hEp685lzY=' 'sha256-ER9LZCe8unYk8AJJ2qopE+rFh7OUv8QG5q3h6jZeoSk='; "
		cspHeader += "img-src 'self' blob: data: https://maps.wikimedia.org; "
		cspHeader += "connect-src 'self'; "
		cspHeader += "object-src 'self'; "
		cspHeader += "media-src 'self' blob:; "
		cspHeader += "worker-src 'self' blob:; "
		cspHeader += "form-action 'self'; base-uri 'self'; "
		cspHeader += "frame-src 'self'; "
		header.Set("Content-Security-Policy", cspHeader)
		fn(ctx, res, req)
	}
}

func SecureHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		if host := Config.Get("general.host").String(); host != "" {
			if req.Host != host && req.Host != fmt.Sprintf("%s:443", host) {
				Log.Error("Request coming from \"%s\" was blocked, only traffic from \"%s\" is allowed. You can change this from the admin console under configure -> host", req.Host, host)
				SendErrorResult(res, ErrNotAllowed)
				return
			}
		}
		header := res.Header()
		if Config.Get("general.force_ssl").Bool() {
			header.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
		}
		header.Set("X-Content-Type-Options", "nosniff")
		header.Set("X-XSS-Protection", "1; mode=block")
		fn(ctx, res, req)
	}
}

func SecureAjax(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
	return func(ctx App, res http.ResponseWriter, req *http.Request) {
		if req.Header.Get("X-Requested-With") != "XmlHttpRequest" {
			Log.Warning("Intrusion detection: %s - %s", req.RemoteAddr, req.URL.String())
			SendErrorResult(res, ErrNotAllowed)
			return
		}
		fn(ctx, res, req)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"errors"
	"fmt"
	sq "github.com/Masterminds/squirrel"
	log "github.com/Sirupsen/logrus"
	"github.com/guregu/null"
	"github.com/lib/pq"
	"net/http"
	"strings"
	"time"
)

// Returned by a function returning only one listing (usually by ID)
type Listing struct {
	KeyID                int            `json:"keyId"`
	CreationDate         null.Time      `json:"creationDate"`
	LastModificationDate null.Time      `json:"lastModificationDate"`
	Title                string         `json:"title"`
	Description          null.String    `json:"description"`
	UserID               int            `json:"userId"`
	Username             null.String    `json:"username"`
	Price                null.Int       `json:"price"`
	Status               null.String    `json:"status"`
	ExpirationDate       null.Time      `json:"expirationDate"`
	Thumbnail            null.String    `json:"thumbnail"`
	Photos               pq.StringArray `json:"photos"`
	IsStarred            bool           `json:"isStarred"`
}

type listingQuery struct {
	Query            string
	OnlyStarred      bool
	OnlyMine         bool
	TruncationLength int    // number of characters to truncate listing descriptions to
	Limit            uint64 // maximum number of listings to return
	Offset           uint64 // offset in search results to send
	UserID           int
	MinPrice         int
	MaxPrice         int
	MinExpDate       time.Time
	MaxExpDate       time.Time
	MinCreateDate    time.Time
	MaxCreateDate    time.Time
}

func NewListingQuery() *listingQuery {
	q := new(listingQuery)
	q.TruncationLength = defaultTruncationLength
	q.Limit = defaultNumResults
	q.MinPrice = -1
	q.MaxPrice = -1
	return q
}

type IsStarred struct {
	IsStarred bool `json:"isStarred"`
}

// Returns the SQL query that returns true if a particular listing is starred
// by the user with key_id id. This method exists because dealing with nested
// SQL queries in squirrel is an ugly pain in the ass.
func isStarredBy(id int) string {
	// "But Perry!" you say,
	// "concatenating strings and putting it directly in an SQL query is bad!"
	// you say.
	// "You're exactly correct, of course. Unfortunately we need a nested sql
	// query here and I couldn't find any documentation for nested queries
	// using squirrel. (Or any other documentation on squirrel other than the
	// godocs). On the bright side, we're not actually opening ourselves to an
	// injection attack since id is guaranteed to be an int."
	// "But isn't this still annoying and ugly?"
	// "Yeah."
	return fmt.Sprint("exists( SELECT 1 FROM starred_listings",
		" WHERE starred_listings.listing_id=listings.key_id",
		" AND starred_listings.user_id=", id,
		" AND starred_listings.is_active)")
}

// Returns the most recent count listings, based on original date created.
// If queryStr is nonempty, filters that every returned item must have every word in either title or description
// On error, returns an error and the HTTP code associated with that error.
func ReadListings(db *sql.DB, query *listingQuery) ([]*Listing, error, int) {
	// Create listings statement
	stmt := psql.
		Select(
			"listings.key_id",
			"listings.creation_date",
			"listings.last_modification_date",
			"title",
			fmt.Sprintf("left(description, %d)", query.TruncationLength),
			"user_id",
			"users.net_id",
			"price",
			"status",
			"expiration_date",
			"thumbnail_url",
			isStarredBy(query.UserID),
			"photos",
		).
		From("listings").
		Where("listings.is_active=true").
		LeftJoin("users ON listings.user_id = users.key_id")

	// WARNING changes here will also requrire changes in savesearches.CheckNewLisitng.
	// If this is ever changed, we should really just refactor it into a
	// separate function that can be easily shared
	for _, word := range strings.Fields(query.Query) {
		stmt = stmt.Where("(lower(listings.title) LIKE lower(?) OR lower(listings.description) LIKE lower(?))", fmt.Sprint("%", word, "%"), fmt.Sprint("%", word, "%"))
	}

	if query.UserID == 0 && (query.OnlyStarred || query.OnlyMine) {
		return nil, errors.New("Unauthenticated user attempted to view profile data"), http.StatusUnauthorized
	}

	if query.MinPrice >= 0 {
		stmt = stmt.Where("listings.price >= ?", query.MinPrice)
	}

	if query.MaxPrice >= 0 {
		stmt = stmt.Where("listings.price <= ?", query.MaxPrice)
	}

	if !query.MinExpDate.IsZero() {
		stmt = stmt.Where("listings.expiration_date >= ? OR listings.expiration_date IS NULL", query.MinExpDate)
	}

	if !query.MaxExpDate.IsZero() {
		stmt = stmt.Where("listings.expiration_date <= ?", query.MaxExpDate)
	}

	if !query.MinCreateDate.IsZero() {
		stmt = stmt.Where("listings.creation_date >= ? OR listings.creation_date IS NULL", query.MinCreateDate)
	}

	if !query.MaxCreateDate.IsZero() {
		stmt = stmt.Where("listings.creation_date <= ?", query.MaxCreateDate)
	}

	if query.OnlyStarred {
		stmt = stmt.Where(isStarredBy(query.UserID))
	}

	if query.OnlyMine {
		stmt = stmt.Where(sq.Eq{"user_id": query.UserID})
	}

	stmt = stmt.OrderBy("listings.creation_date DESC")
	if query.Limit > defaultNumResults {
		stmt = stmt.Limit(query.Limit)
	} else {
		stmt = stmt.Limit(defaultNumResults)
	}
	stmt = stmt.Offset(query.Offset)

	queryStr, _, _ := stmt.ToSql()
	log.WithField("query", queryStr).Debug("query!")

	// Query db
	rows, err := stmt.RunWith(db).Query()
	if err != nil {
		return nil, err, http.StatusInternalServerError
	}
	defer rows.Close()

	// Populate listing structs
	listings := make([]*Listing, 0)
	for rows.Next() {
		l := new(Listing)
		err := rows.Scan(
			&l.KeyID,
			&l.CreationDate,
			&l.LastModificationDate,
			&l.Title,
			&l.Description,
			&l.UserID,
			&l.Username,
			&l.Price,
			&l.Status,
			&l.ExpirationDate,
			&l.Thumbnail,
			&l.IsStarred,
			&l.Photos,
		)
		if err != nil {
			return nil, err, http.StatusInternalServerError
		}
		listings = append(listings, l)
	}

	if err = rows.Err(); err != nil {
		return nil, err, http.StatusInternalServerError
	}

	return listings, nil, http.StatusOK
}

// Returns the most recent count listings, based on original date created. On error
// returns an error and the HTTP code associated with that error.
func ReadListing(db *sql.DB, id string) (Listing, error, int) {
	var listing Listing

	// Create listing query
	query := psql.
		Select(
			"listings.key_id",
			"listings.creation_date",
			"listings.last_modification_date",
			"title",
			"description",
			"user_id",
			"users.net_id",
			"price",
			"status",
			"expiration_date",
			"thumbnail_url",
			"photos",
		).
		From("listings").
		Where("listings.is_active=true").
		LeftJoin("users ON listings.user_id = users.key_id").
		Where(sq.Eq{"listings.key_id": id})

	// Query db for listing
	rows, err := query.RunWith(db).Query()
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}
	defer rows.Close()

	// Populate listing struct
	rows.Next()
	err = rows.Scan(
		&listing.KeyID,
		&listing.CreationDate,
		&listing.LastModificationDate,
		&listing.Title,
		&listing.Description,
		&listing.UserID,
		&listing.Username,
		&listing.Price,
		&listing.Status,
		&listing.ExpirationDate,
		&listing.Thumbnail,
		&listing.Photos,
	)
	if err == sql.ErrNoRows {
		return listing, err, http.StatusNotFound
	} else if err != nil {
		return listing, err, http.StatusInternalServerError
	}

	return listing, nil, http.StatusOK
}

// Inserts the given listing (belonging to userId) into the database. Returns
// listing with its new KeyID added.
func CreateListing(db *sql.DB, listing Listing, userId int) (Listing, error, int) {
	listing.UserID = userId

	if listing.Photos == nil {
		listing.Photos = []string{}
	}

	// Insert listing
	stmt := psql.Insert("listings").
		Columns("title", "description", "user_id", "price", "status",
			"expiration_date", "thumbnail_url", "photos").
		Values(listing.Title, listing.Description, userId, listing.Price,
			listing.Status, listing.ExpirationDate, listing.Thumbnail, listing.Photos).
		Suffix("RETURNING key_id, creation_date")

	// Add listing to database, retrieve the one we just added (now with a key_id)
	rows, err := stmt.RunWith(db).Query()
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}
	defer rows.Close()
	rows.Next()
	err = rows.Scan(&listing.KeyID, &listing.CreationDate)
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}

	// Insert listing into "caching" table for saved searches to run against
	stmt = psql.Insert("new_listings").
		Columns("key_id", "creation_date", "title", "description", "user_id", "price", "status",
			"expiration_date", "thumbnail_url", "photos").
		Values(listing.KeyID, listing.CreationDate, listing.Title, listing.Description, userId, listing.Price,
			listing.Status, listing.ExpirationDate, listing.Thumbnail, listing.Photos)

	// Populate listing struct
	_, err = stmt.RunWith(db).Exec()
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}

	// Send email(s) if this listing matches anyone's saved search.
	go CheckNewListing(db, listing)
	return listing, nil, http.StatusCreated
}

// Overwrites the listing in the database with the given id with the given listing
// (belonging to userId). Returns the updated listing.
func UpdateListing(db *sql.DB, id string, listing Listing, userId int) (error, int) {
	listing.UserID = userId

	// Update listing
	stmt := psql.Update("listings").
		SetMap(map[string]interface{}{
			"title":           listing.Title,
			"description":     listing.Description,
			"price":           listing.Price,
			"status":          listing.Status,
			"expiration_date": listing.ExpirationDate,
			"thumbnail_url":   listing.Thumbnail,
			"photos":          listing.Photos}).
		Where(sq.Eq{"listings.key_id": id,
			"listings.user_id": userId})

	// Update listing
	result, err := stmt.RunWith(db).Exec()
	return getUpdateResultCode(result, err)
}

// Deletes the listing in the database with the given id with the given listing
// (belonging to userId).
func DeleteListing(db *sql.DB, id string, userId int) (error, int) {
	// Delete listing
	stmt := psql.Delete("listings").
		Where(sq.Eq{"listings.key_id": id,
			"listings.user_id": userId})
	result, err := stmt.RunWith(db).Exec()

	// Remove from cache table if necessary
	stmt = psql.Delete("new_listings").
		Where(sq.Eq{"new_listings.key_id": id,
			"new_listings.user_id": userId})
	stmt.RunWith(db).Exec()

	return getUpdateResultCode(result, err)
}

// SetStar adds or removes a star, depending on whether add is set to true.
func SetStar(db *sql.DB, add bool, listingId string, userId int) (error, int) {
	if add {
		return addStar(db, listingId, userId)
	} else {
		return removeStar(db, listingId, userId)
	}
}

// addStar adds a star to the table for the given listingId and userId.
func addStar(db *sql.DB, listingId string, userId int) (error, int) {
	insertStarStmt := psql.Insert("starred_listings").
		Columns("user_id", "listing_id").
		Values(userId, listingId)

	// Query db for listing
	result, err := insertStarStmt.RunWith(db).Exec()
	return getUpdateResultCode(result, err)
}

// removeStar remvoes a star from the given listingId for a given userId.
func removeStar(db *sql.DB, listingId string, userId int) (error, int) {
	stmt := psql.Delete("starred_listings").
		Where(sq.Eq{"listing_id": listingId, "user_id": userId})

	// Query db for listing
	result, err := stmt.RunWith(db).Exec()
	return getUpdateResultCode(result, err)
}
<commit_msg>Remove unused code<commit_after>package models

import (
	"database/sql"
	"errors"
	"fmt"
	sq "github.com/Masterminds/squirrel"
	log "github.com/Sirupsen/logrus"
	"github.com/guregu/null"
	"github.com/lib/pq"
	"net/http"
	"strings"
	"time"
)

// Returned by a function returning only one listing (usually by ID)
type Listing struct {
	KeyID                int            `json:"keyId"`
	CreationDate         null.Time      `json:"creationDate"`
	LastModificationDate null.Time      `json:"lastModificationDate"`
	Title                string         `json:"title"`
	Description          null.String    `json:"description"`
	UserID               int            `json:"userId"`
	Username             null.String    `json:"username"`
	Price                null.Int       `json:"price"`
	Status               null.String    `json:"status"`
	ExpirationDate       null.Time      `json:"expirationDate"`
	Thumbnail            null.String    `json:"thumbnail"`
	Photos               pq.StringArray `json:"photos"`
	IsStarred            bool           `json:"isStarred"`
}

type listingQuery struct {
	Query            string
	OnlyStarred      bool
	OnlyMine         bool
	TruncationLength int    // number of characters to truncate listing descriptions to
	Limit            uint64 // maximum number of listings to return
	Offset           uint64 // offset in search results to send
	UserID           int
	MinPrice         int
	MaxPrice         int
	MinExpDate       time.Time
	MaxExpDate       time.Time
	MinCreateDate    time.Time
	MaxCreateDate    time.Time
}

func NewListingQuery() *listingQuery {
	q := new(listingQuery)
	q.TruncationLength = defaultTruncationLength
	q.Limit = defaultNumResults
	q.MinPrice = -1
	q.MaxPrice = -1
	return q
}

type IsStarred struct {
	IsStarred bool `json:"isStarred"`
}

// Returns the SQL query that returns true if a particular listing is starred
// by the user with key_id id. This method exists because dealing with nested
// SQL queries in squirrel is an ugly pain in the ass.
func isStarredBy(id int) string {
	// "But Perry!" you say,
	// "concatenating strings and putting it directly in an SQL query is bad!"
	// you say.
	// "You're exactly correct, of course. Unfortunately we need a nested sql
	// query here and I couldn't find any documentation for nested queries
	// using squirrel. (Or any other documentation on squirrel other than the
	// godocs). On the bright side, we're not actually opening ourselves to an
	// injection attack since id is guaranteed to be an int."
	// "But isn't this still annoying and ugly?"
	// "Yeah."
	return fmt.Sprint("exists( SELECT 1 FROM starred_listings",
		" WHERE starred_listings.listing_id=listings.key_id",
		" AND starred_listings.user_id=", id,
		" AND starred_listings.is_active)")
}

// Returns the most recent count listings, based on original date created.
// If queryStr is nonempty, filters that every returned item must have every word in either title or description
// On error, returns an error and the HTTP code associated with that error.
func ReadListings(db *sql.DB, query *listingQuery) ([]*Listing, error, int) {
	// Create listings statement
	stmt := psql.
		Select(
			"listings.key_id",
			"listings.creation_date",
			"listings.last_modification_date",
			"title",
			fmt.Sprintf("left(description, %d)", query.TruncationLength),
			"user_id",
			"users.net_id",
			"price",
			"status",
			"expiration_date",
			"thumbnail_url",
			isStarredBy(query.UserID),
			"photos",
		).
		From("listings").
		Where("listings.is_active=true").
		LeftJoin("users ON listings.user_id = users.key_id")

	for _, word := range strings.Fields(query.Query) {
		stmt = stmt.Where("(lower(listings.title) LIKE lower(?) OR lower(listings.description) LIKE lower(?))", fmt.Sprint("%", word, "%"), fmt.Sprint("%", word, "%"))
	}

	if query.UserID == 0 && (query.OnlyStarred || query.OnlyMine) {
		return nil, errors.New("Unauthenticated user attempted to view profile data"), http.StatusUnauthorized
	}

	if query.MinPrice >= 0 {
		stmt = stmt.Where("listings.price >= ?", query.MinPrice)
	}

	if query.MaxPrice >= 0 {
		stmt = stmt.Where("listings.price <= ?", query.MaxPrice)
	}

	if !query.MinExpDate.IsZero() {
		stmt = stmt.Where("listings.expiration_date >= ? OR listings.expiration_date IS NULL", query.MinExpDate)
	}

	if !query.MaxExpDate.IsZero() {
		stmt = stmt.Where("listings.expiration_date <= ?", query.MaxExpDate)
	}

	if !query.MinCreateDate.IsZero() {
		stmt = stmt.Where("listings.creation_date >= ? OR listings.creation_date IS NULL", query.MinCreateDate)
	}

	if !query.MaxCreateDate.IsZero() {
		stmt = stmt.Where("listings.creation_date <= ?", query.MaxCreateDate)
	}

	if query.OnlyStarred {
		stmt = stmt.Where(isStarredBy(query.UserID))
	}

	if query.OnlyMine {
		stmt = stmt.Where(sq.Eq{"user_id": query.UserID})
	}

	stmt = stmt.OrderBy("listings.creation_date DESC")
	if query.Limit > defaultNumResults {
		stmt = stmt.Limit(query.Limit)
	} else {
		stmt = stmt.Limit(defaultNumResults)
	}
	stmt = stmt.Offset(query.Offset)

	queryStr, _, _ := stmt.ToSql()
	log.WithField("query", queryStr).Debug("query!")

	// Query db
	rows, err := stmt.RunWith(db).Query()
	if err != nil {
		return nil, err, http.StatusInternalServerError
	}
	defer rows.Close()

	// Populate listing structs
	listings := make([]*Listing, 0)
	for rows.Next() {
		l := new(Listing)
		err := rows.Scan(
			&l.KeyID,
			&l.CreationDate,
			&l.LastModificationDate,
			&l.Title,
			&l.Description,
			&l.UserID,
			&l.Username,
			&l.Price,
			&l.Status,
			&l.ExpirationDate,
			&l.Thumbnail,
			&l.IsStarred,
			&l.Photos,
		)
		if err != nil {
			return nil, err, http.StatusInternalServerError
		}
		listings = append(listings, l)
	}

	if err = rows.Err(); err != nil {
		return nil, err, http.StatusInternalServerError
	}

	return listings, nil, http.StatusOK
}

// Returns the most recent count listings, based on original date created. On error
// returns an error and the HTTP code associated with that error.
func ReadListing(db *sql.DB, id string) (Listing, error, int) {
	var listing Listing

	// Create listing query
	query := psql.
		Select(
			"listings.key_id",
			"listings.creation_date",
			"listings.last_modification_date",
			"title",
			"description",
			"user_id",
			"users.net_id",
			"price",
			"status",
			"expiration_date",
			"thumbnail_url",
			"photos",
		).
		From("listings").
		Where("listings.is_active=true").
		LeftJoin("users ON listings.user_id = users.key_id").
		Where(sq.Eq{"listings.key_id": id})

	// Query db for listing
	rows, err := query.RunWith(db).Query()
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}
	defer rows.Close()

	// Populate listing struct
	rows.Next()
	err = rows.Scan(
		&listing.KeyID,
		&listing.CreationDate,
		&listing.LastModificationDate,
		&listing.Title,
		&listing.Description,
		&listing.UserID,
		&listing.Username,
		&listing.Price,
		&listing.Status,
		&listing.ExpirationDate,
		&listing.Thumbnail,
		&listing.Photos,
	)
	if err == sql.ErrNoRows {
		return listing, err, http.StatusNotFound
	} else if err != nil {
		return listing, err, http.StatusInternalServerError
	}

	return listing, nil, http.StatusOK
}

// Inserts the given listing (belonging to userId) into the database. Returns
// listing with its new KeyID added.
func CreateListing(db *sql.DB, listing Listing, userId int) (Listing, error, int) {
	listing.UserID = userId

	if listing.Photos == nil {
		listing.Photos = []string{}
	}

	// Insert listing
	stmt := psql.Insert("listings").
		Columns("title", "description", "user_id", "price", "status",
			"expiration_date", "thumbnail_url", "photos").
		Values(listing.Title, listing.Description, userId, listing.Price,
			listing.Status, listing.ExpirationDate, listing.Thumbnail, listing.Photos).
		Suffix("RETURNING key_id, creation_date")

	// Add listing to database, retrieve the one we just added (now with a key_id)
	rows, err := stmt.RunWith(db).Query()
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}
	defer rows.Close()
	rows.Next()
	err = rows.Scan(&listing.KeyID, &listing.CreationDate)
	if err != nil {
		return listing, err, http.StatusInternalServerError
	}

	// Send email(s) if this listing matches anyone's saved search.
	go CheckNewListing(db, listing)
	return listing, nil, http.StatusCreated
}

// Overwrites the listing in the database with the given id with the given listing
// (belonging to userId). Returns the updated listing.
func UpdateListing(db *sql.DB, id string, listing Listing, userId int) (error, int) {
	listing.UserID = userId

	// Update listing
	stmt := psql.Update("listings").
		SetMap(map[string]interface{}{
			"title":           listing.Title,
			"description":     listing.Description,
			"price":           listing.Price,
			"status":          listing.Status,
			"expiration_date": listing.ExpirationDate,
			"thumbnail_url":   listing.Thumbnail,
			"photos":          listing.Photos}).
		Where(sq.Eq{"listings.key_id": id,
			"listings.user_id": userId})

	// Update listing
	result, err := stmt.RunWith(db).Exec()
	return getUpdateResultCode(result, err)
}

// Deletes the listing in the database with the given id with the given listing
// (belonging to userId).
func DeleteListing(db *sql.DB, id string, userId int) (error, int) {
	// Delete listing
	stmt := psql.Delete("listings").
		Where(sq.Eq{"listings.key_id": id,
			"listings.user_id": userId})
	result, err := stmt.RunWith(db).Exec()

	return getUpdateResultCode(result, err)
}

// SetStar adds or removes a star, depending on whether add is set to true.
func SetStar(db *sql.DB, add bool, listingId string, userId int) (error, int) {
	if add {
		return addStar(db, listingId, userId)
	} else {
		return removeStar(db, listingId, userId)
	}
}

// addStar adds a star to the table for the given listingId and userId.
func addStar(db *sql.DB, listingId string, userId int) (error, int) {
	insertStarStmt := psql.Insert("starred_listings").
		Columns("user_id", "listing_id").
		Values(userId, listingId)

	// Query db for listing
	result, err := insertStarStmt.RunWith(db).Exec()
	return getUpdateResultCode(result, err)
}

// removeStar remvoes a star from the given listingId for a given userId.
func removeStar(db *sql.DB, listingId string, userId int) (error, int) {
	stmt := psql.Delete("starred_listings").
		Where(sq.Eq{"listing_id": listingId, "user_id": userId})

	// Query db for listing
	result, err := stmt.RunWith(db).Exec()
	return getUpdateResultCode(result, err)
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"reflect"

	"github.com/ungerik/go-reflection"
)

type ResultsHandler interface {
	HandleResults(args Args, argVals, resultVals []reflect.Value, resultErr error) error
}

type ResultsHandlerFunc func(args Args, argVals, resultVals []reflect.Value, resultErr error) error

func (f ResultsHandlerFunc) HandleResults(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
	return f(args, argVals, resultVals, resultErr)
}

func resultsToInterfaces(results []reflect.Value) ([]interface{}, error) {
	r := make([]interface{}, len(results))
	for i, result := range results {
		switch reflection.DerefValue(result).Kind() {
		case reflect.Struct, reflect.Slice, reflect.Array:
			b, err := json.MarshalIndent(result.Interface(), "", "  ")
			if err != nil {
				return nil, err
			}
			r[i] = string(b)
		default:
			r[i] = result.Interface()
		}
	}
	return r, nil
}

func PrintTo(writer io.Writer) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		_, err = fmt.Fprint(writer, r...)
		return err
	}
}

func PrintlnTo(writer io.Writer) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		_, err = fmt.Fprintln(writer, r...)
		return err
	}
}

var Println ResultsHandlerFunc = func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
	if resultErr != nil {
		return resultErr
	}
	r, err := resultsToInterfaces(resultVals)
	if err != nil || len(r) == 0 {
		return err
	}
	_, err = fmt.Println(r...)
	return err
}

func PrintlnWithPrefixTo(prefix string, writer io.Writer) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil {
			return err
		}
		r = append([]interface{}{prefix}, r...)
		_, err = fmt.Fprintln(writer, r...)
		return err
	}
}

func PrintlnWithPrefix(prefix string) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil {
			return err
		}
		r = append([]interface{}{prefix}, r...)
		_, err = fmt.Println(r...)
		return err
	}
}

func LogTo(logger *log.Logger) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		logger.Println(r...)
		return nil
	}
}

func LogWithPrefixTo(prefix string, logger *log.Logger) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		r = append([]interface{}{prefix}, r...)
		logger.Println(r...)
		return nil
	}
}

type PrintlnText string

func (t PrintlnText) HandleResults(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
	if resultErr != nil {
		return resultErr
	}
	_, err := fmt.Println(t)
	return err
}
<commit_msg>extracted Logger interface<commit_after>package command

import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"

	"github.com/ungerik/go-reflection"
)

type ResultsHandler interface {
	HandleResults(args Args, argVals, resultVals []reflect.Value, resultErr error) error
}

type ResultsHandlerFunc func(args Args, argVals, resultVals []reflect.Value, resultErr error) error

func (f ResultsHandlerFunc) HandleResults(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
	return f(args, argVals, resultVals, resultErr)
}

func resultsToInterfaces(results []reflect.Value) ([]interface{}, error) {
	r := make([]interface{}, len(results))
	for i, result := range results {
		switch reflection.DerefValue(result).Kind() {
		case reflect.Struct, reflect.Slice, reflect.Array:
			b, err := json.MarshalIndent(result.Interface(), "", "  ")
			if err != nil {
				return nil, err
			}
			r[i] = string(b)
		default:
			r[i] = result.Interface()
		}
	}
	return r, nil
}

func PrintTo(writer io.Writer) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		_, err = fmt.Fprint(writer, r...)
		return err
	}
}

func PrintlnTo(writer io.Writer) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		_, err = fmt.Fprintln(writer, r...)
		return err
	}
}

var Println ResultsHandlerFunc = func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
	if resultErr != nil {
		return resultErr
	}
	r, err := resultsToInterfaces(resultVals)
	if err != nil || len(r) == 0 {
		return err
	}
	_, err = fmt.Println(r...)
	return err
}

func PrintlnWithPrefixTo(prefix string, writer io.Writer) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil {
			return err
		}
		r = append([]interface{}{prefix}, r...)
		_, err = fmt.Fprintln(writer, r...)
		return err
	}
}

func PrintlnWithPrefix(prefix string) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil {
			return err
		}
		r = append([]interface{}{prefix}, r...)
		_, err = fmt.Println(r...)
		return err
	}
}

type Logger interface {
	Printf(format string, args ...interface{})
}

func LogTo(logger Logger) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		logger.Printf(fmt.Sprintln(r...))
		return nil
	}
}

func LogWithPrefixTo(prefix string, logger Logger) ResultsHandlerFunc {
	return func(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
		if resultErr != nil {
			return resultErr
		}
		r, err := resultsToInterfaces(resultVals)
		if err != nil || len(r) == 0 {
			return err
		}
		r = append([]interface{}{prefix}, r...)
		logger.Printf(fmt.Sprintln(r...))
		return nil
	}
}

type PrintlnText string

func (t PrintlnText) HandleResults(args Args, argVals, resultVals []reflect.Value, resultErr error) error {
	if resultErr != nil {
		return resultErr
	}
	_, err := fmt.Println(t)
	return err
}
<|endoftext|>


<commit_before>package server

import (
	"testing"
	"time"

	"github.com/cosminrentea/gobbler/server/fcm"
	"github.com/cosminrentea/gobbler/testutil"
	"github.com/stretchr/testify/assert"
)

func Test_Subscribe_on_random_node(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8080",
		NodeID:     1,
		NodePort:   20000,
		Remotes:    "localhost:20000",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8081",
		NodeID:     2,
		NodePort:   20001,
		Remotes:    "localhost:20000",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)

	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Subscribe_working_After_Node_Restart(t *testing.T) {
	// defer testutil.EnableDebugForMethod()()
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	nodeConfig1 := testClusterNodeConfig{
		HttpListen: "localhost:8082",
		NodeID:     1,
		NodePort:   20002,
		Remotes:    "localhost:20002",
	}
	node1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(node1)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8083",
		NodeID:     2,
		NodePort:   20003,
		Remotes:    "localhost:20002",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")

	// connect a clinet and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// stop a node, cleanup without removing directories
	node1.cleanup(false)
	time.Sleep(time.Millisecond * 150)

	// restart the service
	restartedNode1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(restartedNode1)
	defer restartedNode1.cleanup(true)

	restartedNode1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// send a message to the former subscription.
	client1, err = restartedNode1.client("user1", 1000, true)
	a.NoError(err)
	time.Sleep(time.Second)

	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err, "Subscription should work even after node restart")

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	restartedNode1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Independent_Receiving(t *testing.T) {
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8084",
		NodeID:     1,
		NodePort:   20004,
		Remotes:    "localhost:20004",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8085",
		NodeID:     2,
		NodePort:   20005,
		Remotes:    "localhost:20004",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// reset the counter
	node1.FCM.reset()

	// NOW connect to second node
	client2, err := node2.client("user2", 1000, true)
	a.NoError(err)
	err = client2.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(1)
}

func Test_NoReceiving_After_Unsubscribe(t *testing.T) {
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8086",
		NodeID:     1,
		NodePort:   20006,
		Remotes:    "localhost:20006",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8087",
		NodeID:     2,
		NodePort:   20007,
		Remotes:    "localhost:20006",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// Unsubscribe
	node2.Unsubscribe(testTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// reset the counter
	node1.FCM.reset()

	// and send a message again. No one should receive it
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(0)
}
<commit_msg>Skip cluster test if disabled<commit_after>package server

import (
	"testing"
	"time"

	"github.com/cosminrentea/gobbler/server/fcm"
	"github.com/cosminrentea/gobbler/testutil"
	"github.com/stretchr/testify/assert"
)

func Test_Subscribe_on_random_node(t *testing.T) {
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8080",
		NodeID:     1,
		NodePort:   20000,
		Remotes:    "localhost:20000",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8081",
		NodeID:     2,
		NodePort:   20001,
		Remotes:    "localhost:20000",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)

	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Subscribe_working_After_Node_Restart(t *testing.T) {
	// defer testutil.EnableDebugForMethod()()
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	nodeConfig1 := testClusterNodeConfig{
		HttpListen: "localhost:8082",
		NodeID:     1,
		NodePort:   20002,
		Remotes:    "localhost:20002",
	}
	node1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(node1)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8083",
		NodeID:     2,
		NodePort:   20003,
		Remotes:    "localhost:20002",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")

	// connect a clinet and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// stop a node, cleanup without removing directories
	node1.cleanup(false)
	time.Sleep(time.Millisecond * 150)

	// restart the service
	restartedNode1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(restartedNode1)
	defer restartedNode1.cleanup(true)

	restartedNode1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// send a message to the former subscription.
	client1, err = restartedNode1.client("user1", 1000, true)
	a.NoError(err)
	time.Sleep(time.Second)

	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err, "Subscription should work even after node restart")

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	restartedNode1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Independent_Receiving(t *testing.T) {
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8084",
		NodeID:     1,
		NodePort:   20004,
		Remotes:    "localhost:20004",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8085",
		NodeID:     2,
		NodePort:   20005,
		Remotes:    "localhost:20004",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// reset the counter
	node1.FCM.reset()

	// NOW connect to second node
	client2, err := node2.client("user2", 1000, true)
	a.NoError(err)
	err = client2.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(1)
}

func Test_NoReceiving_After_Unsubscribe(t *testing.T) {
	testutil.SkipIfDisabled(t)
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8086",
		NodeID:     1,
		NodePort:   20006,
		Remotes:    "localhost:20006",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8087",
		NodeID:     2,
		NodePort:   20007,
		Remotes:    "localhost:20006",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, fcm.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(testTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// Unsubscribe
	node2.Unsubscribe(testTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// reset the counter
	node1.FCM.reset()

	// and send a message again. No one should receive it
	err = client1.Send(testTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(0)
}
<|endoftext|>


<commit_before>package templates

// Token is the page users see when authenticated.
const Token = `
<!DOCTYPE html>

	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Token</title>

	<link rel="stylesheet" href="/static/css/normalize.css">
	<link rel="stylesheet" href="/static/css/skeleton.css">
	<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
	<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet">

Access Token

			<textarea style="font-size: 12pt" class="u-full-width code" readonly spellcheck="false" onclick="this.focus();this.select();">{{.Token}}</textarea>

				The token will expire in < 1 hour.

				Previously Issued Certificates

`
<commit_msg>Set the textarea height<commit_after>package templates

// Token is the page users see when authenticated.
const Token = `
<!DOCTYPE html>

	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Token</title>

	<link rel="stylesheet" href="/static/css/normalize.css">
	<link rel="stylesheet" href="/static/css/skeleton.css">
	<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
	<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet">

Access Token

			<textarea style="font-size: 12pt" class="u-full-width code" readonly spellcheck="false" onclick="this.focus();this.select();">{{.Token}}</textarea>

				The token will expire in < 1 hour.

				Previously Issued Certificates

`
<|endoftext|>