<commit_before>package runner

import (
	"errors"

	"github.com/Sirupsen/logrus"
	b "github.com/winchman/builder-core/builder"
	"github.com/winchman/builder-core/communication"
	p "github.com/winchman/builder-core/parser"
	"github.com/winchman/builder-core/unit-config"
)

// Flag is an option type for the RunBuild command
type Flag uint8

const (
	// KeepTemporaryTag instructs the builder not to delete the random uuid tag
	KeepTemporaryTag Flag = 1 << iota // KeepTemporaryTag == 1 (iota has been reset)

	noop1 Flag = 1 << iota // noop1 == 2 // here for example/testing purposes
	noop2 Flag = 1 << iota // noop2 == 4 // here for example/testing purposes
)

// Options encapsulates the options for RunBuild/RunBuildSynchronously
type Options struct {
	UnitConfig *unitconfig.UnitConfig
	ContextDir string

	// LogLevel is only used for RunBuildSynchronously, ignored for RunBuild
	// LogLevel defaults to PanicLevel if not set
	LogLevel logrus.Level
}

func shouldKeepTemporaryTag(flags []Flag) bool {
	var total Flag
	for _, flag := range flags {
		total |= flag
	}
	return total&KeepTemporaryTag == KeepTemporaryTag
}

// RunBuild runs a complete build for the provided unit config.  Currently, the
// channels argument is ignored but will be used in the future along with the
// LogMsg and StatusMsg interfaces
func RunBuild(opts Options, flags ...Flag) (comm.LogChan, comm.EventChan, comm.ExitChan) {

	var unitConfig = opts.UnitConfig
	var contextDir = opts.ContextDir

	var log = make(chan comm.LogEntry, 1)
	var event = make(chan comm.Event, 1)
	var exit = make(chan error)

	go func() {
		var err error

		if unitConfig == nil {
			exit <- errors.New("unit config may not be nil")
			return
		}

		parser := p.NewParser(p.NewParserOptions{
			ContextDir: contextDir,
			Log:        log,
			Event:      event,
		})
		commandSequence := parser.Parse(unitConfig)

		builder := b.NewBuilder(b.NewBuilderOptions{
			ContextDir: contextDir,
			Log:        log,
			Event:      event,
		})
		builder.KeepTemporaryTag = shouldKeepTemporaryTag(flags)
		if err = builder.BuildCommandSequence(commandSequence); err != nil {
			exit <- err
			return
		}

		exit <- nil
	}()

	return log, event, exit
}

// RunBuildSynchronously - run a build, wait for it to finish, log to stdout
func RunBuildSynchronously(opts Options, flags ...Flag) error {
	var logger = logrus.New()
	logger.Level = opts.LogLevel
	log, status, done := RunBuild(opts, flags...) // make sure to update this as needed
	for {
		select {
		case e, ok := <-log:
			if !ok {
				return errors.New("log channel closed prematurely")
			}
			logLevel(logger, e.Entry())
		case event, ok := <-status:
			if !ok {
				return errors.New("status channel closed prematurely")
			}
			logger.WithFields(event.Data()).Debugf("status event (type %s)", event.EventType())
		case err, ok := <-done:
			if !ok {
				return errors.New("exit channel closed prematurely")
			}
			return err
		}
	}
}

func logLevel(logger *logrus.Logger, e *logrus.Entry) {
	e.Logger = logger
	switch e.Level {
	case logrus.PanicLevel:
		e.Panicln(e.Message)
	case logrus.FatalLevel:
		e.Fatalln(e.Message)
	case logrus.ErrorLevel:
		e.Errorln(e.Message)
	case logrus.WarnLevel:
		e.Warnln(e.Message)
	case logrus.InfoLevel:
		e.Infoln(e.Message)
	default:
		e.Debugln(e.Message)
	}
}
<commit_msg>TRIVIAL rename function [ci skip]<commit_after>package runner

import (
	"errors"

	"github.com/Sirupsen/logrus"
	b "github.com/winchman/builder-core/builder"
	"github.com/winchman/builder-core/communication"
	p "github.com/winchman/builder-core/parser"
	"github.com/winchman/builder-core/unit-config"
)

// Flag is an option type for the RunBuild command
type Flag uint8

const (
	// KeepTemporaryTag instructs the builder not to delete the random uuid tag
	KeepTemporaryTag Flag = 1 << iota // KeepTemporaryTag == 1 (iota has been reset)

	noop1 Flag = 1 << iota // noop1 == 2 // here for example/testing purposes
	noop2 Flag = 1 << iota // noop2 == 4 // here for example/testing purposes
)

// Options encapsulates the options for RunBuild/RunBuildSynchronously
type Options struct {
	UnitConfig *unitconfig.UnitConfig
	ContextDir string

	// LogLevel is only used for RunBuildSynchronously, ignored for RunBuild
	// LogLevel defaults to PanicLevel if not set
	LogLevel logrus.Level
}

func shouldKeepTemporaryTag(flags []Flag) bool {
	var total Flag
	for _, flag := range flags {
		total |= flag
	}
	return total&KeepTemporaryTag == KeepTemporaryTag
}

// RunBuild runs a complete build for the provided unit config.  Currently, the
// channels argument is ignored but will be used in the future along with the
// LogMsg and StatusMsg interfaces
func RunBuild(opts Options, flags ...Flag) (comm.LogChan, comm.EventChan, comm.ExitChan) {

	var unitConfig = opts.UnitConfig
	var contextDir = opts.ContextDir

	var log = make(chan comm.LogEntry, 1)
	var event = make(chan comm.Event, 1)
	var exit = make(chan error)

	go func() {
		var err error

		if unitConfig == nil {
			exit <- errors.New("unit config may not be nil")
			return
		}

		parser := p.NewParser(p.NewParserOptions{
			ContextDir: contextDir,
			Log:        log,
			Event:      event,
		})
		commandSequence := parser.Parse(unitConfig)

		builder := b.NewBuilder(b.NewBuilderOptions{
			ContextDir: contextDir,
			Log:        log,
			Event:      event,
		})
		builder.KeepTemporaryTag = shouldKeepTemporaryTag(flags)
		if err = builder.BuildCommandSequence(commandSequence); err != nil {
			exit <- err
			return
		}

		exit <- nil
	}()

	return log, event, exit
}

// RunBuildSynchronously - run a build, wait for it to finish, log to stdout
func RunBuildSynchronously(opts Options, flags ...Flag) error {
	var logger = logrus.New()
	logger.Level = opts.LogLevel
	log, status, done := RunBuild(opts, flags...) // make sure to update this as needed
	for {
		select {
		case e, ok := <-log:
			if !ok {
				return errors.New("log channel closed prematurely")
			}
			logEntry(logger, e.Entry())
		case event, ok := <-status:
			if !ok {
				return errors.New("status channel closed prematurely")
			}
			logger.WithFields(event.Data()).Debugf("status event (type %s)", event.EventType())
		case err, ok := <-done:
			if !ok {
				return errors.New("exit channel closed prematurely")
			}
			return err
		}
	}
}

func logEntry(logger *logrus.Logger, e *logrus.Entry) {
	e.Logger = logger
	switch e.Level {
	case logrus.PanicLevel:
		e.Panicln(e.Message)
	case logrus.FatalLevel:
		e.Fatalln(e.Message)
	case logrus.ErrorLevel:
		e.Errorln(e.Message)
	case logrus.WarnLevel:
		e.Warnln(e.Message)
	case logrus.InfoLevel:
		e.Infoln(e.Message)
	default:
		e.Debugln(e.Message)
	}
}
<|endoftext|>


<commit_before>package smac

import (
	"reflect"
	"sort"
	"testing"
)

func Test_LinoConstruction(t *testing.T) {

	t.Log("Given the need to test the prefix map creation")
	{
		words := []string{"aaaa", "aabc", "abc", "bbb", "v", "vvv", "vvvaaa"}
		prefix0 := make(map[string]string)
		prefix1 := make(map[string]string)
		prefix2 := make(map[string]string)
		prefix3 := make(map[string]string)

		prefix1["a"] = "aaaa"
		prefix1["b"] = "bbb"
		prefix1["v"] = "v"

		for k, v := range prefix1 {
			prefix2[k] = v
		}

		prefix2["aa"] = "aaaa"
		prefix2["ab"] = "abc"
		prefix2["bb"] = "bbb"
		prefix2["vv"] = "vvv"

		for k, v := range prefix2 {
			prefix3[k] = v
		}

		prefix3["aaa"] = "aaaa"
		prefix3["aab"] = "aabc"
		prefix3["abc"] = "abc"
		prefix3["bbb"] = "bbb"
		prefix3["vvv"] = "vvv"

		sort.Strings(words)

		prefixes := makePrefixMap(words, 0)
		if !reflect.DeepEqual(prefixes, prefix0) {
			t.Log("Should be able to create a 0 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 0 len prefix map", checkMark)

		prefixes = makePrefixMap(words, 1)

		if !reflect.DeepEqual(prefixes, prefix1) {
			t.Fatal("Should be able to create a 1 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 1 len prefix map", checkMark)

		prefixes = makePrefixMap(words, 2)

		if !reflect.DeepEqual(prefixes, prefix2) {
			t.Fatal("Should be able to create a 2 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 2 len prefix map", checkMark)

		prefixes = makePrefixMap(words, 3)

		if !reflect.DeepEqual(prefixes, prefix3) {
			t.Fatal("Should be able to create a 3 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 3 len prefix map", checkMark)

	}

	t.Log("Given the need to test the lino creation")
	{

		words := []string{"aaaa", "aabc", "abc", "bbb", "v", "vvv", "vvvaaa"}
	}
}
<commit_msg>done testing lino engine construction<commit_after>package smac

import (
	"reflect"
	"sort"
	"testing"
)

func Test_LinoConstruction(t *testing.T) {

	t.Log("Given the need to test the prefix map creation")
	{
		words := []string{"aaaa", "aabc", "abc", "bbb", "v", "vvv", "vvvaaa"}
		prefix0 := make(map[string]string)
		prefix1 := make(map[string]string)
		prefix2 := make(map[string]string)
		prefix3 := make(map[string]string)

		prefix1["a"] = "aaaa"
		prefix1["b"] = "bbb"
		prefix1["v"] = "v"

		for k, v := range prefix1 {
			prefix2[k] = v
		}

		prefix2["aa"] = "aaaa"
		prefix2["ab"] = "abc"
		prefix2["bb"] = "bbb"
		prefix2["vv"] = "vvv"

		for k, v := range prefix2 {
			prefix3[k] = v
		}

		prefix3["aaa"] = "aaaa"
		prefix3["aab"] = "aabc"
		prefix3["abc"] = "abc"
		prefix3["bbb"] = "bbb"
		prefix3["vvv"] = "vvv"

		sort.Strings(words)

		prefixes := makePrefixMap(words, 0)
		if !reflect.DeepEqual(prefixes, prefix0) {
			t.Log("Should be able to create a 0 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 0 len prefix map", checkMark)

		prefixes = makePrefixMap(words, 1)

		if !reflect.DeepEqual(prefixes, prefix1) {
			t.Fatal("Should be able to create a 1 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 1 len prefix map", checkMark)

		prefixes = makePrefixMap(words, 2)

		if !reflect.DeepEqual(prefixes, prefix2) {
			t.Fatal("Should be able to create a 2 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 2 len prefix map", checkMark)

		prefixes = makePrefixMap(words, 3)

		if !reflect.DeepEqual(prefixes, prefix3) {
			t.Fatal("Should be able to create a 3 len prefix map", ballotX)
		}
		t.Log("Should be able to create a 3 len prefix map", checkMark)

	}

	t.Log("Given the need to test the lino creation")
	{

		words := []string{"aaaa", "aabc", "abc", "bbb", "v", "vvv", "vvvaaa"}
		autoComplete, _ := NewAutoCompleteLinoS(words, 0, 0, 0)

		for i, word := range words[:len(words)-1] {
			nextWord := autoComplete.wordMap[word].next
			if nextWord != words[i+1] {
				t.Fatal("Should be able to build a linked list of dictionary words", ballotX)
			}
		}
		t.Log("Should be able to build a linked list of dictionary words", checkMark)
		if autoComplete.head != "aaaa" {
			t.Fatal("Should be able to correctly initialize the head of an autocompleter", ballotX)
		}
		t.Log("Should be able to correctly initialize the head of an autocompleter", checkMark)
		if autoComplete.tail != "vvvaaa" {
			t.Fatal("Should be able to correctly initialize the tail of an autocompleter", ballotX)
		}
		t.Log("Should be able to correctly initialize the tail of an autocompleter", checkMark)
	}
}
<|endoftext|>


<commit_before>package testing

import (
	"sync"
	"time"

	"fmt"

	"reflect"

	"strings"

	"github.com/awslabs/aws-sdk-go/service/swf"
	"github.com/pborman/uuid"
	"github.com/sclasen/swfsm/activity"
	"github.com/sclasen/swfsm/fsm"
)

type TestAdapter interface {
	TestName() string
	Logf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	FailNow()
}

type TestConfig struct {
	Testing                    TestAdapter
	FSM                        *fsm.FSM
	StubFSM                    *fsm.FSM
	Workers                    []*activity.ActivityWorker
	StubbedWorkflows           []string
	ShortStubbedWorkflows      []string
	DefaultWaitTimeout         int
	DefaultActivityInterceptor activity.ActivityInterceptor
}

func NewTestListener(t TestConfig) *TestListener {
	if t.DefaultWaitTimeout == 0 {
		t.DefaultWaitTimeout = 10
	}

	tl := &TestListener{
		decisionOutcomes: make(chan DecisionOutcome, 1000),
		historyInterest:  make(map[string]chan *swf.HistoryEvent, 1000),
		decisionInterest: make(map[string]chan *swf.Decision, 1000),
		stateInterest:    make(map[string]chan string, 1000),
		dataInterest:     make(map[string]chan *StateData, 1000),
		DefaultWait:      time.Duration(t.DefaultWaitTimeout) * time.Second,
		TestID:           strings.Join([]string{t.Testing.TestName(), uuid.New()}, "-"),
		testAdapter:      t.Testing,
		dataType:         t.FSM.DataType,
		serializer:       t.FSM.Serializer,
	}

	t.FSM.ReplicationHandler = TestReplicator(tl.decisionOutcomes)
	t.FSM.DecisionInterceptor = fsm.NewComposedDecisionInterceptor(
		t.FSM.DecisionInterceptor,
		TestDecisionInterceptor(tl.TestID, t.StubbedWorkflows, t.ShortStubbedWorkflows),
	)
	t.FSM.TaskList = tl.TestFsmTaskList()

	if t.StubFSM != nil {
		t.StubFSM.ReplicationHandler = TestReplicator(tl.decisionOutcomes)
	}

	for _, w := range t.Workers {
		if w.ActivityInterceptor == nil {
			w.ActivityInterceptor = t.DefaultActivityInterceptor
		}
		w.TaskList = tl.TestWorkerTaskList(w)
		w.AllowPanics = true
	}

	tl.Start()
	return tl
}

type TestListener struct {
	decisionOutcomes chan DecisionOutcome
	historyInterest  map[string]chan *swf.HistoryEvent
	historyLock      sync.Mutex
	decisionInterest map[string]chan *swf.Decision
	decisionLock     sync.Mutex
	stateInterest    map[string]chan string
	stateLock        sync.Mutex
	dataInterest     map[string]chan *StateData
	dataLock         sync.Mutex
	DefaultWait      time.Duration
	testAdapter      TestAdapter
	TestID           string
	dataType         interface{}
	serializer       fsm.StateSerializer
}

func (tl *TestListener) TestFsmTaskList() string {
	return tl.TestID
}

func (tl *TestListener) TestWorkerTaskList(w *activity.ActivityWorker) string {
	return w.TaskList + tl.TestID
}

func (tl *TestListener) RegisterHistoryInterest(workflowID string) chan *swf.HistoryEvent {
	defer tl.historyLock.Unlock()
	tl.historyLock.Lock()
	historyChan, ok := tl.historyInterest[workflowID]
	if !ok {
		historyChan = make(chan *swf.HistoryEvent, 1000)
		tl.historyInterest[workflowID] = historyChan
	}
	return historyChan
}

func (tl *TestListener) RegisterDecisionInterest(workflowID string) chan *swf.Decision {
	defer tl.decisionLock.Unlock()
	tl.decisionLock.Lock()
	decisionChan, ok := tl.decisionInterest[workflowID]
	if !ok {
		decisionChan = make(chan *swf.Decision, 1000)
		tl.decisionInterest[workflowID] = decisionChan
	}
	return decisionChan
}

func (tl *TestListener) RegisterStateInterest(workflowID string) chan string {
	defer tl.stateLock.Unlock()
	tl.stateLock.Lock()
	stateChan, ok := tl.stateInterest[workflowID]
	if !ok {
		stateChan = make(chan string, 1000)
		tl.stateInterest[workflowID] = stateChan
	}
	return stateChan
}

func (tl *TestListener) RegisterDataInterest(workflowID string) chan *StateData {
	defer tl.dataLock.Unlock()
	tl.dataLock.Lock()
	dataChan, ok := tl.dataInterest[workflowID]
	if !ok {
		dataChan = make(chan *StateData, 1000)
		tl.dataInterest[workflowID] = dataChan
	}
	return dataChan
}

func (tl *TestListener) AwaitStateFor(workflowID, state string, waitFor time.Duration) {
	ch := tl.RegisterStateInterest(workflowID)
	timer := time.After(waitFor)

	for {
		select {
		case s := <-ch:
			tl.testAdapter.Logf("TestListener: await state for workflow=%s state=%s received-state=%s", workflowID, state, s)
			if s == state {
				return
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s state=%s", workflowID, state))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s state=%s", workflowID, state)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitState(workflowID, state string) {
	tl.AwaitStateFor(workflowID, state, tl.DefaultWait)
}

func (tl *TestListener) AwaitEventFor(workflowID string, waitFor time.Duration, predicate func(*swf.HistoryEvent) bool) {
	ch := tl.RegisterHistoryInterest(workflowID)
	timer := time.After(waitFor)
	for {
		select {
		case h := <-ch:
			if predicate(h) {
				tl.testAdapter.Logf("TestListener: await event for workflow=%s received-event=%s predicate=true", workflowID, *h.EventType)
				return
			} else {
				tl.testAdapter.Logf("TestListener: await event for workflow=%s received-event=%s predicate=false", workflowID, *h.EventType)
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s event", workflowID))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s event", workflowID)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitEvent(workflowID string, predicate func(*swf.HistoryEvent) bool) {
	tl.AwaitEventFor(workflowID, tl.DefaultWait, predicate)
}

func (tl *TestListener) AwaitDecisionFor(workflowID string, waitFor time.Duration, predicate func(*swf.Decision) bool) {
	ch := tl.RegisterDecisionInterest(workflowID)
	timer := time.After(waitFor)
	for {
		select {
		case h := <-ch:
			if predicate(h) {
				tl.testAdapter.Logf("TestListener: await decision for workflow=%s received-decision=%s predicate=true", workflowID, *h.DecisionType)
				return
			} else {
				tl.testAdapter.Logf("TestListener: await decision for workflow=%s received-decision=%s predicate=false", workflowID, *h.DecisionType)
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s decision", workflowID))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s decision", workflowID)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitDecision(workflowID string, predicate func(*swf.Decision) bool) {
	tl.AwaitDecisionFor(workflowID, tl.DefaultWait, predicate)
}

func (tl *TestListener) AwaitDataFor(workflowID string, waitFor time.Duration, predicate func(*StateData) bool) {
	ch := tl.RegisterDataInterest(workflowID)
	timer := time.After(waitFor)
	for {
		select {
		case d := <-ch:
			if predicate(d) {
				tl.testAdapter.Logf("TestListener: await data for workflow=%s received-data=%+v predicate=true", workflowID, d)
				return
			} else {
				tl.testAdapter.Logf("TestListener: await data for workflow=%s received-data=%+v predicate=false", workflowID, d)
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s data", workflowID))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s data", workflowID)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitData(workflowID string, predicate func(*StateData) bool) {
	tl.AwaitDataFor(workflowID, tl.DefaultWait, predicate)
}

func (tl *TestListener) Start() {
	tl.testAdapter.Logf("TestListener: Starting")
	go tl.forward()
}

func (tl *TestListener) Stop() {
	close(tl.decisionOutcomes)
}

func (tl *TestListener) forward() {
	tl.testAdapter.Logf("TestListener: Forwarding")
	for {
		select {
		case do, ok := <-tl.decisionOutcomes:
			tl.testAdapter.Logf("TestListener: ")
			if !ok {
				tl.testAdapter.Logf("TestListener: decisionOutcomes closed!!!!!!!!")
				return
			}

			workflow := *do.DecisionTask.WorkflowExecution.WorkflowID
			tl.testAdapter.Logf("TestListener: DecisionOutcome for workflow %s", workflow)
			//send history events
			if c, ok := tl.historyInterest[workflow]; ok {
				tl.testAdapter.Logf("TestListener: yes historyInterest for workflow %s", workflow)
				for i := len(do.DecisionTask.Events) - 1; i >= 0; i-- {
					event := do.DecisionTask.Events[i]
					if *event.EventID > *do.DecisionTask.PreviousStartedEventID {
						tl.testAdapter.Logf("TestListener: yes historyInterest for workflow %s %s", workflow, *do.DecisionTask.Events[i].EventType)
						c <- do.DecisionTask.Events[i]
					}
				}
			} else {
				tl.testAdapter.Logf("TestListener: no historyInterest for workflow %s", workflow)
			}
			//send decisions
			if c, ok := tl.decisionInterest[workflow]; ok {
				for _, d := range do.Decisions {
					tl.testAdapter.Logf("TestListener: yes decisionInterest for workflow %s %s", workflow, *d.DecisionType)
					c <- d
				}
			} else {
				tl.testAdapter.Logf("TestListener: no decisionInterest for workflow %s", workflow)
			}
			//send states
			if c, ok := tl.stateInterest[workflow]; ok {
				tl.testAdapter.Logf("TestListener: yes stateInterest for workflow %s %s", workflow, do.State)
				c <- do.State.StateName
			} else {
				tl.testAdapter.Logf("TestListener: no stateInterest for workflow %s", workflow)
			}
			//send data
			if c, ok := tl.dataInterest[workflow]; ok {
				tl.testAdapter.Logf("TestListener: yes stateInterest for workflow %s %s", workflow, do.State)
				stateData := &StateData{
					State: do.State.StateName,
					Data:  tl.deserialize(do.State.StateData),
				}
				c <- stateData
			} else {
				tl.testAdapter.Logf("TestListener: no stateInterest for workflow %s", workflow)
			}
		case <-time.After(1 * time.Second):
			tl.testAdapter.Logf("TestListener: warn, no DecisionOutcomes after 1 second")
		}
	}
}

func (tl *TestListener) deserialize(serialized string) interface{} {
	data := reflect.New(reflect.TypeOf(tl.dataType)).Interface()
	err := tl.serializer.Deserialize(serialized, data)
	if err != nil {
		panic("cant deserialize data")
	}
	return data
}
<commit_msg>Add option for failing activities once in tests<commit_after>package testing

import (
	"sync"
	"time"

	"fmt"

	"reflect"

	"strings"

	"github.com/awslabs/aws-sdk-go/service/swf"
	"github.com/pborman/uuid"
	"github.com/sclasen/swfsm/activity"
	"github.com/sclasen/swfsm/fsm"
)

type TestAdapter interface {
	TestName() string
	Logf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	FailNow()
}

type TestConfig struct {
	Testing               TestAdapter
	FSM                   *fsm.FSM
	StubFSM               *fsm.FSM
	Workers               []*activity.ActivityWorker
	StubbedWorkflows      []string
	ShortStubbedWorkflows []string
	DefaultWaitTimeout    int
	FailActivitiesOnce    bool
}

func NewTestListener(t TestConfig) *TestListener {
	if t.DefaultWaitTimeout == 0 {
		t.DefaultWaitTimeout = 10
	}

	tl := &TestListener{
		decisionOutcomes: make(chan DecisionOutcome, 1000),
		historyInterest:  make(map[string]chan *swf.HistoryEvent, 1000),
		decisionInterest: make(map[string]chan *swf.Decision, 1000),
		stateInterest:    make(map[string]chan string, 1000),
		dataInterest:     make(map[string]chan *StateData, 1000),
		DefaultWait:      time.Duration(t.DefaultWaitTimeout) * time.Second,
		TestID:           strings.Join([]string{t.Testing.TestName(), uuid.New()}, "-"),
		testAdapter:      t.Testing,
		dataType:         t.FSM.DataType,
		serializer:       t.FSM.Serializer,
	}

	t.FSM.ReplicationHandler = TestReplicator(tl.decisionOutcomes)
	t.FSM.DecisionInterceptor = fsm.NewComposedDecisionInterceptor(
		t.FSM.DecisionInterceptor,
		TestDecisionInterceptor(tl.TestID, t.StubbedWorkflows, t.ShortStubbedWorkflows),
	)
	t.FSM.TaskList = tl.TestFsmTaskList()

	if t.StubFSM != nil {
		t.StubFSM.ReplicationHandler = TestReplicator(tl.decisionOutcomes)
	}

	for _, w := range t.Workers {
		if t.FailActivitiesOnce {
			w.ActivityInterceptor = activity.NewComposedDecisionInterceptor(
				w.ActivityInterceptor,
				TestFailOnceActivityInterceptor(),
			)
		}

		w.TaskList = tl.TestWorkerTaskList(w)
		w.AllowPanics = true
	}

	tl.Start()
	return tl
}

type TestListener struct {
	decisionOutcomes chan DecisionOutcome
	historyInterest  map[string]chan *swf.HistoryEvent
	historyLock      sync.Mutex
	decisionInterest map[string]chan *swf.Decision
	decisionLock     sync.Mutex
	stateInterest    map[string]chan string
	stateLock        sync.Mutex
	dataInterest     map[string]chan *StateData
	dataLock         sync.Mutex
	DefaultWait      time.Duration
	testAdapter      TestAdapter
	TestID           string
	dataType         interface{}
	serializer       fsm.StateSerializer
}

func (tl *TestListener) TestFsmTaskList() string {
	return tl.TestID
}

func (tl *TestListener) TestWorkerTaskList(w *activity.ActivityWorker) string {
	return w.TaskList + tl.TestID
}

func (tl *TestListener) RegisterHistoryInterest(workflowID string) chan *swf.HistoryEvent {
	defer tl.historyLock.Unlock()
	tl.historyLock.Lock()
	historyChan, ok := tl.historyInterest[workflowID]
	if !ok {
		historyChan = make(chan *swf.HistoryEvent, 1000)
		tl.historyInterest[workflowID] = historyChan
	}
	return historyChan
}

func (tl *TestListener) RegisterDecisionInterest(workflowID string) chan *swf.Decision {
	defer tl.decisionLock.Unlock()
	tl.decisionLock.Lock()
	decisionChan, ok := tl.decisionInterest[workflowID]
	if !ok {
		decisionChan = make(chan *swf.Decision, 1000)
		tl.decisionInterest[workflowID] = decisionChan
	}
	return decisionChan
}

func (tl *TestListener) RegisterStateInterest(workflowID string) chan string {
	defer tl.stateLock.Unlock()
	tl.stateLock.Lock()
	stateChan, ok := tl.stateInterest[workflowID]
	if !ok {
		stateChan = make(chan string, 1000)
		tl.stateInterest[workflowID] = stateChan
	}
	return stateChan
}

func (tl *TestListener) RegisterDataInterest(workflowID string) chan *StateData {
	defer tl.dataLock.Unlock()
	tl.dataLock.Lock()
	dataChan, ok := tl.dataInterest[workflowID]
	if !ok {
		dataChan = make(chan *StateData, 1000)
		tl.dataInterest[workflowID] = dataChan
	}
	return dataChan
}

func (tl *TestListener) AwaitStateFor(workflowID, state string, waitFor time.Duration) {
	ch := tl.RegisterStateInterest(workflowID)
	timer := time.After(waitFor)

	for {
		select {
		case s := <-ch:
			tl.testAdapter.Logf("TestListener: await state for workflow=%s state=%s received-state=%s", workflowID, state, s)
			if s == state {
				return
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s state=%s", workflowID, state))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s state=%s", workflowID, state)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitState(workflowID, state string) {
	tl.AwaitStateFor(workflowID, state, tl.DefaultWait)
}

func (tl *TestListener) AwaitEventFor(workflowID string, waitFor time.Duration, predicate func(*swf.HistoryEvent) bool) {
	ch := tl.RegisterHistoryInterest(workflowID)
	timer := time.After(waitFor)
	for {
		select {
		case h := <-ch:
			if predicate(h) {
				tl.testAdapter.Logf("TestListener: await event for workflow=%s received-event=%s predicate=true", workflowID, *h.EventType)
				return
			} else {
				tl.testAdapter.Logf("TestListener: await event for workflow=%s received-event=%s predicate=false", workflowID, *h.EventType)
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s event", workflowID))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s event", workflowID)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitEvent(workflowID string, predicate func(*swf.HistoryEvent) bool) {
	tl.AwaitEventFor(workflowID, tl.DefaultWait, predicate)
}

func (tl *TestListener) AwaitDecisionFor(workflowID string, waitFor time.Duration, predicate func(*swf.Decision) bool) {
	ch := tl.RegisterDecisionInterest(workflowID)
	timer := time.After(waitFor)
	for {
		select {
		case h := <-ch:
			if predicate(h) {
				tl.testAdapter.Logf("TestListener: await decision for workflow=%s received-decision=%s predicate=true", workflowID, *h.DecisionType)
				return
			} else {
				tl.testAdapter.Logf("TestListener: await decision for workflow=%s received-decision=%s predicate=false", workflowID, *h.DecisionType)
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s decision", workflowID))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s decision", workflowID)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitDecision(workflowID string, predicate func(*swf.Decision) bool) {
	tl.AwaitDecisionFor(workflowID, tl.DefaultWait, predicate)
}

func (tl *TestListener) AwaitDataFor(workflowID string, waitFor time.Duration, predicate func(*StateData) bool) {
	ch := tl.RegisterDataInterest(workflowID)
	timer := time.After(waitFor)
	for {
		select {
		case d := <-ch:
			if predicate(d) {
				tl.testAdapter.Logf("TestListener: await data for workflow=%s received-data=%+v predicate=true", workflowID, d)
				return
			} else {
				tl.testAdapter.Logf("TestListener: await data for workflow=%s received-data=%+v predicate=false", workflowID, d)
			}
		case <-timer:
			panic(fmt.Sprintf("TestListener: timed out waiting for workflow=%s data", workflowID))
			tl.testAdapter.Fatalf("TestListener: timed out waiting for workflow=%s data", workflowID)
			tl.testAdapter.FailNow()
		}
	}
}

func (tl *TestListener) AwaitData(workflowID string, predicate func(*StateData) bool) {
	tl.AwaitDataFor(workflowID, tl.DefaultWait, predicate)
}

func (tl *TestListener) Start() {
	tl.testAdapter.Logf("TestListener: Starting")
	go tl.forward()
}

func (tl *TestListener) Stop() {
	close(tl.decisionOutcomes)
}

func (tl *TestListener) forward() {
	tl.testAdapter.Logf("TestListener: Forwarding")
	for {
		select {
		case do, ok := <-tl.decisionOutcomes:
			tl.testAdapter.Logf("TestListener: ")
			if !ok {
				tl.testAdapter.Logf("TestListener: decisionOutcomes closed!!!!!!!!")
				return
			}

			workflow := *do.DecisionTask.WorkflowExecution.WorkflowID
			tl.testAdapter.Logf("TestListener: DecisionOutcome for workflow %s", workflow)
			//send history events
			if c, ok := tl.historyInterest[workflow]; ok {
				tl.testAdapter.Logf("TestListener: yes historyInterest for workflow %s", workflow)
				for i := len(do.DecisionTask.Events) - 1; i >= 0; i-- {
					event := do.DecisionTask.Events[i]
					if *event.EventID > *do.DecisionTask.PreviousStartedEventID {
						tl.testAdapter.Logf("TestListener: yes historyInterest for workflow %s %s", workflow, *do.DecisionTask.Events[i].EventType)
						c <- do.DecisionTask.Events[i]
					}
				}
			} else {
				tl.testAdapter.Logf("TestListener: no historyInterest for workflow %s", workflow)
			}
			//send decisions
			if c, ok := tl.decisionInterest[workflow]; ok {
				for _, d := range do.Decisions {
					tl.testAdapter.Logf("TestListener: yes decisionInterest for workflow %s %s", workflow, *d.DecisionType)
					c <- d
				}
			} else {
				tl.testAdapter.Logf("TestListener: no decisionInterest for workflow %s", workflow)
			}
			//send states
			if c, ok := tl.stateInterest[workflow]; ok {
				tl.testAdapter.Logf("TestListener: yes stateInterest for workflow %s %s", workflow, do.State)
				c <- do.State.StateName
			} else {
				tl.testAdapter.Logf("TestListener: no stateInterest for workflow %s", workflow)
			}
			//send data
			if c, ok := tl.dataInterest[workflow]; ok {
				tl.testAdapter.Logf("TestListener: yes stateInterest for workflow %s %s", workflow, do.State)
				stateData := &StateData{
					State: do.State.StateName,
					Data:  tl.deserialize(do.State.StateData),
				}
				c <- stateData
			} else {
				tl.testAdapter.Logf("TestListener: no stateInterest for workflow %s", workflow)
			}
		case <-time.After(1 * time.Second):
			tl.testAdapter.Logf("TestListener: warn, no DecisionOutcomes after 1 second")
		}
	}
}

func (tl *TestListener) deserialize(serialized string) interface{} {
	data := reflect.New(reflect.TypeOf(tl.dataType)).Interface()
	err := tl.serializer.Deserialize(serialized, data)
	if err != nil {
		panic("cant deserialize data")
	}
	return data
}
<|endoftext|>


<commit_before>package semrel

import (
	"context"
	"github.com/Masterminds/semver"
	"github.com/google/go-github/github"
	"regexp"
	"strings"
)

type Change struct {
	Major, Minor, Patch bool
}

type Commit struct {
	SHA     string
	Raw     []string
	Type    string
	Scope   string
	Message string
	Change  Change
}

type Release struct {
	SHA     string
	Version *semver.Version
}

type Repository struct {
	Owner, Repo string
}

var commitPattern = regexp.MustCompile("^(\\w*)(?:\\((.*)\\))?\\: (.*)$")
var breakingPattern = regexp.MustCompile("BREAKING CHANGES?")

func ParseCommit(commit *github.RepositoryCommit) *Commit {
	c := new(Commit)
	c.SHA = *commit.SHA
	c.Raw = strings.Split(*commit.Commit.Message, "\n")
	found := commitPattern.FindAllStringSubmatch(c.Raw[0], -1)
	if len(found) < 1 {
		return c
	}
	c.Type = strings.ToLower(found[0][1])
	c.Scope = found[0][2]
	c.Message = found[0][3]
	c.Change = Change{
		Major: breakingPattern.MatchString(*commit.Commit.Message),
		Minor: c.Type == "feat",
		Patch: c.Type == "fix",
	}
	return c
}

func GetCommits(ctx context.Context, client *github.Client, repo *Repository) ([]*Commit, error) {
	opts := &github.CommitsListOptions{
		ListOptions: github.ListOptions{PerPage: 100},
	}
	commits, _, err := client.Repositories.ListCommits(ctx, repo.Owner, repo.Repo, opts)
	if err != nil {
		return nil, err
	}
	ret := make([]*Commit, len(commits))
	for i, commit := range commits {
		ret[i] = ParseCommit(commit)
	}
	return ret, nil
}

func GetLatestRelease(ctx context.Context, client *github.Client, repo *Repository) (*Release, error) {
	opts := &github.ListOptions{PerPage: 1}
	tags, _, err := client.Repositories.ListTags(ctx, repo.Owner, repo.Repo, opts)
	if err != nil {
		return nil, err
	}
	v, _ := semver.NewVersion(*tags[0].Name)
	return &Release{*tags[0].Commit.SHA, v}, nil
}

func GetChange(commits []*Commit, release *Release) Change {
	var change Change
	for _, commit := range commits {
		if release.SHA == commit.SHA {
			break
		}
		change.Major = change.Major || commit.Change.Major
		change.Minor = change.Minor || commit.Change.Minor
		change.Patch = change.Patch || commit.Change.Patch
	}
	return change
}

func ApplyChange(version *semver.Version, change Change) *semver.Version {
	var newVersion semver.Version
	switch {
	case change.Major:
		newVersion = version.IncMajor()
		break
	case change.Minor:
		newVersion = version.IncMinor()
		break
	case change.Patch:
		newVersion = version.IncPatch()
		break
	default:
		return nil
	}
	return &newVersion
}
<commit_msg>feat: add release feature<commit_after>package semrel

import (
	"context"
	"fmt"
	"github.com/Masterminds/semver"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"regexp"
	"strings"
	"time"
)

var commitPattern = regexp.MustCompile("^(\\w*)(?:\\((.*)\\))?\\: (.*)$")
var breakingPattern = regexp.MustCompile("BREAKING CHANGES?")

type Change struct {
	Major, Minor, Patch bool
}

type Commit struct {
	SHA     string
	Raw     []string
	Type    string
	Scope   string
	Message string
	Change  Change
}

type Release struct {
	SHA     string
	Version *semver.Version
}

type Repository struct {
	Owner, Repo string
	Ctx         context.Context
	Client      *github.Client
}

func NewRepository(ctx context.Context, slug, token string) *Repository {
	repo := new(Repository)
	splited := strings.Split(slug, "/")
	repo.Owner = splited[0]
	repo.Repo = splited[1]
	repo.Ctx = ctx
	repo.Client = github.NewClient(oauth2.NewClient(ctx, oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: token},
	)))
	return repo
}

func parseCommit(commit *github.RepositoryCommit) *Commit {
	c := new(Commit)
	c.SHA = *commit.SHA
	c.Raw = strings.Split(*commit.Commit.Message, "\n")
	found := commitPattern.FindAllStringSubmatch(c.Raw[0], -1)
	if len(found) < 1 {
		return c
	}
	c.Type = strings.ToLower(found[0][1])
	c.Scope = found[0][2]
	c.Message = found[0][3]
	c.Change = Change{
		Major: breakingPattern.MatchString(*commit.Commit.Message),
		Minor: c.Type == "feat",
		Patch: c.Type == "fix",
	}
	return c
}

func (repo *Repository) GetCommits() ([]*Commit, error) {
	opts := &github.CommitsListOptions{
		ListOptions: github.ListOptions{PerPage: 100},
	}
	commits, _, err := repo.Client.Repositories.ListCommits(repo.Ctx, repo.Owner, repo.Repo, opts)
	if err != nil {
		return nil, err
	}
	ret := make([]*Commit, len(commits))
	for i, commit := range commits {
		ret[i] = parseCommit(commit)
	}
	return ret, nil
}

func (repo *Repository) GetLatestRelease() (*Release, error) {
	opts := &github.ListOptions{PerPage: 1}
	tags, _, err := repo.Client.Repositories.ListTags(repo.Ctx, repo.Owner, repo.Repo, opts)
	if err != nil {
		return nil, err
	}
	if len(tags) == 0 {
		return &Release{"", &semver.Version{}}, nil
	}
	v, _ := semver.NewVersion(*tags[0].Name)
	return &Release{*tags[0].Commit.SHA, v}, nil
}

func (repo *Repository) CreateRelease(commits []*Commit, latestRelease *Release, newVersion *semver.Version) error {
	tag := fmt.Sprintf("v%s", newVersion.String())
	sha := commits[0].SHA
	changelog := GetChangelog(commits, latestRelease, newVersion)
	opts := &github.RepositoryRelease{
		TagName:         &tag,
		TargetCommitish: &sha,
		Body:            &changelog,
	}
	_, _, err := repo.Client.Repositories.CreateRelease(repo.Ctx, repo.Owner, repo.Repo, opts)
	if err != nil {
		return err
	}
	return nil
}

func CaluclateChange(commits []*Commit, latestRelease *Release) Change {
	var change Change
	for _, commit := range commits {
		if latestRelease.SHA == commit.SHA {
			break
		}
		change.Major = change.Major || commit.Change.Major
		change.Minor = change.Minor || commit.Change.Minor
		change.Patch = change.Patch || commit.Change.Patch
	}
	return change
}

func ApplyChange(version *semver.Version, change Change) *semver.Version {
	if version.Major() == 0 {
		change.Major = true
	}
	var newVersion semver.Version
	switch {
	case change.Major:
		newVersion = version.IncMajor()
		break
	case change.Minor:
		newVersion = version.IncMinor()
		break
	case change.Patch:
		newVersion = version.IncPatch()
		break
	default:
		return nil
	}
	return &newVersion
}

func GetNewVersion(commits []*Commit, latestRelease *Release) *semver.Version {
	return ApplyChange(latestRelease.Version, CaluclateChange(commits, latestRelease))
}

func GetChangelog(commits []*Commit, latestRelease *Release, newVersion *semver.Version) string {
	ret := fmt.Sprintf("## %s (%s)\n\n", newVersion.String(), time.Now().UTC().Format("2006-01-02"))
	for _, commit := range commits {
		if latestRelease.SHA == commit.SHA {
			break
		}
		if commit.Type == "" {
			continue
		}
		ret += fmt.Sprintf("%s (%s)\n", commit.Raw[0], commit.SHA[:8])
	}
	return ret
}
<|endoftext|>


<commit_before>package cat

import "net"

var Tchan chan Transaction = make(chan Transaction)

//sender_run is internally used and only called by Cat_init_if.
//sender_run receive Transaction from a specific channel,
//encode Transaction and start new goroutines to send Transaction to backend.
//Basically, only 1 goroutine keeps the function.
func sender_run() {
}

//sender_send is internally used and only called by sender_run
func sender_send(conn net.Conn, data []byte) {
}
<commit_msg>sender receive transaction<commit_after>package cat

import "net"
import "fmt"

var Tchan chan Transaction = make(chan Transaction, 256)

//sender_run is internally used and only called by Cat_init_if.
//sender_run receive Transaction from a specific channel,
//encode Transaction and start new goroutines to send Transaction to backend.
//Basically, only 1 goroutine keeps the function.
func sender_run() {
	for {
		t := <-Tchan
		fmt.Println(t.GetName())
		fmt.Println(t.GetType())
	}
}

//sender_send is internally used and only called by sender_run
func sender_send(conn net.Conn, data []byte) {
}
<|endoftext|>


<commit_before>package main

import (
  "github.com/codegangsta/martini"
  "encoding/json"
  "net/http"
  "os"
  "strconv"
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

func main() {
  m := martini.Classic()

  dbPassword := os.Getenv("OOPS_DB_PASS")
  db, err := gorm.Open("mysql", "oops:" + dbPassword + "@tcp(15.126.247.23:3306)/oops")
  if err != nil { panic(err) }
  //defer db.Close() //TODO what should this be?

  //db.SetPool(10)

  db.LogMode(true)

  m.Map(db)

  //These are all the years of active school data. We could also derive it from the DB, possibly
  years := []string{"20022003", "20032004", "20042005", "20052006", "20062007", "20072008", "20082009", "20092010", "20102011", "20112012", "20122013"}

  m.Get("/schools", func(res http.ResponseWriter) string {
    var allSchools = []School{}
    db.Find(&allSchools)
    return render(res, allSchools)
  })

  //TODO need to also return stats by year -- needs to return []DistrictsByYear - district level stats for each year and each district
  m.Get("/districts", func(res http.ResponseWriter) string {
    allDistricts := []District{}
    db.Find(&allDistricts)

    districtsByYear := []DistrictsByYear{}
    for _, year := range years {
      districtByYear := DistrictsByYear{
        Year: year,
      }

      allDistrictsWithYears := []DistrictYear{}
      for _, district := range allDistricts {

        //FIXME hardcoded
        district.Latitude = 41.31027811
        district.Longitude = -96.146874

        districtWithYear := DistrictYear {
          EnrollmentSize: 2555,
          District: district,
        }
        allDistrictsWithYears = append(allDistrictsWithYears, districtWithYear)
      }

      districtByYear.Districts = allDistrictsWithYears
      districtsByYear = append(districtsByYear, districtByYear)
    }

    return render(res, districtsByYear)
  })

  type YearAndSchool struct {
  	SchoolId	int64
  	Years 		string
  }

  //TODO need to also return stats by year for each school in the district
  m.Get("/district/:id", func(res http.ResponseWriter, params martini.Params) string {
    schoolsByYear := []SchoolsByYear{}

    schoolsInDistrict := []School{}
    db.Where("district_id = ?", params["id"]).Find(&schoolsInDistrict)

    var schoolStats = []SchoolStat{}
    db.Where("school_id LIKE ? and teacher_size is not null", params["id"] + "%").Find(&schoolStats)

    schoolAndYearToStats := map[YearAndSchool]SchoolStat{}
    for _, schoolStat := range schoolStats {
    	schoolAndYearToStats[YearAndSchool{
    		SchoolId: schoolStat.SchoolId,
    		Years: schoolStat.Years,
    	}] = schoolStat
    }

    //TODO Use years from db? - if so make sure they're in the right order
    for _, year := range years {

      schoolsInYear := []SchoolYear{}
      for _, school := range schoolsInDistrict {
        var enrollment = schoolAndYearToStats[YearAndSchool{
    		SchoolId: school.Id,
    		Years: year,
    		}].EnrollmentSize
    	if enrollment > 0 {
	        schoolsInYear = append(schoolsInYear, SchoolYear {
	          EnrollmentSize: enrollment,
	          School: school,
	        })
        }
      }

      schoolsByYear = append(schoolsByYear, SchoolsByYear{
        Year: year,
        Schools: schoolsInYear,
      })
    }

    return render(res, schoolsByYear)

  })

  //TODO Possible clean-up opportunities here, but it's looking pretty decent
  m.Get("/school/:id", func(res http.ResponseWriter, params martini.Params) string {
    var school = School{
      //FIXME hardcoded data
      Latitude : 41.31027811,
      Longitude : -96.146874,
    }
    schoolId := params["id"]
    db.Where("id = ?", schoolId).First(&school)

    var classStats = []ClassStat{}
    db.Where("school_id = ?", params["id"]).Find(&classStats)

    var yearsToEnrollments = map[string][]GradeEnrollment{}
    for _, row := range classStats {
      if row.EnrollmentSize > 0 {
        yearsToEnrollments[row.Years] = append(yearsToEnrollments[row.Years], GradeEnrollment{
          Grade: row.Grade,
          EnrollmentSize:    row.EnrollmentSize,
        })
      }
    }

    var schoolStats = []SchoolStat{}
    db.Where("school_id = ? and teacher_size is not null", params["id"]).Find(&schoolStats)

    var yearsToTotalStats = map[string]SchoolStat{}
    for _, row := range schoolStats {
    	yearsToTotalStats[row.Years] = row
    }

    var enrollmentData = []EnrollmentByYear{}
    //Using hard coded years to make sure they're returned in order:
    for _, year := range years {

	enrollment := yearsToEnrollments[year]

      //calculate Students field
      //Could pull from SchoolStat, but will want to change
      //query above and deal properly with teacher_size being null
      var classSize int64 = 0
      for _, i := range enrollment { classSize += i.EnrollmentSize }

	  teachersAsFloat, _ := strconv.ParseFloat(yearsToTotalStats[year].TeacherSize, 64)
      enrollmentData = append(enrollmentData, EnrollmentByYear{
        Year: year,
        GradeEnrollment: enrollment,
        Students: classSize,
        Teachers: int64(teachersAsFloat),
      })
    }

    //put it all together
    var allData = SchoolWithEnrollment {
      School: school,
      EnrollmentByYear: enrollmentData,
    }

    return render(res, allData)
  })

  m.Run()
}

func render(res http.ResponseWriter, data interface{}) string {
  thing, err := json.Marshal(data)
  if err != nil { panic(err) }
  return asJson(res, thing)
}

func asJson(res http.ResponseWriter, data []byte) string {
  res.Header().Set("Content-Type", "application/json")
  res.Header().Set("Access-Control-Allow-Origin", "*")
  return string(data[:])
}
<commit_msg>Update server.go<commit_after>package main

import (
  "github.com/codegangsta/martini"
  "encoding/json"
  "net/http"
  "os"
  "strconv"
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

func main() {
  m := martini.Classic()

  dbPassword := os.Getenv("OOPS_DB_PASS")
  db, err := gorm.Open("mysql", "oops:" + dbPassword + "@tcp(15.126.247.23:3306)/oops")
  if err != nil { panic(err) }
  //defer db.Close() //TODO what should this be?

  //db.SetPool(10)

  db.LogMode(true)

  m.Map(db)

  //These are all the years of active school data. We could also derive it from the DB, possibly
  years := []string{"20022003", "20032004", "20042005", "20052006", "20062007", "20072008", "20082009", "20092010", "20102011", "20112012", "20122013"}

  m.Get("/schools", func(res http.ResponseWriter) string {
    var allSchools = []School{}
    db.Find(&allSchools)
    return render(res, allSchools)
  })

  //TODO need to also return stats by year -- needs to return []DistrictsByYear - district level stats for each year and each district
  m.Get("/districts", func(res http.ResponseWriter) string {
    allDistricts := []District{}
    db.Find(&allDistricts)

    districtsByYear := []DistrictsByYear{}
    for _, year := range years {
      districtByYear := DistrictsByYear{
        Year: year,
      }

      allDistrictsWithYears := []DistrictYear{}
      for _, district := range allDistricts {

        //FIXME hardcoded
        district.Latitude = 41.31027811
        district.Longitude = -96.146874

        districtWithYear := DistrictYear {
          EnrollmentSize: 2555,
          District: district,
        }
        allDistrictsWithYears = append(allDistrictsWithYears, districtWithYear)
      }

      districtByYear.Districts = allDistrictsWithYears
      districtsByYear = append(districtsByYear, districtByYear)
    }

    return render(res, districtsByYear)
  })

  type YearAndSchool struct {
  	SchoolId	int64
  	Years 		string
  }

  //TODO need to also return stats by year for each school in the district
  m.Get("/district/:id", func(res http.ResponseWriter, params martini.Params) string {
    schoolsByYear := []SchoolsByYear{}

    schoolsInDistrict := []School{}
    db.Where("district_id = ?", params["id"]).Find(&schoolsInDistrict)

    var schoolStats = []SchoolStat{}
    db.Where("school_id LIKE ?", params["id"] + "%").Find(&schoolStats)

    schoolAndYearToStats := map[YearAndSchool]SchoolStat{}
    for _, schoolStat := range schoolStats {
    	schoolAndYearToStats[YearAndSchool{
    		SchoolId: schoolStat.SchoolId,
    		Years: schoolStat.Years,
    	}] = schoolStat
    }

    //TODO Use years from db? - if so make sure they're in the right order
    for _, year := range years {

      schoolsInYear := []SchoolYear{}
      for _, school := range schoolsInDistrict {
        var enrollment = schoolAndYearToStats[YearAndSchool{
    		SchoolId: school.Id,
    		Years: year,
    		}].EnrollmentSize
    	if enrollment > 0 {
	        schoolsInYear = append(schoolsInYear, SchoolYear {
	          EnrollmentSize: enrollment,
	          School: school,
	        })
        }
      }

      schoolsByYear = append(schoolsByYear, SchoolsByYear{
        Year: year,
        Schools: schoolsInYear,
      })
    }

    return render(res, schoolsByYear)

  })

  //TODO Possible clean-up opportunities here, but it's looking pretty decent
  m.Get("/school/:id", func(res http.ResponseWriter, params martini.Params) string {
    var school = School{
      //FIXME hardcoded data
      Latitude : 41.31027811,
      Longitude : -96.146874,
    }
    schoolId := params["id"]
    db.Where("id = ?", schoolId).First(&school)

    var classStats = []ClassStat{}
    db.Where("school_id = ?", params["id"]).Find(&classStats)

    var yearsToEnrollments = map[string][]GradeEnrollment{}
    for _, row := range classStats {
      if row.EnrollmentSize > 0 {
        yearsToEnrollments[row.Years] = append(yearsToEnrollments[row.Years], GradeEnrollment{
          Grade: row.Grade,
          EnrollmentSize:    row.EnrollmentSize,
        })
      }
    }

    var schoolStats = []SchoolStat{}
    db.Where("school_id = ? and teacher_size is not null", params["id"]).Find(&schoolStats)

    var yearsToTotalStats = map[string]SchoolStat{}
    for _, row := range schoolStats {
    	yearsToTotalStats[row.Years] = row
    }

    var enrollmentData = []EnrollmentByYear{}
    //Using hard coded years to make sure they're returned in order:
    for _, year := range years {

	enrollment := yearsToEnrollments[year]

      //calculate Students field
      //Could pull from SchoolStat, but will want to change
      //query above and deal properly with teacher_size being null
      var classSize int64 = 0
      for _, i := range enrollment { classSize += i.EnrollmentSize }

	  teachersAsFloat, _ := strconv.ParseFloat(yearsToTotalStats[year].TeacherSize, 64)
      enrollmentData = append(enrollmentData, EnrollmentByYear{
        Year: year,
        GradeEnrollment: enrollment,
        Students: classSize,
        Teachers: int64(teachersAsFloat),
      })
    }

    //put it all together
    var allData = SchoolWithEnrollment {
      School: school,
      EnrollmentByYear: enrollmentData,
    }

    return render(res, allData)
  })

  m.Run()
}

func render(res http.ResponseWriter, data interface{}) string {
  thing, err := json.Marshal(data)
  if err != nil { panic(err) }
  return asJson(res, thing)
}

func asJson(res http.ResponseWriter, data []byte) string {
  res.Header().Set("Content-Type", "application/json")
  res.Header().Set("Access-Control-Allow-Origin", "*")
  return string(data[:])
}
<|endoftext|>


<commit_before>package rex

import (
	"flag"
	"fmt"
	"net/http"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/goanywhere/env"
	"github.com/gorilla/mux"
)

var (
	debug    bool
	port     int
	maxprocs int

	once sync.Once
)

type server struct {
	middleware *middleware
	mux        *mux.Router
	ready      bool
	subservers []*server
}

func New() *server {
	self := &server{
		middleware: new(middleware),
		mux:        mux.NewRouter().StrictSlash(true),
	}
	self.configure()
	return self
}

func (self *server) configure() {
	once.Do(func() {
		flag.BoolVar(&debug, "debug", env.Bool("DEBUG", true), "flag to toggle debug mode")
		flag.IntVar(&port, "port", env.Int("PORT", 5000), "port to run the application server")
		flag.IntVar(&maxprocs, "maxprocs", env.Int("MAXPROCS", runtime.NumCPU()), "maximum cpu processes to run the server")
		flag.Parse()
	})
}

// build constructs all server/subservers along with their middleware modules chain.
func (self *server) build() http.Handler {
	if !self.ready {
		// * add server mux into middlware stack to serve as final http.Handler.
		self.Use(func(http.Handler) http.Handler {
			return self.mux
		})
		// * add subservers into middlware stack to serve as final http.Handler.
		for index := 0; index < len(self.subservers); index++ {
			server := self.subservers[index]
			server.Use(func(http.Handler) http.Handler {
				return server.mux
			})
		}
		self.ready = true
	}
	return self.middleware
}

// register adds the http.Handler/http.HandleFunc into Gorilla mux.
func (self *server) register(pattern string, handler interface{}, methods ...string) {
	var name = strings.Join(methods, "|") + ":" + pattern
	// finds the full function name (with package) as its mappings.
	//var name = runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name()

	switch H := handler.(type) {
	case http.Handler:
		self.mux.Handle(pattern, H).Methods(methods...).Name(name)

	case func(http.ResponseWriter, *http.Request):
		self.mux.HandleFunc(pattern, H).Methods(methods...).Name(name)

	default:
		panic("Unsupported handler: " + name)
	}
}

// Any maps most common HTTP methods request to the given `http.Handler`.
// Supports: GET | POST | PUT | DELETE | OPTIONS | HEAD
func (self *server) Any(pattern string, handler interface{}) {
	self.register(pattern, handler, "GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD")
}

// Group creates a new application group under the given path prefix.
func (self *server) Group(prefix string) *server {
	var middleware = new(middleware)
	self.mux.PathPrefix(prefix).Handler(middleware)
	var mux = self.mux.PathPrefix(prefix).Subrouter()

	server := &server{middleware: middleware, mux: mux}
	self.subservers = append(self.subservers, server)
	return server
}

// Name returns route name for the given request, if any.
func (self *server) Name(r *http.Request) (name string) {
	var match mux.RouteMatch
	if self.mux.Match(r, &match) {
		name = match.Route.GetName()
	}
	return name
}

// FileServer registers a handler to serve HTTP (GET|HEAD) requests
// with the contents of file system under the given directory.
func (self *server) FileServer(prefix, dir string) {
	if abs, err := filepath.Abs(dir); err == nil {
		fs := http.StripPrefix(prefix, http.FileServer(http.Dir(abs)))
		self.mux.PathPrefix(prefix).Handler(fs)
	} else {
		panic("Failed to setup file server: " + err.Error())
	}
}

// Use add the middleware module into the stack chain.
func (self *server) Use(module func(http.Handler) http.Handler) {
	self.middleware.stack = append(self.middleware.stack, module)
}

// Get is a shortcut for mux.HandleFunc(pattern, handler).Methods("GET"),
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Get(pattern string, handler interface{}) {
	self.register(pattern, handler, "GET")
}

// Head is a shortcut for mux.HandleFunc(pattern, handler).Methods("HEAD")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Head(pattern string, handler interface{}) {
	self.register(pattern, handler, "HEAD")
}

// Options is a shortcut for mux.HandleFunc(pattern, handler).Methods("OPTIONS")
// it also fetch the full function name of the handler (with package) to name the route.
// NOTE method OPTIONS is **NOT** cachable, beware of what you are going to do.
func (self *server) Options(pattern string, handler interface{}) {
	self.register(pattern, handler, "OPTIONS")
}

// POST is a shortcut for mux.HandleFunc(pattern, handler).Methods("POST")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Post(pattern string, handler interface{}) {
	self.register(pattern, handler, "POST")
}

// Put is a shortcut for mux.HandleFunc(pattern, handler).Methods("PUT")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Put(pattern string, handler interface{}) {
	self.register(pattern, handler, "PUT")
}

// Delete is a shortcut for mux.HandleFunc(pattern, handler).Methods("DELETE")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Delete(pattern string, handler interface{}) {
	self.register(pattern, handler, "DELETE")
}

// Trace is a shortcut for mux.HandleFunc(pattern, handler).Methods("TRACE")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Trace(pattern string, handler interface{}) {
	self.register(pattern, handler, "TRACE")
}

// Connect is a shortcut for mux.HandleFunc(pattern, handler).Methods("CONNECT")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Connect(pattern string, handler interface{}) {
	self.register(pattern, handler, "CONNECT")
}

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (self *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.build().ServeHTTP(w, r)
}

// Run starts the application server to serve incoming requests at the given address.
func (self *server) Run() {
	runtime.GOMAXPROCS(maxprocs)

	go func() {
		time.Sleep(500 * time.Millisecond)
		log.Infof("Application server is listening at %d", port)
	}()

	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), self); err != nil {
		log.Fatalf("Failed to start the server: %v", err)
	}
}

// Vars returns the route variables for the current request, if any.
func (self *server) Vars(r *http.Request) map[string]string {
	return mux.Vars(r)
}
<commit_msg>add modules supports<commit_after>package rex

import (
	"flag"
	"fmt"
	"net/http"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/goanywhere/env"
	"github.com/gorilla/mux"
)

var (
	debug    bool
	port     int
	maxprocs int

	once sync.Once
)

type server struct {
	middleware *middleware
	mux        *mux.Router
	ready      bool
	subservers []*server
}

func New() *server {
	self := &server{
		middleware: new(middleware),
		mux:        mux.NewRouter().StrictSlash(true),
	}
	self.configure()
	return self
}

func (self *server) configure() {
	once.Do(func() {
		flag.BoolVar(&debug, "debug", env.Bool("DEBUG", true), "flag to toggle debug mode")
		flag.IntVar(&port, "port", env.Int("PORT", 5000), "port to run the application server")
		flag.IntVar(&maxprocs, "maxprocs", env.Int("MAXPROCS", runtime.NumCPU()), "maximum cpu processes to run the server")
		flag.Parse()
	})
}

// build constructs all server/subservers along with their middleware modules chain.
func (self *server) build() http.Handler {
	if !self.ready {
		// * add server mux into middlware stack to serve as final http.Handler.
		self.Use(func(http.Handler) http.Handler {
			return self.mux
		})
		// * add subservers into middlware stack to serve as final http.Handler.
		for index := 0; index < len(self.subservers); index++ {
			server := self.subservers[index]
			server.Use(func(http.Handler) http.Handler {
				return server.mux
			})
		}
		self.ready = true
	}
	return self.middleware
}

// register adds the http.Handler/http.HandleFunc into Gorilla mux.
func (self *server) register(pattern string, handler interface{}, methods ...string) {
	var name = strings.Join(methods, "|") + ":" + pattern
	// finds the full function name (with package) as its mappings.
	//var name = runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name()

	switch H := handler.(type) {
	case http.Handler:
		self.mux.Handle(pattern, H).Methods(methods...).Name(name)

	case func(http.ResponseWriter, *http.Request):
		self.mux.HandleFunc(pattern, H).Methods(methods...).Name(name)

	default:
		panic("Unsupported handler: " + name)
	}
}

// Any maps most common HTTP methods request to the given `http.Handler`.
// Supports: GET | POST | PUT | DELETE | OPTIONS | HEAD
func (self *server) Any(pattern string, handler interface{}) {
	self.register(pattern, handler, "GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD")
}

// Group creates a new application group under the given path prefix.
func (self *server) Group(prefix string) *server {
	var middleware = new(middleware)
	self.mux.PathPrefix(prefix).Handler(middleware)
	var mux = self.mux.PathPrefix(prefix).Subrouter()

	server := &server{middleware: middleware, mux: mux}
	self.subservers = append(self.subservers, server)
	return server
}

// Name returns route name for the given request, if any.
func (self *server) Name(r *http.Request) (name string) {
	var match mux.RouteMatch
	if self.mux.Match(r, &match) {
		name = match.Route.GetName()
	}
	return name
}

// FileServer registers a handler to serve HTTP (GET|HEAD) requests
// with the contents of file system under the given directory.
func (self *server) FileServer(prefix, dir string) {
	if abs, err := filepath.Abs(dir); err == nil {
		fs := http.StripPrefix(prefix, http.FileServer(http.Dir(abs)))
		self.mux.PathPrefix(prefix).Handler(fs)
	} else {
		panic("Failed to setup file server: " + err.Error())
	}
}

// Use add the middleware module into the stack chain.
func (self *server) Use(module ...func(http.Handler) http.Handler) {
	self.middleware.stack = append(self.middleware.stack, module...)
}

// Get is a shortcut for mux.HandleFunc(pattern, handler).Methods("GET"),
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Get(pattern string, handler interface{}) {
	self.register(pattern, handler, "GET")
}

// Head is a shortcut for mux.HandleFunc(pattern, handler).Methods("HEAD")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Head(pattern string, handler interface{}) {
	self.register(pattern, handler, "HEAD")
}

// Options is a shortcut for mux.HandleFunc(pattern, handler).Methods("OPTIONS")
// it also fetch the full function name of the handler (with package) to name the route.
// NOTE method OPTIONS is **NOT** cachable, beware of what you are going to do.
func (self *server) Options(pattern string, handler interface{}) {
	self.register(pattern, handler, "OPTIONS")
}

// POST is a shortcut for mux.HandleFunc(pattern, handler).Methods("POST")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Post(pattern string, handler interface{}) {
	self.register(pattern, handler, "POST")
}

// Put is a shortcut for mux.HandleFunc(pattern, handler).Methods("PUT")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Put(pattern string, handler interface{}) {
	self.register(pattern, handler, "PUT")
}

// Delete is a shortcut for mux.HandleFunc(pattern, handler).Methods("DELETE")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Delete(pattern string, handler interface{}) {
	self.register(pattern, handler, "DELETE")
}

// Trace is a shortcut for mux.HandleFunc(pattern, handler).Methods("TRACE")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Trace(pattern string, handler interface{}) {
	self.register(pattern, handler, "TRACE")
}

// Connect is a shortcut for mux.HandleFunc(pattern, handler).Methods("CONNECT")
// it also fetch the full function name of the handler (with package) to name the route.
func (self *server) Connect(pattern string, handler interface{}) {
	self.register(pattern, handler, "CONNECT")
}

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (self *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.build().ServeHTTP(w, r)
}

// Run starts the application server to serve incoming requests at the given address.
func (self *server) Run() {
	runtime.GOMAXPROCS(maxprocs)

	go func() {
		time.Sleep(500 * time.Millisecond)
		log.Infof("Application server is listening at %d", port)
	}()

	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), self); err != nil {
		log.Fatalf("Failed to start the server: %v", err)
	}
}

// Vars returns the route variables for the current request, if any.
func (self *server) Vars(r *http.Request) map[string]string {
	return mux.Vars(r)
}
<|endoftext|>


<commit_before>// Copyright 2015 The httpserver Authors. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in the
// LICENSE file.
package httpserver

import (
	"fmt"
	"net/http"
	"text/template"

	"github.com/gorilla/mux"
)

const (
	SkipCheckHttpMethod = ""
	GetMethod           = "GET"
	PutMethod           = "PUT"
	PostMethod          = "POST"
	DeleteMethod        = "DELETE"
)

type HttpServer struct {
	port             string
	address          string
	errTemplate      *template.Template
	notFoundTemplate *template.Template
	Router           HttpRouter
}

type HttpRouter interface {
	HandleFunc(string, http.HandlerFunc)
	Handle(string, http.Handler) *mux.Route
	ServeHTTP(http.ResponseWriter, *http.Request)
	NotFoundHandler() http.Handler
}

func NewHttpServer(router HttpRouter, a string, p string) *HttpServer {
	s := &HttpServer{Router: router, address: a, port: p}
	return s
}

func (s *HttpServer) SetErrTemplate(t *template.Template) {
	s.errTemplate = t
}

func (s *HttpServer) SetNotFoundTemplate(t *template.Template) {
	s.notFoundTemplate = t
}

func (s *HttpServer) errorHandler(route *Route) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if recoverErr := recover(); recoverErr != nil {
				error := NewError(fmt.Sprintf("\"%v\"", recoverErr))
				w.WriteHeader(500)
				if s.errTemplate != nil {
					s.errTemplate.Execute(w, error)
				} else {
					fmt.Fprintf(w, fmt.Sprintf("\"%v\"", recoverErr))
				}
			}
		}()
		if route.HasIncorrectHttpMethod(r.Method) {
			http.Error(w, "Method not allowed", 405)
			return
		}
		route.HandleFunc(w, r)
	}
}
func (s *HttpServer) NotFound(w http.ResponseWriter, r *http.Request) {

	w.WriteHeader(404)
	if s.notFoundTemplate != nil {
		s.notFoundTemplate.Execute(w, nil)
	} else {
		fmt.Fprintf(w, "Not found")
	}

}

func (s *HttpServer) DeployAtBase(h RouteHandler) {
	s.Deploy("", h)
}

func (s *HttpServer) Deploy(context string, h RouteHandler) {
	routes := h.GetRoutes()
	for _, r := range routes {
		s.Router.HandleFunc(fmt.Sprintf("%s/%s", context, r.Path), s.errorHandler(r))
	}
}

func (s *HttpServer) Start() error {
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	http.Handle("/", s.Router)
	//s.Router.NotFoundHandler = http.HandlerFunc(s.NotFound)
	return http.ListenAndServe(fmt.Sprintf("%s:%s", s.address, s.port), nil)
}
<commit_msg>bugfix<commit_after>// Copyright 2015 The httpserver Authors. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in the
// LICENSE file.
package httpserver

import (
	"fmt"
	"net/http"
	"text/template"

	"github.com/gorilla/mux"
)

const (
	SkipCheckHttpMethod = ""
	GetMethod           = "GET"
	PutMethod           = "PUT"
	PostMethod          = "POST"
	DeleteMethod        = "DELETE"
)

type HttpServer struct {
	port             string
	address          string
	errTemplate      *template.Template
	notFoundTemplate *template.Template
	Router           HttpRouter
}

type HttpRouter interface {
	HandleFunc(string, http.HandlerFunc) *mux.Route
	Handle(string, http.Handler) *mux.Route
	ServeHTTP(http.ResponseWriter, *http.Request)
	NotFoundHandler() http.Handler
}

func NewHttpServer(router HttpRouter, a string, p string) *HttpServer {
	s := &HttpServer{Router: router, address: a, port: p}
	return s
}

func (s *HttpServer) SetErrTemplate(t *template.Template) {
	s.errTemplate = t
}

func (s *HttpServer) SetNotFoundTemplate(t *template.Template) {
	s.notFoundTemplate = t
}

func (s *HttpServer) errorHandler(route *Route) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if recoverErr := recover(); recoverErr != nil {
				error := NewError(fmt.Sprintf("\"%v\"", recoverErr))
				w.WriteHeader(500)
				if s.errTemplate != nil {
					s.errTemplate.Execute(w, error)
				} else {
					fmt.Fprintf(w, fmt.Sprintf("\"%v\"", recoverErr))
				}
			}
		}()
		if route.HasIncorrectHttpMethod(r.Method) {
			http.Error(w, "Method not allowed", 405)
			return
		}
		route.HandleFunc(w, r)
	}
}
func (s *HttpServer) NotFound(w http.ResponseWriter, r *http.Request) {

	w.WriteHeader(404)
	if s.notFoundTemplate != nil {
		s.notFoundTemplate.Execute(w, nil)
	} else {
		fmt.Fprintf(w, "Not found")
	}

}

func (s *HttpServer) DeployAtBase(h RouteHandler) {
	s.Deploy("", h)
}

func (s *HttpServer) Deploy(context string, h RouteHandler) {
	routes := h.GetRoutes()
	for _, r := range routes {
		s.Router.HandleFunc(fmt.Sprintf("%s/%s", context, r.Path), s.errorHandler(r))
	}
}

func (s *HttpServer) Start() error {
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	http.Handle("/", s.Router)
	//s.Router.NotFoundHandler = http.HandlerFunc(s.NotFound)
	return http.ListenAndServe(fmt.Sprintf("%s:%s", s.address, s.port), nil)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strconv"
)

var conns = make(map[uint32]*tls.Conn)

// Server wraps a listener instance and registers default connection and message handlers with the listener.
type Server struct {
	debug    bool
	listener *Listener
}

// NewServer creates and returns a new server instance with a listener created using given parameters.
func NewServer(cert, privKey []byte, laddr string, debug bool) (*Server, error) {
	l, err := Listen(cert, privKey, laddr, debug)
	if err != nil {
		return nil, err
	}

	return &Server{
		debug:    debug,
		listener: l,
	}, nil
}

// Accept accepts connections on the internal listener and handles connections with registered onnection and message handlers.
// This function blocks and never returns, unless there is an error while accepting a new connection.
func (s *Server) Accept() error {
	return s.listener.Accept(handleMsg, handleDisconn)
}

// Stop stops a server instance gracefully, waiting for remaining data to be written on open connections.
func (s *Server) Stop() error {
	return nil
}

// handleMsg handles incoming client messages.
func handleMsg(conn *tls.Conn, session *Session, msg []byte) {
	// authenticate the session if not already done
	if session.UserID == 0 {
		userID, err := auth(conn.ConnectionState().PeerCertificates, msg)
		if err != nil {
			session.Error = fmt.Sprintf("Cannot parse client message or method mismatched: %v", err)
		}
		session.UserID = userID
		conns[userID] = conn
		return
	}
}

// auth handles Google+ sign-in and client certificate authentication.
func auth(peerCerts []*x509.Certificate, msg []byte) (userID uint32, err error) {
	// client certificate authorization: certificate is verified by the TLS listener instance so we trust it
	if len(peerCerts) > 0 {
		idstr := peerCerts[0].Subject.CommonName
		uid64, err := strconv.ParseUint(idstr, 10, 32)
		if err != nil {
			return 0, err
		}
		userID = uint32(uid64)
		log.Printf("Client connected with client certificate subject: %+v", peerCerts[0].Subject)
		return userID, nil
	}

	// Google+ authentication
	var req ReqMsg
	err = json.Unmarshal(msg, &req)
	if err != nil {
		return
	}
	switch req.Method {
	case "auth.token":
		// http://attilaolah.eu/2013/11/29/json-decoding-in-go/
		return
	case "auth.google":
		// todo: ping google, get user info, save user info in DB, generate and return permanent jwt token (or should this part be NBusy's business?)
		return
	default:
		return 0, errors.New("initial unauthenticated request should be in the 'auth.xxx' form")
	}
}

func handleDisconn(conn *tls.Conn, session *Session) {
	delete(conns, session.UserID)
}
<commit_msg>add token/usr conversion func<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strconv"
)

var conns = make(map[uint32]*tls.Conn)

// Server wraps a listener instance and registers default connection and message handlers with the listener.
type Server struct {
	debug    bool
	listener *Listener
}

// NewServer creates and returns a new server instance with a listener created using given parameters.
func NewServer(cert, privKey []byte, laddr string, debug bool) (*Server, error) {
	l, err := Listen(cert, privKey, laddr, debug)
	if err != nil {
		return nil, err
	}

	return &Server{
		debug:    debug,
		listener: l,
	}, nil
}

// Accept accepts connections on the internal listener and handles connections with registered onnection and message handlers.
// This function blocks and never returns, unless there is an error while accepting a new connection.
func (s *Server) Accept() error {
	return s.listener.Accept(handleMsg, handleDisconn)
}

// Stop stops a server instance gracefully, waiting for remaining data to be written on open connections.
func (s *Server) Stop() error {
	return nil
}

// handleMsg handles incoming client messages.
func handleMsg(conn *tls.Conn, session *Session, msg []byte) {
	// authenticate the session if not already done
	if session.UserID == 0 {
		userID, err := auth(conn.ConnectionState().PeerCertificates, msg)
		if err != nil {
			session.Error = fmt.Sprintf("Cannot parse client message or method mismatched: %v", err)
		}
		session.UserID = userID
		conns[userID] = conn
		return
	}
}

// auth handles Google+ sign-in and client certificate authentication.
func auth(peerCerts []*x509.Certificate, msg []byte) (userID uint32, err error) {
	// client certificate authorization: certificate is verified by the TLS listener instance so we trust it
	if len(peerCerts) > 0 {
		idstr := peerCerts[0].Subject.CommonName
		uid64, err := strconv.ParseUint(idstr, 10, 32)
		if err != nil {
			return 0, err
		}
		userID = uint32(uid64)
		log.Printf("Client connected with client certificate subject: %+v", peerCerts[0].Subject)
		return userID, nil
	}

	// Google+ authentication
	var req ReqMsg
	if err = json.Unmarshal(msg, &req); err != nil {
		return
	}

	switch req.Method {
	case "auth.token":
		var token string
		if err = json.Unmarshal(req.Params, &token); err != nil {
			return
		}
		// assume that token = user ID for testing
		uid64, err := strconv.ParseUint(token, 10, 32)
		if err != nil {
			return 0, err
		}
		userID = uint32(uid64)
		return userID, nil
	case "auth.google":
		// todo: ping google, get user info, save user info in DB, generate and return permanent jwt token (or should this part be NBusy's business?)
		return
	default:
		return 0, errors.New("initial unauthenticated request should be in the 'auth.xxx' form")
	}
}

func handleDisconn(conn *tls.Conn, session *Session) {
	delete(conns, session.UserID)
}
<|endoftext|>


<commit_before>package qmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"runtime"
	"sync"
	"time"

	"github.com/bitly/go-nsq"
)

var sMutex = &sync.Mutex{}

type Server struct {
	Name        string
	TTL         time.Duration
	RequestChan chan Request
	ResultChan  chan []byte

	Requests map[string]chan []byte

	producer *nsq.Producer
	consumer *nsq.Consumer
	DB       *DB

	queue *QueueConfig
}

func NewServer(sc *ServerConfig) (*Server, error) {
	var server Server
	var err error

	server.Name = sc.Name
	server.TTL = sc.TTL
	server.queue = sc.Queue
	server.Requests = make(map[string]chan []byte)

	producer, err := nsq.NewProducer(server.queue.HostNSQDAddr, nsq.NewConfig())
	if err != nil {
		return &server, err
	}
	server.producer = producer

	channelName := fmt.Sprintf("%s#ephemeral", server.Name)
	consumer, err := nsq.NewConsumer("result", channelName, nsq.NewConfig())
	if err != nil {
		return &server, err
	}
	server.consumer = consumer

	server.RequestChan = make(chan Request)
	server.ResultChan = make(chan []byte)
	server.DB = NewDB(sc.DBAddr)

	if err = SetupLogging(sc.Logging); err != nil {
		return &server, err
	}

	lg.Info("Server created as %s", server.Name)
	return &server, nil
}

func (s Server) Reload() error {
	doneChan := make(chan *nsq.ProducerTransaction)
	err := s.producer.PublishAsync("command", []byte("reload"), doneChan)
	<-doneChan
	return err
}

func (s Server) Queue(script string, data []byte) (<-chan []byte, error) {
	var err error

	req := Request{
		ID:        NewID(),
		Script:    script,
		Status:    StatusQUEUED,
		StartTime: time.Now(),
	}
	if err = json.Unmarshal(data, &req); err != nil {
		return nil, err
	}

	ch := make(chan []byte, 1)
	sMutex.Lock()
	s.Requests[req.ID] = ch
	sMutex.Unlock()
	runtime.Gosched()
	s.RequestChan <- req

	// Asynchronous call
	if req.CallbackURL != "" {
		newCh := make(chan []byte, 1)
		defer close(newCh)
		reply := <-ch
		newCh <- reply
		go callback(req.CallbackURL, ch)
		return newCh, nil
	}
	return ch, nil
}

func (s Server) Run() error {
	var err error

	s.consumer.AddHandler(nsq.HandlerFunc(s.resultHandler))
	if err = ConnectConsumer(s.queue, s.consumer); err != nil {
		s.Exit()
		return err
	}

	go func() {
		for req := range s.RequestChan {
			go s.processRequest(req)
		}
	}()

	go func() {
		for res := range s.ResultChan {
			go s.processResult(res)
		}
	}()

	return nil
}

func (s Server) Exit() {
	s.producer.Stop()
	s.consumer.Stop()
	defer close(s.RequestChan)
	defer close(s.ResultChan)
	for _, ch := range s.Requests {
		go close(ch)
	}
}

// Message handler(s)

func (s Server) resultHandler(m *nsq.Message) error {
	s.ResultChan <- m.Body
	return nil
}

// Helper functions

func (s Server) processRequest(r Request) {
	var err error

	data, err := r.WriteJSON()
	if err != nil {
		lg.Error(err.Error())
		return
	}

	doneChan := make(chan *nsq.ProducerTransaction)
	if err = s.producer.PublishAsync("job", data, doneChan); err != nil {
		lg.Error(err.Error())
		return
	}
	<-doneChan
	lg.Info("Request queued as %s", r.ID)
	go s.startTTL(r)

	sMutex.Lock()
	ch, exist := s.Requests[r.ID]
	sMutex.Unlock()
	runtime.Gosched()
	if exist && r.CallbackURL != "" {
		ch <- data
	}
}

func (s Server) processResult(data []byte) {
	var r Job
	if err := json.Unmarshal(data, &r); err != nil {
		lg.Error(err.Error())
		return
	}
	s.finish(r.Script, r.ID, data)
}

func (s Server) startTTL(req Request) {
	req.FinishTime = <-time.After(s.TTL)
	sMutex.Lock()
	_, exists := s.Requests[req.ID]
	sMutex.Unlock()
	runtime.Gosched()
	if !exists {
		return
	}
	req.Duration = fmt.Sprintf("%f", req.FinishTime.Sub(req.StartTime).Seconds())
	req.Status = StatusTIMEOUT

	data, err := json.Marshal(&req)
	if err != nil {
		lg.Error(err.Error())
	}
	go s.killJob(req.ID)
	doneChan := make(chan *nsq.ProducerTransaction)
	if err := s.producer.PublishAsync("result", data, doneChan); err != nil {
		lg.Error(err.Error())
	}
	<-doneChan
}

func (s Server) finish(script, id string, data []byte) {
	sMutex.Lock()
	ch, exist := s.Requests[id]
	sMutex.Unlock()
	runtime.Gosched()
	if exist {
		defer func() {
			sMutex.Lock()
			delete(s.Requests, id)
			sMutex.Unlock()
			runtime.Gosched()
		}()
		defer close(ch)
		ch <- data
		s.DB.SetLog(script, id, string(data))
	}
}

func (s Server) killJob(id string) {
	doneChan := make(chan *nsq.ProducerTransaction)
	cmd := fmt.Sprintf("kill:%s", id)
	if err := s.producer.PublishAsync("command", []byte(cmd), doneChan); err != nil {
		lg.Error(err.Error())
	}
	<-doneChan
	lg.Info("Sent kill request for %s", id)
}

func callback(url string, ch chan []byte) {
	buf := bytes.NewBuffer(<-ch)
	_, err := http.Post(url, "application/json", buf)
	if err != nil {
		lg.Error(err.Error())
	}
}
<commit_msg>Removed TTL for asynchronous calls<commit_after>package qmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"runtime"
	"sync"
	"time"

	"github.com/bitly/go-nsq"
)

var sMutex = &sync.Mutex{}

type Server struct {
	Name        string
	TTL         time.Duration
	RequestChan chan Request
	ResultChan  chan []byte

	Requests map[string]chan []byte

	producer *nsq.Producer
	consumer *nsq.Consumer
	DB       *DB

	queue *QueueConfig
}

func NewServer(sc *ServerConfig) (*Server, error) {
	var server Server
	var err error

	server.Name = sc.Name
	server.TTL = sc.TTL
	server.queue = sc.Queue
	server.Requests = make(map[string]chan []byte)

	producer, err := nsq.NewProducer(server.queue.HostNSQDAddr, nsq.NewConfig())
	if err != nil {
		return &server, err
	}
	server.producer = producer

	channelName := fmt.Sprintf("%s#ephemeral", server.Name)
	consumer, err := nsq.NewConsumer("result", channelName, nsq.NewConfig())
	if err != nil {
		return &server, err
	}
	server.consumer = consumer

	server.RequestChan = make(chan Request)
	server.ResultChan = make(chan []byte)
	server.DB = NewDB(sc.DBAddr)

	if err = SetupLogging(sc.Logging); err != nil {
		return &server, err
	}

	lg.Info("Server created as %s", server.Name)
	return &server, nil
}

func (s Server) Reload() error {
	doneChan := make(chan *nsq.ProducerTransaction)
	err := s.producer.PublishAsync("command", []byte("reload"), doneChan)
	<-doneChan
	return err
}

func (s Server) Queue(script string, data []byte) (<-chan []byte, error) {
	var err error

	req := Request{
		ID:        NewID(),
		Script:    script,
		Status:    StatusQUEUED,
		StartTime: time.Now(),
	}
	if err = json.Unmarshal(data, &req); err != nil {
		return nil, err
	}

	ch := make(chan []byte, 1)
	sMutex.Lock()
	s.Requests[req.ID] = ch
	sMutex.Unlock()
	runtime.Gosched()
	s.RequestChan <- req

	// Synchronous call
	if req.CallbackURL != "" {
		newCh := make(chan []byte, 1)
		defer close(newCh)
		reply := <-ch
		newCh <- reply
		go callback(req.CallbackURL, ch)
		return newCh, nil
	}
	return ch, nil
}

func (s Server) Run() error {
	var err error

	s.consumer.AddHandler(nsq.HandlerFunc(s.resultHandler))
	if err = ConnectConsumer(s.queue, s.consumer); err != nil {
		s.Exit()
		return err
	}

	go func() {
		for req := range s.RequestChan {
			go s.processRequest(req)
		}
	}()

	go func() {
		for res := range s.ResultChan {
			go s.processResult(res)
		}
	}()

	return nil
}

func (s Server) Exit() {
	s.producer.Stop()
	s.consumer.Stop()
	defer close(s.RequestChan)
	defer close(s.ResultChan)
	for _, ch := range s.Requests {
		go close(ch)
	}
}

// Message handler(s)

func (s Server) resultHandler(m *nsq.Message) error {
	s.ResultChan <- m.Body
	return nil
}

// Helper functions

func (s Server) processRequest(r Request) {
	var err error

	data, err := r.WriteJSON()
	if err != nil {
		lg.Error(err.Error())
		return
	}

	doneChan := make(chan *nsq.ProducerTransaction)
	if err = s.producer.PublishAsync("job", data, doneChan); err != nil {
		lg.Error(err.Error())
		return
	}
	<-doneChan
	lg.Info("Request queued as %s", r.ID)
	if r.CallbackURL == "" { // Synchronous Call
		go s.startTTL(r)
	}

	sMutex.Lock()
	ch, exist := s.Requests[r.ID]
	sMutex.Unlock()
	runtime.Gosched()
	if exist && r.CallbackURL != "" {
		ch <- data
	}
}

func (s Server) processResult(data []byte) {
	var r Job
	if err := json.Unmarshal(data, &r); err != nil {
		lg.Error(err.Error())
		return
	}
	s.finish(r.Script, r.ID, data)
}

func (s Server) startTTL(req Request) {
	req.FinishTime = <-time.After(s.TTL)
	sMutex.Lock()
	_, exists := s.Requests[req.ID]
	sMutex.Unlock()
	runtime.Gosched()
	if !exists {
		return
	}
	req.Duration = fmt.Sprintf("%f", req.FinishTime.Sub(req.StartTime).Seconds())
	req.Status = StatusTIMEOUT

	data, err := json.Marshal(&req)
	if err != nil {
		lg.Error(err.Error())
	}
	go s.killJob(req.ID)
	doneChan := make(chan *nsq.ProducerTransaction)
	if err := s.producer.PublishAsync("result", data, doneChan); err != nil {
		lg.Error(err.Error())
	}
	<-doneChan
}

func (s Server) finish(script, id string, data []byte) {
	sMutex.Lock()
	ch, exist := s.Requests[id]
	sMutex.Unlock()
	runtime.Gosched()
	if exist {
		defer func() {
			sMutex.Lock()
			delete(s.Requests, id)
			sMutex.Unlock()
			runtime.Gosched()
		}()
		defer close(ch)
		ch <- data
		s.DB.SetLog(script, id, string(data))
	}
}

func (s Server) killJob(id string) {
	doneChan := make(chan *nsq.ProducerTransaction)
	cmd := fmt.Sprintf("kill:%s", id)
	if err := s.producer.PublishAsync("command", []byte(cmd), doneChan); err != nil {
		lg.Error(err.Error())
	}
	<-doneChan
	lg.Info("Sent kill request for %s", id)
}

func callback(url string, ch chan []byte) {
	buf := bytes.NewBuffer(<-ch)
	_, err := http.Post(url, "application/json", buf)
	if err != nil {
		lg.Error(err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"log"
	"os"
	"strings"
	"time"

	"github.com/intervention-engine/fhir/server"
	"github.com/intervention-engine/ie/controllers"
	"github.com/intervention-engine/ie/huddles"
	"github.com/intervention-engine/ie/utilities"
	"github.com/robfig/cron"
	"github.com/gin-gonic/gin"
)

// Setup the huddle flag to accumulate huddle config file paths
type huddleSlice []string

func (h *huddleSlice) String() string {
	return fmt.Sprint(*h)
}
func (h *huddleSlice) Set(value string) error {
	*h = append(*h, value)
	return nil
}

var huddleFlag huddleSlice

func init() {
	flag.Var(&huddleFlag, "huddle", "path to a huddle configuration file (repeat flag for multiple paths)")
}

func main() {
	codeLookupFlag := flag.Bool("loadCodes", false, "flag to enable download of icd-9 and icd-10 code lookup")
	icd9URL := flag.String("icd9URL", "https://www.cms.gov/Medicare/Coding/ICD9ProviderDiagnosticCodes/Downloads/ICD-9-CM-v32-master-descriptions.zip", "url for icd-9 code definition zip")
	icd10URL := flag.String("icd10URL", "https://www.cms.gov/Medicare/Coding/ICD10/Downloads/2016-Code-Descriptions-in-Tabular-Order.zip", "url for icd-10 code definition zip")
	subscriptionFlag := flag.Bool("subscriptions", false, "enables limited support for resource subscriptions (default: false)")
	reqLog := flag.Bool("reqlog", false, "Enables request logging -- do NOT use in production")
	flag.Parse()

	err := os.Mkdir("/etc/ielogs", 0755)
	if err != nil && !os.IsExist(err){
		fmt.Println("Error creating log directory:" + err.Error())
	}

	lf, err := os.Create("/etc/ielogs/ie.log")
	if err != nil {
		fmt.Println("Unable to create ie log file:" + err.Error())
	}
	log.SetOutput(lf)

	mongoURL := os.Getenv("MONGO_URL")
	if mongoURL == "" {
		mongoURL = "mongodb://localhost:27017"
	}

	riskServiceURL := os.Getenv("RISK_SERVICE_URL")
	if riskServiceURL == "" {
		riskServiceURL = "http://localhost:9000"
	}

	huddleConfigs := huddleFlag
	if len(huddleConfigs) == 0 && os.Getenv("HUDDLE_CONFIGS") != "" {
		huddleConfigs = huddleSlice(strings.Split(os.Getenv("HUDDLE_CONFIGS"), ","))
	}

	if *codeLookupFlag {
		utilities.LoadICDFromCMS(mongoURL, *icd9URL, "ICD-9")
		utilities.LoadICDFromCMS(mongoURL, *icd10URL, "ICD-10")
	}

	var ip net.IP
	var selfURL string
	host, err := os.Hostname()
	if err != nil {
		panic(err)
	}
	addrs, err := net.LookupIP(host)
	if err != nil {
		fmt.Println("Unable to lookup IP based on hostname, defaulting to localhost.")
		selfURL = "http://localhost:3001"
	}
	for _, addr := range addrs {
		if ipv4 := addr.To4(); ipv4 != nil {
			ip = ipv4
			selfURL = "http://" + ip.String() + ":3001"
		}
	}

	s := server.NewServer(mongoURL)

	if *reqLog {
		s.Engine.Use(server.RequestLoggerHandler)
	}

	// Since the huddle controller needs info from the command line, set it up here.  When IE is refactored
	// to take out globals (and other stuff), this should be rethought.
	c := cron.New()
	huddleController := new(huddles.HuddleSchedulerController)
	for _, huddlePath := range huddleConfigs {
		data, err := ioutil.ReadFile(huddlePath)
		if err != nil {
			panic(err)
		}
		config := new(huddles.HuddleConfig)
		if err := json.Unmarshal(data, config); err != nil {
			panic(err)
		}
		huddleController.AddConfig(config)

		// Wait 1 minute before doing initial runs or setting up cron jobs.  This allows the server to get
		// started (since it needs to initiate the db connection, etc).
		time.AfterFunc(1*time.Minute, func() {
			// Do an initial run
			fmt.Println("Initial scheduling for huddle with name ", config.Name)
			huddles.ScheduleHuddles(config)

			// Set up the cron job for future runs
			if config.SchedulerCronSpec != "" {
				err := c.AddFunc(config.SchedulerCronSpec, func() {
					_, err := huddles.ScheduleHuddles(config)
					if err != nil {
						fmt.Printf("ERROR: Could not schedule huddles for huddle with name %s: %v", config.Name, err)
					}
				})
				if err != nil {
					panic(err)
				}
				fmt.Printf("Huddle with name %s scheduled with cron spec: %s\n", config.Name, config.SchedulerCronSpec)
			} else {
				fmt.Printf("Warning: Huddle with name %s is not configured with a scheduler cron job.\n", config.Name)
			}
		})
	}
	s.Engine.GET("/ScheduleHuddles", huddleController.ScheduleHandler)

	ginLogFile, err := os.Create("/etc/ielogs/gin.log")
	if err != nil {
		fmt.Println("Unable to create gin log file." + err.Error())
	}
	s.Engine.Use(gin.LoggerWithWriter(ginLogFile))

	if len(huddleConfigs) > 0 {
		c.Start()
		defer c.Stop()
	}

	closer := controllers.RegisterRoutes(s, selfURL, riskServiceURL, *subscriptionFlag)
	defer closer()

	s.Run(server.DefaultConfig)
}
<commit_msg>dont try to set log files if there were errors creating them<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"log"
	"os"
	"strings"
	"time"

	"github.com/intervention-engine/fhir/server"
	"github.com/intervention-engine/ie/controllers"
	"github.com/intervention-engine/ie/huddles"
	"github.com/intervention-engine/ie/utilities"
	"github.com/robfig/cron"
	"github.com/gin-gonic/gin"
)

// Setup the huddle flag to accumulate huddle config file paths
type huddleSlice []string

func (h *huddleSlice) String() string {
	return fmt.Sprint(*h)
}
func (h *huddleSlice) Set(value string) error {
	*h = append(*h, value)
	return nil
}

var huddleFlag huddleSlice

func init() {
	flag.Var(&huddleFlag, "huddle", "path to a huddle configuration file (repeat flag for multiple paths)")
}

func main() {
	codeLookupFlag := flag.Bool("loadCodes", false, "flag to enable download of icd-9 and icd-10 code lookup")
	icd9URL := flag.String("icd9URL", "https://www.cms.gov/Medicare/Coding/ICD9ProviderDiagnosticCodes/Downloads/ICD-9-CM-v32-master-descriptions.zip", "url for icd-9 code definition zip")
	icd10URL := flag.String("icd10URL", "https://www.cms.gov/Medicare/Coding/ICD10/Downloads/2016-Code-Descriptions-in-Tabular-Order.zip", "url for icd-10 code definition zip")
	subscriptionFlag := flag.Bool("subscriptions", false, "enables limited support for resource subscriptions (default: false)")
	reqLog := flag.Bool("reqlog", false, "Enables request logging -- do NOT use in production")
	flag.Parse()

	err := os.Mkdir("/etc/ielogs", 0755)
	if err != nil && !os.IsExist(err){
		fmt.Println("Error creating log directory:" + err.Error())
	}

	lf, err := os.Create("/etc/ielogs/ie.log")
	if err != nil {
		fmt.Println("Unable to create ie log file:" + err.Error())
	} else { log.SetOutput(lf) }

	mongoURL := os.Getenv("MONGO_URL")
	if mongoURL == "" {
		mongoURL = "mongodb://localhost:27017"
	}

	riskServiceURL := os.Getenv("RISK_SERVICE_URL")
	if riskServiceURL == "" {
		riskServiceURL = "http://localhost:9000"
	}

	huddleConfigs := huddleFlag
	if len(huddleConfigs) == 0 && os.Getenv("HUDDLE_CONFIGS") != "" {
		huddleConfigs = huddleSlice(strings.Split(os.Getenv("HUDDLE_CONFIGS"), ","))
	}

	if *codeLookupFlag {
		utilities.LoadICDFromCMS(mongoURL, *icd9URL, "ICD-9")
		utilities.LoadICDFromCMS(mongoURL, *icd10URL, "ICD-10")
	}

	var ip net.IP
	var selfURL string
	host, err := os.Hostname()
	if err != nil {
		panic(err)
	}
	addrs, err := net.LookupIP(host)
	if err != nil {
		fmt.Println("Unable to lookup IP based on hostname, defaulting to localhost.")
		selfURL = "http://localhost:3001"
	}
	for _, addr := range addrs {
		if ipv4 := addr.To4(); ipv4 != nil {
			ip = ipv4
			selfURL = "http://" + ip.String() + ":3001"
		}
	}

	s := server.NewServer(mongoURL)

	if *reqLog {
		s.Engine.Use(server.RequestLoggerHandler)
	}

	// Since the huddle controller needs info from the command line, set it up here.  When IE is refactored
	// to take out globals (and other stuff), this should be rethought.
	c := cron.New()
	huddleController := new(huddles.HuddleSchedulerController)
	for _, huddlePath := range huddleConfigs {
		data, err := ioutil.ReadFile(huddlePath)
		if err != nil {
			panic(err)
		}
		config := new(huddles.HuddleConfig)
		if err := json.Unmarshal(data, config); err != nil {
			panic(err)
		}
		huddleController.AddConfig(config)

		// Wait 1 minute before doing initial runs or setting up cron jobs.  This allows the server to get
		// started (since it needs to initiate the db connection, etc).
		time.AfterFunc(1*time.Minute, func() {
			// Do an initial run
			fmt.Println("Initial scheduling for huddle with name ", config.Name)
			huddles.ScheduleHuddles(config)

			// Set up the cron job for future runs
			if config.SchedulerCronSpec != "" {
				err := c.AddFunc(config.SchedulerCronSpec, func() {
					_, err := huddles.ScheduleHuddles(config)
					if err != nil {
						fmt.Printf("ERROR: Could not schedule huddles for huddle with name %s: %v", config.Name, err)
					}
				})
				if err != nil {
					panic(err)
				}
				fmt.Printf("Huddle with name %s scheduled with cron spec: %s\n", config.Name, config.SchedulerCronSpec)
			} else {
				fmt.Printf("Warning: Huddle with name %s is not configured with a scheduler cron job.\n", config.Name)
			}
		})
	}
	s.Engine.GET("/ScheduleHuddles", huddleController.ScheduleHandler)

	ginLogFile, err := os.Create("/etc/ielogs/gin.log")
	if err != nil {
		fmt.Println("Unable to create gin log file." + err.Error())
	} else { s.Engine.Use(gin.LoggerWithWriter(ginLogFile)) }

	if len(huddleConfigs) > 0 {
		c.Start()
		defer c.Stop()
	}

	closer := controllers.RegisterRoutes(s, selfURL, riskServiceURL, *subscriptionFlag)
	defer closer()

	s.Run(server.DefaultConfig)
}
<|endoftext|>