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

// Package sort provides primitives for sorting slices and user-defined
// collections.
package sort

import "math"

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package.  The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int
	// Less returns whether the element with index i should sort
	// before the element with index j.
	Less(i, j int) bool
	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// Insertion sort
func insertionSort(data Interface, a, b int) {
	for i := a + 1; i < b; i++ {
		for j := i; j > a && data.Less(j, j-1); j-- {
			data.Swap(j, j-1)
		}
	}
}

// siftDown implements the heap property on data[lo, hi).
// first is an offset into the array where the root of the heap lies.
func siftDown(data Interface, lo, hi, first int) {
	root := lo
	for {
		child := 2*root + 1
		if child >= hi {
			break
		}
		if child+1 < hi && data.Less(first+child, first+child+1) {
			child++
		}
		if !data.Less(first+root, first+child) {
			return
		}
		data.Swap(first+root, first+child)
		root = child
	}
}

func heapSort(data Interface, a, b int) {
	first := a
	lo := 0
	hi := b - a

	// Build heap with greatest element at top.
	for i := (hi - 1) / 2; i >= 0; i-- {
		siftDown(data, i, hi, first)
	}

	// Pop elements, largest first, into end of data.
	for i := hi - 1; i >= 0; i-- {
		data.Swap(first, first+i)
		siftDown(data, lo, i, first)
	}
}

// Quicksort, following Bentley and McIlroy,
// ``Engineering a Sort Function,'' SP&E November 1993.

// medianOfThree moves the median of the three values data[a], data[b], data[c] into data[a].
func medianOfThree(data Interface, a, b, c int) {
	m0 := b
	m1 := a
	m2 := c
	// bubble sort on 3 elements
	if data.Less(m1, m0) {
		data.Swap(m1, m0)
	}
	if data.Less(m2, m1) {
		data.Swap(m2, m1)
	}
	if data.Less(m1, m0) {
		data.Swap(m1, m0)
	}
	// now data[m0] <= data[m1] <= data[m2]
}

func swapRange(data Interface, a, b, n int) {
	for i := 0; i < n; i++ {
		data.Swap(a+i, b+i)
	}
}

func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
	m := lo + (hi-lo)/2 // Written like this to avoid integer overflow.
	if hi-lo > 40 {
		// Tukey's ``Ninther,'' median of three medians of three.
		s := (hi - lo) / 8
		medianOfThree(data, lo, lo+s, lo+2*s)
		medianOfThree(data, m, m-s, m+s)
		medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
	}
	medianOfThree(data, lo, m, hi-1)

	// Invariants are:
	//	data[lo] = pivot (set up by ChoosePivot)
	//	data[lo <= i < a] = pivot
	//	data[a <= i < b] < pivot
	//	data[b <= i < c] is unexamined
	//	data[c <= i < d] > pivot
	//	data[d <= i < hi] = pivot
	//
	// Once b meets c, can swap the "= pivot" sections
	// into the middle of the slice.
	pivot := lo
	a, b, c, d := lo+1, lo+1, hi, hi
	for b < c {
		if data.Less(b, pivot) { // data[b] < pivot
			b++
			continue
		}
		if !data.Less(pivot, b) { // data[b] = pivot
			data.Swap(a, b)
			a++
			b++
			continue
		}
		if data.Less(pivot, c-1) { // data[c-1] > pivot
			c--
			continue
		}
		if !data.Less(c-1, pivot) { // data[c-1] = pivot
			data.Swap(c-1, d-1)
			c--
			d--
			continue
		}
		// data[b] > pivot; data[c-1] < pivot
		data.Swap(b, c-1)
		b++
		c--
	}

	n := min(b-a, a-lo)
	swapRange(data, lo, b-n, n)

	n = min(hi-d, d-c)
	swapRange(data, c, hi-n, n)

	return lo + b - a, hi - (d - c)
}

func quickSort(data Interface, a, b, maxDepth int) {
	for b-a > 7 {
		if maxDepth == 0 {
			heapSort(data, a, b)
			return
		}
		maxDepth--
		mlo, mhi := doPivot(data, a, b)
		// Avoiding recursion on the larger subproblem guarantees
		// a stack depth of at most lg(b-a).
		if mlo-a < b-mhi {
			quickSort(data, a, mlo, maxDepth)
			a = mhi // i.e., quickSort(data, mhi, b)
		} else {
			quickSort(data, mhi, b, maxDepth)
			b = mlo // i.e., quickSort(data, a, mlo)
		}
	}
	if b-a > 1 {
		insertionSort(data, a, b)
	}
}

func Sort(data Interface) {
	// Switch to heapsort if depth of 2*ceil(lg(n)) is reached.
	n := data.Len()
	maxDepth := 0
	for 1<<uint(maxDepth) < n {
		maxDepth++
	}
	maxDepth *= 2
	quickSort(data, 0, data.Len(), maxDepth)
}

func IsSorted(data Interface) bool {
	n := data.Len()
	for i := n - 1; i > 0; i-- {
		if data.Less(i, i-1) {
			return false
		}
	}
	return true
}

// Convenience types for common cases

// IntSlice attaches the methods of Interface to []int, sorting in increasing order.
type IntSlice []int

func (p IntSlice) Len() int           { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p IntSlice) Sort() { Sort(p) }

// Float64Slice attaches the methods of Interface to []float64, sorting in increasing order.
type Float64Slice []float64

func (p Float64Slice) Len() int           { return len(p) }
func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || math.IsNaN(p[i]) && !math.IsNaN(p[j]) }
func (p Float64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p Float64Slice) Sort() { Sort(p) }

// StringSlice attaches the methods of Interface to []string, sorting in increasing order.
type StringSlice []string

func (p StringSlice) Len() int           { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p StringSlice) Sort() { Sort(p) }

// Convenience wrappers for common cases

// Ints sorts a slice of ints in increasing order.
func Ints(a []int) { Sort(IntSlice(a)) }

// Float64s sorts a slice of float64s in increasing order.
func Float64s(a []float64) { Sort(Float64Slice(a)) }

// Strings sorts a slice of strings in increasing order.
func Strings(a []string) { Sort(StringSlice(a)) }

// IntsAreSorted tests whether a slice of ints is sorted in increasing order.
func IntsAreSorted(a []int) bool { return IsSorted(IntSlice(a)) }

// Float64sAreSorted tests whether a slice of float64s is sorted in increasing order.
func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) }

// StringsAreSorted tests whether a slice of strings is sorted in increasing order.
func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) }
<commit_msg>sort: eliminate extra Len() call<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sort provides primitives for sorting slices and user-defined
// collections.
package sort

import "math"

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package.  The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int
	// Less returns whether the element with index i should sort
	// before the element with index j.
	Less(i, j int) bool
	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// Insertion sort
func insertionSort(data Interface, a, b int) {
	for i := a + 1; i < b; i++ {
		for j := i; j > a && data.Less(j, j-1); j-- {
			data.Swap(j, j-1)
		}
	}
}

// siftDown implements the heap property on data[lo, hi).
// first is an offset into the array where the root of the heap lies.
func siftDown(data Interface, lo, hi, first int) {
	root := lo
	for {
		child := 2*root + 1
		if child >= hi {
			break
		}
		if child+1 < hi && data.Less(first+child, first+child+1) {
			child++
		}
		if !data.Less(first+root, first+child) {
			return
		}
		data.Swap(first+root, first+child)
		root = child
	}
}

func heapSort(data Interface, a, b int) {
	first := a
	lo := 0
	hi := b - a

	// Build heap with greatest element at top.
	for i := (hi - 1) / 2; i >= 0; i-- {
		siftDown(data, i, hi, first)
	}

	// Pop elements, largest first, into end of data.
	for i := hi - 1; i >= 0; i-- {
		data.Swap(first, first+i)
		siftDown(data, lo, i, first)
	}
}

// Quicksort, following Bentley and McIlroy,
// ``Engineering a Sort Function,'' SP&E November 1993.

// medianOfThree moves the median of the three values data[a], data[b], data[c] into data[a].
func medianOfThree(data Interface, a, b, c int) {
	m0 := b
	m1 := a
	m2 := c
	// bubble sort on 3 elements
	if data.Less(m1, m0) {
		data.Swap(m1, m0)
	}
	if data.Less(m2, m1) {
		data.Swap(m2, m1)
	}
	if data.Less(m1, m0) {
		data.Swap(m1, m0)
	}
	// now data[m0] <= data[m1] <= data[m2]
}

func swapRange(data Interface, a, b, n int) {
	for i := 0; i < n; i++ {
		data.Swap(a+i, b+i)
	}
}

func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
	m := lo + (hi-lo)/2 // Written like this to avoid integer overflow.
	if hi-lo > 40 {
		// Tukey's ``Ninther,'' median of three medians of three.
		s := (hi - lo) / 8
		medianOfThree(data, lo, lo+s, lo+2*s)
		medianOfThree(data, m, m-s, m+s)
		medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
	}
	medianOfThree(data, lo, m, hi-1)

	// Invariants are:
	//	data[lo] = pivot (set up by ChoosePivot)
	//	data[lo <= i < a] = pivot
	//	data[a <= i < b] < pivot
	//	data[b <= i < c] is unexamined
	//	data[c <= i < d] > pivot
	//	data[d <= i < hi] = pivot
	//
	// Once b meets c, can swap the "= pivot" sections
	// into the middle of the slice.
	pivot := lo
	a, b, c, d := lo+1, lo+1, hi, hi
	for b < c {
		if data.Less(b, pivot) { // data[b] < pivot
			b++
			continue
		}
		if !data.Less(pivot, b) { // data[b] = pivot
			data.Swap(a, b)
			a++
			b++
			continue
		}
		if data.Less(pivot, c-1) { // data[c-1] > pivot
			c--
			continue
		}
		if !data.Less(c-1, pivot) { // data[c-1] = pivot
			data.Swap(c-1, d-1)
			c--
			d--
			continue
		}
		// data[b] > pivot; data[c-1] < pivot
		data.Swap(b, c-1)
		b++
		c--
	}

	n := min(b-a, a-lo)
	swapRange(data, lo, b-n, n)

	n = min(hi-d, d-c)
	swapRange(data, c, hi-n, n)

	return lo + b - a, hi - (d - c)
}

func quickSort(data Interface, a, b, maxDepth int) {
	for b-a > 7 {
		if maxDepth == 0 {
			heapSort(data, a, b)
			return
		}
		maxDepth--
		mlo, mhi := doPivot(data, a, b)
		// Avoiding recursion on the larger subproblem guarantees
		// a stack depth of at most lg(b-a).
		if mlo-a < b-mhi {
			quickSort(data, a, mlo, maxDepth)
			a = mhi // i.e., quickSort(data, mhi, b)
		} else {
			quickSort(data, mhi, b, maxDepth)
			b = mlo // i.e., quickSort(data, a, mlo)
		}
	}
	if b-a > 1 {
		insertionSort(data, a, b)
	}
}

func Sort(data Interface) {
	// Switch to heapsort if depth of 2*ceil(lg(n)) is reached.
	n := data.Len()
	maxDepth := 0
	for 1<<uint(maxDepth) < n {
		maxDepth++
	}
	maxDepth *= 2
	quickSort(data, 0, n, maxDepth)
}

func IsSorted(data Interface) bool {
	n := data.Len()
	for i := n - 1; i > 0; i-- {
		if data.Less(i, i-1) {
			return false
		}
	}
	return true
}

// Convenience types for common cases

// IntSlice attaches the methods of Interface to []int, sorting in increasing order.
type IntSlice []int

func (p IntSlice) Len() int           { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p IntSlice) Sort() { Sort(p) }

// Float64Slice attaches the methods of Interface to []float64, sorting in increasing order.
type Float64Slice []float64

func (p Float64Slice) Len() int           { return len(p) }
func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || math.IsNaN(p[i]) && !math.IsNaN(p[j]) }
func (p Float64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p Float64Slice) Sort() { Sort(p) }

// StringSlice attaches the methods of Interface to []string, sorting in increasing order.
type StringSlice []string

func (p StringSlice) Len() int           { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p StringSlice) Sort() { Sort(p) }

// Convenience wrappers for common cases

// Ints sorts a slice of ints in increasing order.
func Ints(a []int) { Sort(IntSlice(a)) }

// Float64s sorts a slice of float64s in increasing order.
func Float64s(a []float64) { Sort(Float64Slice(a)) }

// Strings sorts a slice of strings in increasing order.
func Strings(a []string) { Sort(StringSlice(a)) }

// IntsAreSorted tests whether a slice of ints is sorted in increasing order.
func IntsAreSorted(a []int) bool { return IsSorted(IntSlice(a)) }

// Float64sAreSorted tests whether a slice of float64s is sorted in increasing order.
func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) }

// StringsAreSorted tests whether a slice of strings is sorted in increasing order.
func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) }
<|endoftext|>


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

package sync

// Once is an object that will perform exactly one action.
type Once struct {
	m    Mutex
	done bool
}

// Do calls the function f if and only if the method is being called for the
// first time with this receiver.  In other words, given
// 	var once Once
// if once.Do(f) is called multiple times, only the first call will invoke f,
// even if f has a different value in each invocation.  A new instance of
// Once is required for each function to execute.
//
// Do is intended for initialization that must be run exactly once.  Since f
// is niladic, it may be necessary to use a function literal to capture the
// arguments to a function to be invoked by Do:
// 	config.once.Do(func() { config.init(filename) })
//
func (o *Once) Do(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if !o.done {
		o.done = true
		f()
	}
}
<commit_msg>sync.once: document that Do cannot be invoked recursively. documentation change only.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sync

// Once is an object that will perform exactly one action.
type Once struct {
	m    Mutex
	done bool
}

// Do calls the function f if and only if the method is being called for the
// first time with this receiver.  In other words, given
// 	var once Once
// if Do(f) is called multiple times, only the first call will invoke f,
// even if f has a different value in each invocation.  A new instance of
// Once is required for each function to execute.
//
// Do is intended for initialization that must be run exactly once.  Since f
// is niladic, it may be necessary to use a function literal to capture the
// arguments to a function to be invoked by Do:
// 	config.once.Do(func() { config.init(filename) })
//
// Because no call to Do returns until the one call to f returns, if f causes
// Do to be called, it will deadlock.
//
func (o *Once) Do(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if !o.done {
		o.done = true
		f()
	}
}
<|endoftext|>


<commit_before>package db

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strings"

	"github.com/bosssauce/ponzu/system/admin/config"

	"github.com/boltdb/bolt"
	"github.com/gorilla/schema"
)

var configCache url.Values

func init() {
	configCache = make(url.Values)
}

// SetConfig sets key:value pairs in the db for configuration settings
func SetConfig(data url.Values) error {
	err := store.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("__config"))

		// check for any multi-value fields (ex. checkbox fields)
		// and correctly format for db storage. Essentially, we need
		// fieldX.0: value1, fieldX.1: value2 => fieldX: []string{value1, value2}
		var discardKeys []string
		for k, v := range data {
			if strings.Contains(k, ".") {
				key := strings.Split(k, ".")[0]

				if data.Get(key) == "" {
					data.Set(key, v[0])
					discardKeys = append(discardKeys, k)
				} else {
					data.Add(key, v[0])
				}
			}
		}

		for _, discardKey := range discardKeys {
			data.Del(discardKey)
		}

		cfg := &config.Config{}
		dec := schema.NewDecoder()
		dec.SetAliasTag("json")     // allows simpler struct tagging when creating a content type
		dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct
		err := dec.Decode(cfg, data)
		if err != nil {
			return err
		}

		// check for "invalidate" value to reset the Etag
		if len(cfg.CacheInvalidate) > 0 && cfg.CacheInvalidate[0] == "invalidate" {
			cfg.Etag = NewEtag()
			cfg.CacheInvalidate = []string{}
		}

		j, err := json.Marshal(cfg)
		if err != nil {
			return err
		}

		err = b.Put([]byte("settings"), j)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	configCache = data

	return nil
}

// Config gets the value of a key in the configuration from the db
func Config(key string) ([]byte, error) {
	kv := make(map[string]interface{})

	cfg, err := ConfigAll()
	if err != nil {
		return nil, err
	}

	if len(cfg) < 1 {
		return nil, nil
	}

	err = json.Unmarshal(cfg, &kv)
	if err != nil {
		return nil, err
	}

	return []byte(kv[key].(string)), nil
}

// ConfigAll gets the configuration from the db
func ConfigAll() ([]byte, error) {
	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("__config"))
		val.Write(b.Get([]byte("settings")))

		return nil
	})
	if err != nil {
		return nil, err
	}

	return val.Bytes(), nil
}

// PutConfig updates a single k/v in the config
func PutConfig(key string, value interface{}) error {
	fmt.Println("PutConfig:", key, value)
	kv := make(map[string]interface{})

	c, err := ConfigAll()
	if err != nil {
		return err
	}

	err = json.Unmarshal(c, &kv)
	if err != nil {
		return err
	}

	data := make(url.Values)
	for k, v := range kv {
		switch v.(type) {
		case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
			data.Set(k, fmt.Sprintf("%d", v))

		case float32, float64:
			data.Set(k, fmt.Sprintf("%f", v))

		case bool:
			data.Set(k, fmt.Sprintf("%t", v))

		case string:
			data.Set(k, v.(string))

		case []string:
			vv := v.([]string)
			for i := range vv {
				if i == 0 {
					data.Set(k, vv[i])
				} else {
					data.Add(k, vv[i])
				}
			}

		default:
			log.Println("No type case for:", k, v, "in PutConfig")
			data.Set(k, fmt.Sprintf("%v", v))
		}
	}

	err = SetConfig(data)
	if err != nil {
		return err
	}

	return nil
}

// ConfigCache is a in-memory cache of the Configs for quicker lookups
// 'key' is the JSON tag associated with the config field
func ConfigCache(key string) string {
	return configCache.Get(key)
}
<commit_msg>contin. debugging host port issue in config<commit_after>package db

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strings"

	"github.com/bosssauce/ponzu/system/admin/config"

	"github.com/boltdb/bolt"
	"github.com/gorilla/schema"
)

var configCache url.Values

func init() {
	configCache = make(url.Values)
}

// SetConfig sets key:value pairs in the db for configuration settings
func SetConfig(data url.Values) error {
	err := store.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("__config"))

		// check for any multi-value fields (ex. checkbox fields)
		// and correctly format for db storage. Essentially, we need
		// fieldX.0: value1, fieldX.1: value2 => fieldX: []string{value1, value2}
		var discardKeys []string
		for k, v := range data {
			if strings.Contains(k, ".") {
				key := strings.Split(k, ".")[0]

				if data.Get(key) == "" {
					data.Set(key, v[0])
					discardKeys = append(discardKeys, k)
				} else {
					data.Add(key, v[0])
				}
			}
		}

		for _, discardKey := range discardKeys {
			data.Del(discardKey)
		}

		cfg := &config.Config{}
		dec := schema.NewDecoder()
		dec.SetAliasTag("json")     // allows simpler struct tagging when creating a content type
		dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct
		err := dec.Decode(cfg, data)
		if err != nil {
			return err
		}

		// check for "invalidate" value to reset the Etag
		if len(cfg.CacheInvalidate) > 0 && cfg.CacheInvalidate[0] == "invalidate" {
			cfg.Etag = NewEtag()
			cfg.CacheInvalidate = []string{}
		}

		j, err := json.Marshal(cfg)
		if err != nil {
			return err
		}

		err = b.Put([]byte("settings"), j)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	configCache = data

	return nil
}

// Config gets the value of a key in the configuration from the db
func Config(key string) ([]byte, error) {
	kv := make(map[string]interface{})

	cfg, err := ConfigAll()
	if err != nil {
		return nil, err
	}

	if len(cfg) < 1 {
		return nil, nil
	}

	err = json.Unmarshal(cfg, &kv)
	if err != nil {
		return nil, err
	}

	return []byte(kv[key].(string)), nil
}

// ConfigAll gets the configuration from the db
func ConfigAll() ([]byte, error) {
	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("__config"))
		val.Write(b.Get([]byte("settings")))

		return nil
	})
	if err != nil {
		return nil, err
	}

	return val.Bytes(), nil
}

// PutConfig updates a single k/v in the config
func PutConfig(key string, value interface{}) error {
	fmt.Println("PutConfig:", key, value)
	kv := make(map[string]interface{})

	c, err := ConfigAll()
	if err != nil {
		return err
	}

	err = json.Unmarshal(c, &kv)
	if err != nil {
		return err
	}

	data := make(url.Values)
	for k, v := range kv {
		switch v.(type) {
		case string:
			data.Set(k, v.(string))

		case []string:
			vv := v.([]string)
			for i := range vv {
				if i == 0 {
					data.Set(k, vv[i])
				} else {
					data.Add(k, vv[i])
				}
			}

		default:
			log.Println("No type case for:", k, v, "in PutConfig")
			data.Set(k, fmt.Sprintf("%v", v))
		}
	}

	err = SetConfig(data)
	if err != nil {
		return err
	}

	return nil
}

// ConfigCache is a in-memory cache of the Configs for quicker lookups
// 'key' is the JSON tag associated with the config field
func ConfigCache(key string) string {
	return configCache.Get(key)
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/mjibson/mog/_third_party/github.com/julienschmidt/httprouter"
	"github.com/mjibson/mog/_third_party/golang.org/x/net/websocket"
	"github.com/mjibson/mog/protocol"
)

var indexHTML []byte

func (srv *Server) GetMux(devMode bool) *http.ServeMux {
	var err error
	webFS := FS(devMode)
	if devMode {
		log.Println("using local web assets")
	}
	index, err := webFS.Open("/static/index.html")
	if err != nil {
		log.Fatal(err)
	}
	indexHTML, err = ioutil.ReadAll(index)
	if err != nil {
		log.Fatal(err)
	}
	router := httprouter.New()
	router.GET("/api/cmd/:cmd", JSON(srv.Cmd))
	router.GET("/api/data/:type", JSON(srv.Data))
	router.GET("/api/oauth/:protocol", srv.OAuth)
	router.POST("/api/cmd/:cmd", JSON(srv.Cmd))
	router.POST("/api/queue/change", JSON(srv.QueueChange))
	router.POST("/api/playlist/change/:playlist", JSON(srv.PlaylistChange))
	router.POST("/api/protocol/add", JSON(srv.ProtocolAdd))
	router.POST("/api/protocol/remove", JSON(srv.ProtocolRemove))
	router.POST("/api/protocol/refresh", JSON(srv.ProtocolRefresh))
	mux := http.NewServeMux()
	mux.Handle("/static/", http.FileServer(webFS))
	mux.HandleFunc("/", Index)
	mux.Handle("/api/", router)
	mux.Handle("/ws/", websocket.Handler(srv.WebSocket))
	return mux
}

// ListenAndServe listens on the TCP network address addr and then calls
// Serve to handle requests on incoming connections.
func (srv *Server) ListenAndServe(addr string, devMode bool) error {
	mux := srv.GetMux(devMode)
	log.Println("mog: listening on", addr)
	return http.ListenAndServe(addr, mux)
}

func Index(w http.ResponseWriter, r *http.Request) {
	w.Write(indexHTML)
}

func serveError(w http.ResponseWriter, err error) {
	log.Println(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func JSON(h func(url.Values, httprouter.Params) (interface{}, error)) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		if err := r.ParseMultipartForm(1 << 20); err != nil {
			serveError(w, err)
			return
		}
		d, err := h(r.Form, ps)
		if err != nil {
			serveError(w, err)
			return
		}
		if d == nil {
			return
		}
		b, err := json.Marshal(d)
		if err != nil {
			serveError(w, err)
			return
		}
		w.Header().Add("Content-Type", "application/json")
		w.Write(b)
	}
}

func (srv *Server) OAuth(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	done := make(chan error)
	srv.ch <- cmdAddOAuth{
		name: ps.ByName("protocol"),
		r:    r,
		done: done,
	}
	err := <-done
	if err != nil {
		serveError(w, err)
		return
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

func (srv *Server) Data(form url.Values, ps httprouter.Params) (interface{}, error) {
	return srv.makeWaitData(waitType(ps.ByName("type")))
}

func (srv *Server) Cmd(form url.Values, ps httprouter.Params) (interface{}, error) {
	switch cmd := ps.ByName("cmd"); cmd {
	case "play":
		srv.ch <- cmdPlay
	case "stop":
		srv.ch <- cmdStop
	case "next":
		srv.ch <- cmdNext
	case "prev":
		srv.ch <- cmdPrev
	case "pause":
		srv.ch <- cmdPause
	case "play_idx":
		i, err := strconv.Atoi(form.Get("idx"))
		if err != nil {
			return nil, err
		}
		srv.ch <- cmdPlayIdx(i)
	case "random":
		srv.ch <- cmdRandom
	case "repeat":
		srv.ch <- cmdRepeat
	case "seek":
		d, err := time.ParseDuration(form.Get("pos"))
		if err != nil {
			return nil, err
		}
		srv.ch <- cmdSeek(d)
	default:
		return nil, fmt.Errorf("unknown command: %v", cmd)
	}
	return nil, nil
}

func (srv *Server) QueueChange(form url.Values, ps httprouter.Params) (interface{}, error) {
	srv.ch <- cmdQueueChange(form)
	return nil, nil
}

func (srv *Server) PlaylistChange(form url.Values, ps httprouter.Params) (interface{}, error) {
	srv.ch <- cmdPlaylistChange{
		form: form,
		name: ps.ByName("playlist"),
	}
	return nil, nil
}

func (srv *Server) ProtocolRefresh(form url.Values, ps httprouter.Params) (interface{}, error) {
	return nil, srv.protocolRefresh(form.Get("protocol"), form.Get("key"), false)
}

func (srv *Server) ProtocolAdd(form url.Values, ps httprouter.Params) (interface{}, error) {
	p := form.Get("protocol")
	prot, err := protocol.ByName(p)
	if err != nil {
		return nil, err
	}
	inst, err := prot.NewInstance(form["params"], nil)
	if err != nil {
		return nil, err
	}
	srv.Protocols[p][inst.Key()] = inst
	err = srv.protocolRefresh(p, inst.Key(), false)
	if err != nil {
		delete(srv.Protocols[p], inst.Key())
		return nil, err
	}
	return nil, nil
}

func (srv *Server) ProtocolRemove(form url.Values, ps httprouter.Params) (interface{}, error) {
	p := form.Get("protocol")
	k := form.Get("key")
	prots, ok := srv.Protocols[p]
	if !ok {
		return nil, fmt.Errorf("unknown protocol: %v", p)
	}
	srv.ch <- cmdProtocolRemove{
		protocol: p,
		key:      k,
		prots:    prots,
	}
	return nil, nil
}
<commit_msg>Allow non multipart forms<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/mjibson/mog/_third_party/github.com/julienschmidt/httprouter"
	"github.com/mjibson/mog/_third_party/golang.org/x/net/websocket"
	"github.com/mjibson/mog/protocol"
)

var indexHTML []byte

func (srv *Server) GetMux(devMode bool) *http.ServeMux {
	var err error
	webFS := FS(devMode)
	if devMode {
		log.Println("using local web assets")
	}
	index, err := webFS.Open("/static/index.html")
	if err != nil {
		log.Fatal(err)
	}
	indexHTML, err = ioutil.ReadAll(index)
	if err != nil {
		log.Fatal(err)
	}
	router := httprouter.New()
	router.GET("/api/cmd/:cmd", JSON(srv.Cmd))
	router.GET("/api/data/:type", JSON(srv.Data))
	router.GET("/api/oauth/:protocol", srv.OAuth)
	router.POST("/api/cmd/:cmd", JSON(srv.Cmd))
	router.POST("/api/queue/change", JSON(srv.QueueChange))
	router.POST("/api/playlist/change/:playlist", JSON(srv.PlaylistChange))
	router.POST("/api/protocol/add", JSON(srv.ProtocolAdd))
	router.POST("/api/protocol/remove", JSON(srv.ProtocolRemove))
	router.POST("/api/protocol/refresh", JSON(srv.ProtocolRefresh))
	mux := http.NewServeMux()
	mux.Handle("/static/", http.FileServer(webFS))
	mux.HandleFunc("/", Index)
	mux.Handle("/api/", router)
	mux.Handle("/ws/", websocket.Handler(srv.WebSocket))
	return mux
}

// ListenAndServe listens on the TCP network address addr and then calls
// Serve to handle requests on incoming connections.
func (srv *Server) ListenAndServe(addr string, devMode bool) error {
	mux := srv.GetMux(devMode)
	log.Println("mog: listening on", addr)
	return http.ListenAndServe(addr, mux)
}

func Index(w http.ResponseWriter, r *http.Request) {
	w.Write(indexHTML)
}

func serveError(w http.ResponseWriter, err error) {
	log.Println(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func JSON(h func(url.Values, httprouter.Params) (interface{}, error)) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data") {
			if err := r.ParseMultipartForm(1 << 20); err != nil {
				serveError(w, err)
				return
			}
		} else {
			if err := r.ParseForm(); err != nil {
				serveError(w, err)
				return
			}
		}
		d, err := h(r.Form, ps)
		if err != nil {
			serveError(w, err)
			return
		}
		if d == nil {
			return
		}
		b, err := json.Marshal(d)
		if err != nil {
			serveError(w, err)
			return
		}
		w.Header().Add("Content-Type", "application/json")
		w.Write(b)
	}
}

func (srv *Server) OAuth(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	done := make(chan error)
	srv.ch <- cmdAddOAuth{
		name: ps.ByName("protocol"),
		r:    r,
		done: done,
	}
	err := <-done
	if err != nil {
		serveError(w, err)
		return
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

func (srv *Server) Data(form url.Values, ps httprouter.Params) (interface{}, error) {
	return srv.makeWaitData(waitType(ps.ByName("type")))
}

func (srv *Server) Cmd(form url.Values, ps httprouter.Params) (interface{}, error) {
	switch cmd := ps.ByName("cmd"); cmd {
	case "play":
		srv.ch <- cmdPlay
	case "stop":
		srv.ch <- cmdStop
	case "next":
		srv.ch <- cmdNext
	case "prev":
		srv.ch <- cmdPrev
	case "pause":
		srv.ch <- cmdPause
	case "play_idx":
		i, err := strconv.Atoi(form.Get("idx"))
		if err != nil {
			return nil, err
		}
		srv.ch <- cmdPlayIdx(i)
	case "random":
		srv.ch <- cmdRandom
	case "repeat":
		srv.ch <- cmdRepeat
	case "seek":
		d, err := time.ParseDuration(form.Get("pos"))
		if err != nil {
			return nil, err
		}
		srv.ch <- cmdSeek(d)
	default:
		return nil, fmt.Errorf("unknown command: %v", cmd)
	}
	return nil, nil
}

func (srv *Server) QueueChange(form url.Values, ps httprouter.Params) (interface{}, error) {
	srv.ch <- cmdQueueChange(form)
	return nil, nil
}

func (srv *Server) PlaylistChange(form url.Values, ps httprouter.Params) (interface{}, error) {
	srv.ch <- cmdPlaylistChange{
		form: form,
		name: ps.ByName("playlist"),
	}
	return nil, nil
}

func (srv *Server) ProtocolRefresh(form url.Values, ps httprouter.Params) (interface{}, error) {
	return nil, srv.protocolRefresh(form.Get("protocol"), form.Get("key"), false)
}

func (srv *Server) ProtocolAdd(form url.Values, ps httprouter.Params) (interface{}, error) {
	p := form.Get("protocol")
	prot, err := protocol.ByName(p)
	if err != nil {
		return nil, err
	}
	inst, err := prot.NewInstance(form["params"], nil)
	if err != nil {
		return nil, err
	}
	srv.Protocols[p][inst.Key()] = inst
	err = srv.protocolRefresh(p, inst.Key(), false)
	if err != nil {
		delete(srv.Protocols[p], inst.Key())
		return nil, err
	}
	return nil, nil
}

func (srv *Server) ProtocolRemove(form url.Values, ps httprouter.Params) (interface{}, error) {
	p := form.Get("protocol")
	k := form.Get("key")
	prots, ok := srv.Protocols[p]
	if !ok {
		return nil, fmt.Errorf("unknown protocol: %v", p)
	}
	srv.ch <- cmdProtocolRemove{
		protocol: p,
		key:      k,
		prots:    prots,
	}
	return nil, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nlopes/slack"
	"log"
)

type UserManager struct {
	client             *AuthedSlack
	messageReplies     chan slack.MessageEvent
	newStandups        chan newStandupForUser
	usersByUserId      map[string]*User
	usersByIMChannelId map[string]*User
}

type newStandupForUser struct {
	standup *Standup
	userId  string
}

func NewUserManager(client *AuthedSlack) (um *UserManager) {
	um = &UserManager{
		client:             client,
		messageReplies:     make(chan slack.MessageEvent),
		newStandups:        make(chan newStandupForUser),
		usersByUserId:      make(map[string]*User),
		usersByIMChannelId: make(map[string]*User),
	}
	go um.start()
	return
}

func (self *UserManager) StartStandup(s *Standup, userId string) {
	self.newStandups <- newStandupForUser{standup: s, userId: userId}
}

func (self *UserManager) ReceiveMessageReply(m slack.MessageEvent) {
	self.messageReplies <- m
}

func (self *UserManager) start() {
	var user *User
	var err error
	var ok bool

	for {
		user = nil

		select {
		case m := <-self.messageReplies:
			if user, ok = self.usersByIMChannelId[m.ChannelId]; !ok {
				user, err = self.lookupUserByIMChannelId(m.ChannelId)
				if err != nil {
					log.Printf(
						"error getting channel info; message dropped: %s", err)
					continue
				}
				if user == nil {
					continue
				} else {
					self.usersByUserId[user.Info.Id] = user
					self.usersByIMChannelId[m.ChannelId] = user
				}
			}
			user.ReceiveMessageReply(m)

		case ns := <-self.newStandups:
			if user, ok = self.usersByUserId[ns.userId]; !ok {
				user, err = self.lookupUserById(ns.userId)
				if err != nil {
					log.Printf("error getting user info; new standup dropped: %s", err)
					continue
				}
				if user == nil {
					continue
				} else {
					self.usersByUserId[ns.userId] = user
					self.usersByIMChannelId[user.imChannelId] = user
				}
			}
			user.StartStandup(ns.standup)
		}
	}
}

func (self *UserManager) lookupUserByIMChannelId(channelId string) (user *User, err error) {
	ims, err := self.client.GetIMChannels()
	if err != nil {
		return
	}
	for _, im := range ims {
		if channelId != "" && im.Id == channelId {
			return self.newUser(im.UserId, im.Id)
		}
	}
	return
}

func (self *UserManager) lookupUserById(userId string) (user *User, err error) {
	_, _, channelId, err := self.client.OpenIMChannel(userId)
	if err != nil {
		return nil, err
	}
	return self.newUser(userId, channelId)
}

func (self *UserManager) newUser(userId string, imChannelId string) (user *User, err error) {
	userInfo, err := self.client.GetUserInfo(userId)
	if err != nil {
		return nil, err
	}
	return NewUser(self.client, *userInfo, imChannelId), nil
}
<commit_msg>Don't include bots in standup<commit_after>package main

import (
	"github.com/nlopes/slack"
	"log"
)

type UserManager struct {
	client             *AuthedSlack
	messageReplies     chan slack.MessageEvent
	newStandups        chan newStandupForUser
	usersByUserId      map[string]*User
	usersByIMChannelId map[string]*User
	userIdBlacklist    map[string]bool
	channelIdBlacklist map[string]bool
}

type newStandupForUser struct {
	standup *Standup
	userId  string
}

func NewUserManager(client *AuthedSlack) (um *UserManager) {
	um = &UserManager{
		client:             client,
		messageReplies:     make(chan slack.MessageEvent),
		newStandups:        make(chan newStandupForUser),
		usersByUserId:      make(map[string]*User),
		usersByIMChannelId: make(map[string]*User),
		userIdBlacklist:    make(map[string]bool),
		channelIdBlacklist: make(map[string]bool),
	}
	go um.start()
	return
}

func (self *UserManager) StartStandup(s *Standup, userId string) {
	self.newStandups <- newStandupForUser{standup: s, userId: userId}
}

func (self *UserManager) ReceiveMessageReply(m slack.MessageEvent) {
	self.messageReplies <- m
}

func (self *UserManager) start() {
	var user *User
	var err error
	var ok bool

	for {
		user = nil

		select {
		case m := <-self.messageReplies:
			if user, ok = self.usersByIMChannelId[m.ChannelId]; !ok {
				user, err = self.lookupUserByIMChannelId(m.ChannelId)
				if err != nil {
					log.Printf(
						"error getting channel info; message dropped: %s", err)
					continue
				}
				if user == nil {
					continue
				} else {
					self.usersByUserId[user.Info.Id] = user
					self.usersByIMChannelId[m.ChannelId] = user
				}
			}
			user.ReceiveMessageReply(m)

		case ns := <-self.newStandups:
			if user, ok = self.usersByUserId[ns.userId]; !ok {
				user, err = self.lookupUserById(ns.userId)
				if err != nil {
					log.Printf("error getting user info; new standup dropped: %s", err)
					continue
				}
				if user == nil {
					continue
				} else {
					self.usersByUserId[ns.userId] = user
					self.usersByIMChannelId[user.imChannelId] = user
				}
			}
			user.StartStandup(ns.standup)
		}
	}
}

func (self *UserManager) lookupUserByIMChannelId(channelId string) (user *User, err error) {
	if self.channelIdBlacklist[channelId] {
		return nil, nil
	}

	/* TODO: we could do better here, tracking opening and closing
	 * of IM channels with the RTM API
	 */
	ims, err := self.client.GetIMChannels()
	if err != nil {
		return
	}
	for _, im := range ims {
		if im.Id == channelId {
			user, err = self.newUser(im.UserId, im.Id)
			if user == nil && err == nil {
				self.channelIdBlacklist[channelId] = true
			}
			return
		}
	}
	return
}

func (self *UserManager) lookupUserById(userId string) (user *User, err error) {
	if self.userIdBlacklist[userId] {
		return nil, nil
	}

	_, _, channelId, err := self.client.OpenIMChannel(userId)
	if err != nil {
		return nil, err
	}
	return self.newUser(userId, channelId)
}

func (self *UserManager) newUser(userId string, imChannelId string) (user *User, err error) {
	userInfo, err := self.client.GetUserInfo(userId)
	if err != nil {
		return nil, err
	}
	if userInfo.IsBot {
		self.userIdBlacklist[userInfo.Id] = true
		return nil, nil
	}
	return NewUser(self.client, *userInfo, imChannelId), nil
}
<|endoftext|>


<commit_before>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/db"
	//e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/user"
)

func TestEmailController(t *testing.T) {

	var err error

	user.Secret = "secret"

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(true))

	router.POST("/email", EmailController)

	u := user.DefaultUser()
	u.SetId(2)
	u.SetAuthenticated()
	u.Password()

	assert.True(t, u.ComparePassword("testpassword"), "Test user password should be set")

	token, err := u.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	rows := sqlmock.NewRows([]string{"name", "email"}).AddRow("test", "old@test.com")
	mock.ExpectQuery(`SELECT user_name,user_email FROM users WHERE user_id`).WillReturnRows(rows)

	mock.ExpectExec("UPDATE users SET user_email").
		WithArgs("cool@test.com", 2).
		WillReturnResult(sqlmock.NewResult(1, 1))

	request := []byte(`{"ib": 1, "email": "test@test.com"}`)

	first := performJsonRequest(router, "POST", "/email", request)

	assert.Equal(t, first.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), successMessage(audit.AuditEmailUpdate), "HTTP response should match")

	assert.NoError(t, mock.ExpectationsWereMet(), "An error was not expected")

}
<commit_msg>add controller tests<commit_after>package controllers

import (
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/db"
	//e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/user"
)

func TestEmailController(t *testing.T) {

	var err error

	user.Secret = "secret"

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(true))

	router.POST("/email", EmailController)

	u := user.DefaultUser()
	u.SetId(2)
	u.SetAuthenticated()
	u.Password()

	assert.True(t, u.ComparePassword("testpassword"), "Test user password should be set")

	token, err := u.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	rows := sqlmock.NewRows([]string{"name", "email"}).AddRow("test", "old@test.com")
	mock.ExpectQuery(`SELECT user_name,user_email FROM users WHERE user_id`).WillReturnRows(rows)

	mock.ExpectExec("UPDATE users SET user_email").
		WithArgs("cool@test.com", 2).
		WillReturnResult(sqlmock.NewResult(1, 1))

	request := []byte(`{"ib": 1, "email": "test@test.com"}`)

	first := performJsonRequest(router, "POST", "/email", request)

	assert.Equal(t, first.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), successMessage(audit.AuditEmailUpdate), "HTTP response should match")

	assert.NoError(t, mock.ExpectationsWereMet(), "An error was not expected")

}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"strconv"
	"time"

	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
	"google.golang.org/cloud/storage"

	"github.com/coduno/api/model"
	"github.com/coduno/api/test"
	"github.com/coduno/api/util"
	"github.com/coduno/api/util/passenger"
	"github.com/gorilla/mux"

	"golang.org/x/net/context"
)

var fileNames = map[string]string{
	"py":   "app.py",
	"c":    "app.c",
	"cpp":  "app.cpp",
	"java": "Application.java",
}

// PostSubmission creates a new submission.
func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	var body = struct {
		Code     string
		Language string
	}{}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return http.StatusBadRequest, err
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	var task model.Task
	if err = datastore.Get(ctx, taskKey, &task); err != nil {
		return http.StatusInternalServerError, err
	}

	// Furthermore, the name of the GCS object is derived from the of the
	// encapsulating Submission. To avoid race conditions, allocate an ID.
	low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey)

	submission := model.Submission{
		Task: taskKey,
		Time: time.Now(),
	}

	if body.Code != "" {
		submission.Code, err = store(ctx, submissionKey, body.Code, body.Language)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		submission.Language = body.Language
	}

	// Set the submission in stone.
	if _, err = datastore.Put(ctx, submissionKey, submission); err != nil {
		return http.StatusInternalServerError, err
	}

	var tests model.Tests
	_, err = model.NewQueryForTest().
		Ancestor(taskKey).
		GetAll(ctx, &tests)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	for _, t := range tests {
		if err := test.Tester(t.Tester).Call(ctx, t.Params, *submission.Key(submissionKey)); err != nil {
			log.Warningf(ctx, "%s", err)
			continue
		}
	}

	return 0, nil
}

func store(ctx context.Context, key *datastore.Key, code, language string) (model.StoredObject, error) {
	o := model.StoredObject{}

	// We'll be storing code, so check what name the file should have in GCS.
	fn, ok := fileNames[language]
	if !ok {
		return o, errors.New("language unknown")
	}

	const submissionBucket = "submissions"

	// Now, construct the object.
	o = model.StoredObject{
		Bucket: submissionBucket,
		Name:   nameObject(key) + "/Code/" + fn,
	}

	// Upload the code to GCS.
	gcs := storage.NewWriter(ctx, o.Bucket, o.Name)
	if gcs == nil {
		return o, errors.New("cannot obtain writer to gcs")
	}
	gcs.ObjectAttrs = defaultObjectAttrs(fn)

	if _, err := io.WriteString(gcs, code); err != nil {
		return o, err
	}

	return o, gcs.Close()
}

// FinalSubmission makes the last submission final.
func FinalSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var resultKey *datastore.Key
	if resultKey, err = datastore.DecodeKey(mux.Vars(r)["resultKey"]); err != nil {
		return http.StatusInternalServerError, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	var index int
	if index, err = strconv.Atoi(mux.Vars(r)["index"]); err != nil {
		return http.StatusInternalServerError, err
	}

	var submissionKey *datastore.Key
	if submissionKey, err = datastore.DecodeKey(mux.Vars(r)["submissionKey"]); err != nil {
		return http.StatusInternalServerError, err
	}

	var result model.Result
	if err = datastore.Get(ctx, resultKey, &result); err != nil {
		return http.StatusInternalServerError, err
	}

	result.FinalSubmissions[index] = submissionKey

	if _, err = result.Put(ctx, resultKey); err != nil {
		return http.StatusInternalServerError, err
	}
	w.Write([]byte("OK"))
	return
}

func nameObject(key *datastore.Key) string {
	name := ""
	for key != nil {
		id := key.StringID()
		if id == "" {
			id = strconv.FormatInt(key.IntID(), 10)
		}
		name = "/" + key.Kind() + "/" + id + name
		key = key.Parent()
	}
	return name
}

func defaultObjectAttrs(disposition string) storage.ObjectAttrs {
	// TODO(flowlo): Establish ACLs?
	return storage.ObjectAttrs{
		ContentType:        "text/plain", // TODO(flowlo): Content types per language?
		ContentLanguage:    "",           // TODO(flowlo): Does it make sense to set this?
		ContentEncoding:    "utf-8",
		CacheControl:       "max-age=31556926", // Aggressive caching for one year
		ContentDisposition: "attachment; filename=\"" + disposition + "\"",
	}
}
<commit_msg>ctrl: Pass correct Context to GCS<commit_after>package controllers

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"strconv"
	"time"

	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
	"google.golang.org/cloud/storage"

	"github.com/coduno/api/model"
	"github.com/coduno/api/test"
	"github.com/coduno/api/util"
	"github.com/coduno/api/util/passenger"
	"github.com/gorilla/mux"

	"golang.org/x/net/context"
)

var fileNames = map[string]string{
	"py":   "app.py",
	"c":    "app.c",
	"cpp":  "app.cpp",
	"java": "Application.java",
}

// PostSubmission creates a new submission.
func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	var body = struct {
		Code     string
		Language string
	}{}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return http.StatusBadRequest, err
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	var task model.Task
	if err = datastore.Get(ctx, taskKey, &task); err != nil {
		return http.StatusInternalServerError, err
	}

	// Furthermore, the name of the GCS object is derived from the of the
	// encapsulating Submission. To avoid race conditions, allocate an ID.
	low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey)

	submission := model.Submission{
		Task: taskKey,
		Time: time.Now(),
	}

	if body.Code != "" {
		submission.Code, err = store(ctx, submissionKey, body.Code, body.Language)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		submission.Language = body.Language
	}

	// Set the submission in stone.
	if _, err = datastore.Put(ctx, submissionKey, submission); err != nil {
		return http.StatusInternalServerError, err
	}

	var tests model.Tests
	_, err = model.NewQueryForTest().
		Ancestor(taskKey).
		GetAll(ctx, &tests)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	for _, t := range tests {
		if err := test.Tester(t.Tester).Call(ctx, t.Params, *submission.Key(submissionKey)); err != nil {
			log.Warningf(ctx, "%s", err)
			continue
		}
	}

	return 0, nil
}

var gcsClient = new(http.Client)

func store(ctx context.Context, key *datastore.Key, code, language string) (model.StoredObject, error) {
	o := model.StoredObject{}

	// We'll be storing code, so check what name the file should have in GCS.
	fn, ok := fileNames[language]
	if !ok {
		return o, errors.New("language unknown")
	}

	const submissionBucket = "submissions"

	// Now, construct the object.
	o = model.StoredObject{
		Bucket: submissionBucket,
		Name:   nameObject(key) + "/Code/" + fn,
	}

	sctx := storage.NewContext(ctx, "coduno", gcsClient)

	// Upload the code to GCS.
	gcs := storage.NewWriter(sctx, o.Bucket, o.Name)
	if gcs == nil {
		return o, errors.New("cannot obtain writer to gcs")
	}
	gcs.ObjectAttrs = defaultObjectAttrs(fn)

	if _, err := io.WriteString(gcs, code); err != nil {
		return o, err
	}

	return o, gcs.Close()
}

// FinalSubmission makes the last submission final.
func FinalSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var resultKey *datastore.Key
	if resultKey, err = datastore.DecodeKey(mux.Vars(r)["resultKey"]); err != nil {
		return http.StatusInternalServerError, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	var index int
	if index, err = strconv.Atoi(mux.Vars(r)["index"]); err != nil {
		return http.StatusInternalServerError, err
	}

	var submissionKey *datastore.Key
	if submissionKey, err = datastore.DecodeKey(mux.Vars(r)["submissionKey"]); err != nil {
		return http.StatusInternalServerError, err
	}

	var result model.Result
	if err = datastore.Get(ctx, resultKey, &result); err != nil {
		return http.StatusInternalServerError, err
	}

	result.FinalSubmissions[index] = submissionKey

	if _, err = result.Put(ctx, resultKey); err != nil {
		return http.StatusInternalServerError, err
	}
	w.Write([]byte("OK"))
	return
}

func nameObject(key *datastore.Key) string {
	name := ""
	for key != nil {
		id := key.StringID()
		if id == "" {
			id = strconv.FormatInt(key.IntID(), 10)
		}
		name = "/" + key.Kind() + "/" + id + name
		key = key.Parent()
	}
	return name
}

func defaultObjectAttrs(disposition string) storage.ObjectAttrs {
	// TODO(flowlo): Establish ACLs?
	return storage.ObjectAttrs{
		ContentType:        "text/plain", // TODO(flowlo): Content types per language?
		ContentLanguage:    "",           // TODO(flowlo): Does it make sense to set this?
		ContentEncoding:    "utf-8",
		CacheControl:       "max-age=31556926", // Aggressive caching for one year
		ContentDisposition: "attachment; filename=\"" + disposition + "\"",
	}
}
<|endoftext|>


<commit_before>package lfs

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/git"
)

var (
	// The basic hook which just calls 'git lfs TYPE'
	hookBaseContent = "#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/__CMD__.\\n\"; exit 2; }\ngit lfs __CMD__ \"$@\""
)

// A Hook represents a githook as described in http://git-scm.com/docs/githooks.
// Hooks have a type, which is the type of hook that they are, and a body, which
// represents the thing they will execute when invoked by Git.
type Hook struct {
	Type         string
	Contents     string
	Upgradeables []string
}

// NewStandardHook creates a new hook using the template script calling 'git lfs theType'
func NewStandardHook(theType string, upgradeables []string) *Hook {
	return &Hook{
		Type:         theType,
		Contents:     strings.Replace(hookBaseContent, "__CMD__", theType, -1),
		Upgradeables: upgradeables,
	}
}

func (h *Hook) Exists() bool {
	_, err := os.Stat(h.Path())
	return err == nil
}

// Path returns the desired (or actual, if installed) location where this hook
// should be installed. It returns an absolute path in all cases.
func (h *Hook) Path() string {
	return filepath.Join(h.Dir(), h.Type)
}

// Dir returns the directory used by LFS for storing Git hooks. By default, it
// will return the hooks/ sub-directory of the local repository's .git
// directory. If `core.hooksPath` is configured and supported (Git verison is
// greater than "2.9.0"), it will return that instead.
func (h *Hook) Dir() string {
	customHooksSupported := git.Config.IsGitVersionAtLeast("2.9.0")
	if hp, ok := config.Config.Git.Get("core.hooksPath"); ok && customHooksSupported {
		return hp
	}

	return filepath.Join(config.LocalGitDir, "hooks")
}

// Install installs this Git hook on disk, or upgrades it if it does exist, and
// is upgradeable. It will create a hooks directory relative to the local Git
// directory. It returns and halts at any errors, and returns nil if the
// operation was a success.
func (h *Hook) Install(force bool) error {
	if err := os.MkdirAll(h.Dir(), 0755); err != nil {
		return err
	}

	if h.Exists() && !force {
		return h.Upgrade()
	}

	return h.write()
}

// write writes the contents of this Hook to disk, appending a newline at the
// end, and sets the mode to octal 0755. It writes to disk unconditionally, and
// returns at any error.
func (h *Hook) write() error {
	return ioutil.WriteFile(h.Path(), []byte(h.Contents+"\n"), 0755)
}

// Upgrade upgrades the (assumed to be) existing git hook to the current
// contents. A hook is considered "upgrade-able" if its contents are matched in
// the member variable `Upgradeables`. It halts and returns any errors as they
// arise.
func (h *Hook) Upgrade() error {
	match, err := h.matchesCurrent()
	if err != nil {
		return err
	}

	if !match {
		return nil
	}

	return h.write()
}

// Uninstall removes the hook on disk so long as it matches the current version,
// or any of the past versions of this hook.
func (h *Hook) Uninstall() error {
	if !InRepo() {
		return errors.New("Not in a git repository")
	}

	match, err := h.matchesCurrent()
	if err != nil {
		return err
	}

	if !match {
		return nil
	}

	return os.RemoveAll(h.Path())
}

// matchesCurrent returns whether or not an existing git hook is able to be
// written to or upgraded. A git hook matches those conditions if and only if
// its contents match the current contents, or any past "upgrade-able" contents
// of this hook.
func (h *Hook) matchesCurrent() (bool, error) {
	file, err := os.Open(h.Path())
	if err != nil {
		return false, err
	}

	by, err := ioutil.ReadAll(io.LimitReader(file, 1024))
	file.Close()
	if err != nil {
		return false, err
	}

	contents := strings.TrimSpace(string(by))
	if contents == h.Contents || len(contents) == 0 {
		return true, nil
	}

	for _, u := range h.Upgradeables {
		if u == contents {
			return true, nil
		}
	}

	return false, fmt.Errorf("Hook already exists: %s\n\n%s\n", string(h.Type), contents)
}
<commit_msg>Use template style replacements in hooks<commit_after>package lfs

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/git"
)

var (
	// The basic hook which just calls 'git lfs TYPE'
	hookBaseContent = "#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/{{Command}}.\\n\"; exit 2; }\ngit lfs {{Command}} \"$@\""
)

// A Hook represents a githook as described in http://git-scm.com/docs/githooks.
// Hooks have a type, which is the type of hook that they are, and a body, which
// represents the thing they will execute when invoked by Git.
type Hook struct {
	Type         string
	Contents     string
	Upgradeables []string
}

// NewStandardHook creates a new hook using the template script calling 'git lfs theType'
func NewStandardHook(theType string, upgradeables []string) *Hook {
	return &Hook{
		Type:         theType,
		Contents:     strings.Replace(hookBaseContent, "{{Command}}", theType, -1),
		Upgradeables: upgradeables,
	}
}

func (h *Hook) Exists() bool {
	_, err := os.Stat(h.Path())
	return err == nil
}

// Path returns the desired (or actual, if installed) location where this hook
// should be installed. It returns an absolute path in all cases.
func (h *Hook) Path() string {
	return filepath.Join(h.Dir(), h.Type)
}

// Dir returns the directory used by LFS for storing Git hooks. By default, it
// will return the hooks/ sub-directory of the local repository's .git
// directory. If `core.hooksPath` is configured and supported (Git verison is
// greater than "2.9.0"), it will return that instead.
func (h *Hook) Dir() string {
	customHooksSupported := git.Config.IsGitVersionAtLeast("2.9.0")
	if hp, ok := config.Config.Git.Get("core.hooksPath"); ok && customHooksSupported {
		return hp
	}

	return filepath.Join(config.LocalGitDir, "hooks")
}

// Install installs this Git hook on disk, or upgrades it if it does exist, and
// is upgradeable. It will create a hooks directory relative to the local Git
// directory. It returns and halts at any errors, and returns nil if the
// operation was a success.
func (h *Hook) Install(force bool) error {
	if err := os.MkdirAll(h.Dir(), 0755); err != nil {
		return err
	}

	if h.Exists() && !force {
		return h.Upgrade()
	}

	return h.write()
}

// write writes the contents of this Hook to disk, appending a newline at the
// end, and sets the mode to octal 0755. It writes to disk unconditionally, and
// returns at any error.
func (h *Hook) write() error {
	return ioutil.WriteFile(h.Path(), []byte(h.Contents+"\n"), 0755)
}

// Upgrade upgrades the (assumed to be) existing git hook to the current
// contents. A hook is considered "upgrade-able" if its contents are matched in
// the member variable `Upgradeables`. It halts and returns any errors as they
// arise.
func (h *Hook) Upgrade() error {
	match, err := h.matchesCurrent()
	if err != nil {
		return err
	}

	if !match {
		return nil
	}

	return h.write()
}

// Uninstall removes the hook on disk so long as it matches the current version,
// or any of the past versions of this hook.
func (h *Hook) Uninstall() error {
	if !InRepo() {
		return errors.New("Not in a git repository")
	}

	match, err := h.matchesCurrent()
	if err != nil {
		return err
	}

	if !match {
		return nil
	}

	return os.RemoveAll(h.Path())
}

// matchesCurrent returns whether or not an existing git hook is able to be
// written to or upgraded. A git hook matches those conditions if and only if
// its contents match the current contents, or any past "upgrade-able" contents
// of this hook.
func (h *Hook) matchesCurrent() (bool, error) {
	file, err := os.Open(h.Path())
	if err != nil {
		return false, err
	}

	by, err := ioutil.ReadAll(io.LimitReader(file, 1024))
	file.Close()
	if err != nil {
		return false, err
	}

	contents := strings.TrimSpace(string(by))
	if contents == h.Contents || len(contents) == 0 {
		return true, nil
	}

	for _, u := range h.Upgradeables {
		if u == contents {
			return true, nil
		}
	}

	return false, fmt.Errorf("Hook already exists: %s\n\n%s\n", string(h.Type), contents)
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package util

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"

	"github.com/asaskevich/govalidator"
)

var (
	// MaxRedirects specifies the maximum number of redirects an HTTP
	// request should be able to make
	MaxRedirects = 5
)

// HTTPError represents an error coming over HTTP,
// it is not an error with executing the request itself, it is
// an error the server is flaggin to the client.
type HTTPError struct {
	Code    int
	Message string
}

func (e HTTPError) Error() string {
	return e.Message
}

// checkRedirect implements this clients redirection policy
func checkRedirect(req *http.Request, via []*http.Request) error {
	if len(via) > MaxRedirects {
		return errors.New("Maximum number of redirects reached")
	}

	// use the same headers as before
	req.Header.Set("Authorization", via[len(via)-1].Header.Get("Authorization"))
	req.Header.Set("Accept", "application/json")
	return nil
}

// NewRequest creates a new http.Request that has authorization set up
func newRequest(server string, accessToken string, method string, URI string, body io.Reader) (*http.Request, error) {
	URL := fmt.Sprintf("%s%s", server, URI)
	req, err := http.NewRequest(method, URL, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", fmt.Sprintf("bearer %s", accessToken))
	req.Header.Set("Accept", "application/json")

	return req, nil
}

func performRequest(server, accessToken, method, URI string, body, res interface{}, redirects int) (err error) {
	var req *http.Request

	if body != nil {
		// body is not nil, so serialize it and pass it in the request
		if _, err = govalidator.ValidateStruct(body); err != nil {
			return fmt.Errorf("Got an illegal request body: %s", err)
		}

		buf := new(bytes.Buffer)
		encoder := json.NewEncoder(buf)
		err = encoder.Encode(body)
		if err != nil {
			return err
		}
		req, err = newRequest(server, accessToken, method, URI, buf)
	} else {
		// body is nil so create a nil request
		req, err = newRequest(server, accessToken, method, URI, nil)
	}

	if err != nil {
		return err
	}

	client := &http.Client{
		CheckRedirect: checkRedirect,
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 400 {
		return HTTPError{
			Code:    resp.StatusCode,
			Message: resp.Status,
		}
	}

	if resp.StatusCode == 307 {
		if redirects > 0 {
			location := resp.Header.Get("Location")
			return performRequest(server, accessToken, method, location, body, res, redirects-1)
		}
		return fmt.Errorf("Reached maximum number of redirects")
	}

	if resp.StatusCode >= 300 {
		// 307 is handled, 301, 302, 304 cannot be
		return fmt.Errorf("Unexpected %v redirection to %s", resp.StatusCode, resp.Header.Get("Location"))
	}

	if res != nil {
		defer resp.Body.Close()
		decoder := json.NewDecoder(resp.Body)
		if err := decoder.Decode(res); err != nil {
			return err
		}

		if _, err := govalidator.ValidateStruct(res); err != nil {
			return fmt.Errorf("Got an illegal response from server: %s", err)
		}
	}

	return nil
}

// GET does a get request to the account server,  decoding the result into the object pointed to byres
func GET(server, accessToken, URI string, res interface{}) error {
	return performRequest(server, accessToken, "GET", URI, nil, res, MaxRedirects)
}

// DELETE does a delete request to the account server
func DELETE(server, accessToken, URI string) error {
	return performRequest(server, accessToken, "DELETE", URI, nil, nil, MaxRedirects)
}

// POST creates an HTTP Post request to the specified server, with the body
// encoded as JSON, decoding the result into the object pointed to byres
func POST(server, accessToken, URI string, body, res interface{}) error {
	return performRequest(server, accessToken, "POST", URI, body, res, MaxRedirects)
}

// PUT creates an HTTP Put request to the specified server, with the body
// encoded as JSON, decoding the result into the object pointed to byres
func PUT(server, accessToken, URI string, body, res interface{}) error {
	return performRequest(server, accessToken, "PUT", URI, body, res, MaxRedirects)
}

// PATCH creates an HTTP Patch request to the specified server, with the body
// encoded as JSON, decoding the result into the object pointed to byres
func PATCH(server, accessToken, URI string, body, res interface{}) error {
	return performRequest(server, accessToken, "POST", URI, body, res, MaxRedirects)
}
<commit_msg>use more general validate function<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package util

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

var (
	// MaxRedirects specifies the maximum number of redirects an HTTP
	// request should be able to make
	MaxRedirects = 5
)

// HTTPError represents an error coming over HTTP,
// it is not an error with executing the request itself, it is
// an error the server is flaggin to the client.
type HTTPError struct {
	Code    int
	Message string
}

func (e HTTPError) Error() string {
	return e.Message
}

// checkRedirect implements this clients redirection policy
func checkRedirect(req *http.Request, via []*http.Request) error {
	if len(via) > MaxRedirects {
		return errors.New("Maximum number of redirects reached")
	}

	// use the same headers as before
	req.Header.Set("Authorization", via[len(via)-1].Header.Get("Authorization"))
	req.Header.Set("Accept", "application/json")
	return nil
}

// NewRequest creates a new http.Request that has authorization set up
func newRequest(server string, accessToken string, method string, URI string, body io.Reader) (*http.Request, error) {
	URL := fmt.Sprintf("%s%s", server, URI)
	req, err := http.NewRequest(method, URL, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", fmt.Sprintf("bearer %s", accessToken))
	req.Header.Set("Accept", "application/json")

	return req, nil
}

func performRequest(server, accessToken, method, URI string, body, res interface{}, redirects int) (err error) {
	var req *http.Request

	if body != nil {
		// body is not nil, so serialize it and pass it in the request
		if err = Validate(body); err != nil {
			return fmt.Errorf("Got an illegal request body: %s", err)
		}

		buf := new(bytes.Buffer)
		encoder := json.NewEncoder(buf)
		err = encoder.Encode(body)
		if err != nil {
			return err
		}
		req, err = newRequest(server, accessToken, method, URI, buf)
	} else {
		// body is nil so create a nil request
		req, err = newRequest(server, accessToken, method, URI, nil)
	}

	if err != nil {
		return err
	}

	client := &http.Client{
		CheckRedirect: checkRedirect,
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 400 {
		return HTTPError{
			Code:    resp.StatusCode,
			Message: resp.Status,
		}
	}

	if resp.StatusCode == 307 {
		if redirects > 0 {
			location := resp.Header.Get("Location")
			return performRequest(server, accessToken, method, location, body, res, redirects-1)
		}
		return fmt.Errorf("Reached maximum number of redirects")
	}

	if resp.StatusCode >= 300 {
		// 307 is handled, 301, 302, 304 cannot be
		return fmt.Errorf("Unexpected %v redirection to %s", resp.StatusCode, resp.Header.Get("Location"))
	}

	if res != nil {
		defer resp.Body.Close()
		decoder := json.NewDecoder(resp.Body)
		if err := decoder.Decode(res); err != nil {
			return err
		}

		if err := Validate(res); err != nil {
			return fmt.Errorf("Got an illegal response from server: %s", err)
		}
	}

	return nil
}

// GET does a get request to the account server,  decoding the result into the object pointed to byres
func GET(server, accessToken, URI string, res interface{}) error {
	return performRequest(server, accessToken, "GET", URI, nil, res, MaxRedirects)
}

// DELETE does a delete request to the account server
func DELETE(server, accessToken, URI string) error {
	return performRequest(server, accessToken, "DELETE", URI, nil, nil, MaxRedirects)
}

// POST creates an HTTP Post request to the specified server, with the body
// encoded as JSON, decoding the result into the object pointed to byres
func POST(server, accessToken, URI string, body, res interface{}) error {
	return performRequest(server, accessToken, "POST", URI, body, res, MaxRedirects)
}

// PUT creates an HTTP Put request to the specified server, with the body
// encoded as JSON, decoding the result into the object pointed to byres
func PUT(server, accessToken, URI string, body, res interface{}) error {
	return performRequest(server, accessToken, "PUT", URI, body, res, MaxRedirects)
}

// PATCH creates an HTTP Patch request to the specified server, with the body
// encoded as JSON, decoding the result into the object pointed to byres
func PATCH(server, accessToken, URI string, body, res interface{}) error {
	return performRequest(server, accessToken, "POST", URI, body, res, MaxRedirects)
}
<|endoftext|>


<commit_before>package main

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

	"h12.me/kafka/broker"
)

const (
	clientID = "h12.me/kafka/kafpro"
)

type Config struct {
	Broker  string
	Meta    MetaConfig
	Coord   CoordConfig
	Offset  OffsetConfig
	Commit  CommitConfig
	Time    TimeConfig
	Consume ConsumeConfig
}

type CoordConfig struct {
	GroupName string
}

type OffsetConfig struct {
	GroupName string
	Topic     string
	Partition int
}

type TimeConfig struct {
	Topic     string
	Partition int
	Time      string
}

type ConsumeConfig struct {
	Topic     string
	Partition int
	Offset    int
}

type MetaConfig struct {
	Topics Topics
}

type Topics []string

func (ts Topics) String() string {
	return strings.Join(ts, ",")
}

func (ts *Topics) Set(s string) error {
	*ts = strings.Split(s, ",")
	return nil
}

type CommitConfig struct {
	GroupName string
	Topic     string
	Partition int
	Offset    int
	Retention int // millisecond
}

func main() {
	var cfg Config
	flag.StringVar(&cfg.Broker, "broker", "", "broker address")

	// get subcommand
	if len(os.Args) < 2 {
		usage()
		os.Exit(1)
	}
	subCmd := os.Args[1]
	os.Args = append(os.Args[0:1], os.Args[2:]...)

	switch subCmd {
	case "meta":
		flag.Var(&cfg.Meta.Topics, "topics", "topic names seperated by comma")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := meta(br, &cfg.Meta); err != nil {
			log.Fatal(err)
		}
	case "coord":
		flag.StringVar(&cfg.Coord.GroupName, "group", "", "group name")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := coord(br, &cfg.Coord); err != nil {
			log.Fatal(err)
		}
	case "offset":
		flag.StringVar(&cfg.Offset.GroupName, "group", "", "group name")
		flag.StringVar(&cfg.Offset.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Offset.Partition, "partition", 0, "partition")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := offset(br, &cfg.Offset); err != nil {
			log.Fatal(err)
		}
	case "commit":
		flag.StringVar(&cfg.Commit.GroupName, "group", "", "group name")
		flag.StringVar(&cfg.Commit.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Commit.Partition, "partition", 0, "partition")
		flag.IntVar(&cfg.Commit.Offset, "offset", 0, "offset")
		flag.IntVar(&cfg.Commit.Retention, "retention", 0, "retention")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := commit(br, &cfg.Commit); err != nil {
			log.Fatal(err)
		}
	case "time":
		flag.StringVar(&cfg.Time.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Time.Partition, "partition", 0, "partition")
		flag.StringVar(&cfg.Time.Time, "time", "", "time")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := timeOffset(br, &cfg.Time); err != nil {
			log.Fatal(err)
		}
	case "consume":
		flag.StringVar(&cfg.Consume.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Consume.Partition, "partition", 0, "partition")
		flag.IntVar(&cfg.Consume.Offset, "offset", 0, "offset")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := consume(br, &cfg.Consume); err != nil {
			log.Fatal(err)
		}
	default:
		log.Fatalf("invalid subcommand %s", subCmd)
	}
}

func usage() {
	fmt.Println(`
kafpro is a command line tool for querying Kafka wire API

Usage:

	kafpro command [arguments]

The commands are:

	meta     TopicMetadataRequest
	consume  FetchRequest
	time     OffsetRequest
	offset   OffsetFetchRequestV1
	commit   OffsetCommitRequestV1
	coord    GroupCoordinatorRequest

`)
	flag.PrintDefaults()
}

func meta(br *broker.B, cfg *MetaConfig) error {
	resp, err := br.TopicMetadata(cfg.Topics...)
	if err != nil {
		return err
	}
	fmt.Println(toJSON(resp))
	return nil
}

func coord(br *broker.B, coord *CoordConfig) error {
	resp, err := br.GroupCoordinator(coord.GroupName)
	if err != nil {
		return err
	}
	fmt.Println(toJSON(resp))
	return nil
}

func offset(br *broker.B, cfg *OffsetConfig) error {
	req := &broker.Request{
		ClientID: clientID,
		RequestMessage: &broker.OffsetFetchRequestV1{
			ConsumerGroup: cfg.GroupName,
			PartitionInTopics: []broker.PartitionInTopic{
				{
					TopicName:  cfg.Topic,
					Partitions: []int32{int32(cfg.Partition)},
				},
			},
		},
	}
	resp := broker.OffsetFetchResponse{}
	if err := br.Do(req, &resp); err != nil {
		return err
	}
	fmt.Println(toJSON(&resp))
	for i := range resp {
		t := &resp[i]
		if t.TopicName == cfg.Topic {
			for j := range resp[i].OffsetMetadataInPartitions {
				p := &t.OffsetMetadataInPartitions[j]
				if p.Partition == int32(cfg.Partition) {
					if p.ErrorCode.HasError() {
						return p.ErrorCode
					}
				}
			}
		}
	}
	return nil
}

func commit(br *broker.B, cfg *CommitConfig) error {
	req := &broker.Request{
		ClientID: clientID,
		RequestMessage: &broker.OffsetCommitRequestV1{
			ConsumerGroupID: cfg.GroupName,
			OffsetCommitInTopicV1s: []broker.OffsetCommitInTopicV1{
				{
					TopicName: cfg.Topic,
					OffsetCommitInPartitionV1s: []broker.OffsetCommitInPartitionV1{
						{
							Partition: int32(cfg.Partition),
							Offset:    int64(cfg.Offset),
							// TimeStamp in milliseconds
							TimeStamp: time.Now().Add(time.Duration(cfg.Retention)*time.Millisecond).Unix() * 1000,
						},
					},
				},
			},
		},
	}
	resp := broker.OffsetCommitResponse{}
	if err := br.Do(req, &resp); err != nil {
		return err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName == cfg.Topic {
			for j := range t.ErrorInPartitions {
				p := &t.ErrorInPartitions[j]
				if int(p.Partition) == cfg.Partition {
					if p.ErrorCode.HasError() {
						return p.ErrorCode
					}
				}
			}
		}
	}
	return nil
}

func timeOffset(br *broker.B, cfg *TimeConfig) error {
	var t time.Time
	switch cfg.Time {
	case "latest":
		t = broker.Latest
	case "earliest":
		t = broker.Earliest
	default:
		var err error
		t, err = time.Parse("2006-01-02T15:04:05", cfg.Time)
		if err != nil {
			return fmt.Errorf("error parsing %s: %s", cfg.Time, err.Error())
		}
	}
	resp, err := br.OffsetByTime(cfg.Topic, int32(cfg.Partition), t)
	if err != nil {
		return err
	}
	fmt.Println(toJSON(&resp))
	return nil
}

func consume(br *broker.B, cfg *ConsumeConfig) error {
	req := &broker.Request{
		ClientID: clientID,
		RequestMessage: &broker.FetchRequest{
			ReplicaID:   -1,
			MaxWaitTime: int32(time.Second / time.Millisecond),
			MinBytes:    int32(1024),
			FetchOffsetInTopics: []broker.FetchOffsetInTopic{
				{
					TopicName: cfg.Topic,
					FetchOffsetInPartitions: []broker.FetchOffsetInPartition{
						{
							Partition:   int32(cfg.Partition),
							FetchOffset: int64(cfg.Offset),
							MaxBytes:    int32(1000),
						},
					},
				},
			},
		},
	}
	resp := broker.FetchResponse{}
	if err := br.Do(req, &resp); err != nil {
		return err
	}
	fmt.Println(toJSON(resp))
	for _, t := range resp {
		for _, p := range t.FetchMessageSetInPartitions {
			ms, err := p.MessageSet.Flatten()
			if err != nil {
				return err
			}
			fmt.Println(toJSON(ms))
		}
	}
	return nil
}

func toJSON(v interface{}) string {
	buf, _ := json.MarshalIndent(v, "", "\t")
	return string(buf)
}
<commit_msg>note on go-flags<commit_after>package main

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

	// TODO: "github.com/jessevdk/go-flags"
	"h12.me/kafka/broker"
)

const (
	clientID = "h12.me/kafka/kafpro"
)

type Config struct {
	Broker  string
	Meta    MetaConfig
	Coord   CoordConfig
	Offset  OffsetConfig
	Commit  CommitConfig
	Time    TimeConfig
	Consume ConsumeConfig
}

type CoordConfig struct {
	GroupName string
}

type OffsetConfig struct {
	GroupName string
	Topic     string
	Partition int
}

type TimeConfig struct {
	Topic     string
	Partition int
	Time      string
}

type ConsumeConfig struct {
	Topic     string
	Partition int
	Offset    int
}

type MetaConfig struct {
	Topics Topics
}

type Topics []string

func (ts Topics) String() string {
	return strings.Join(ts, ",")
}

func (ts *Topics) Set(s string) error {
	*ts = strings.Split(s, ",")
	return nil
}

type CommitConfig struct {
	GroupName string
	Topic     string
	Partition int
	Offset    int
	Retention int // millisecond
}

func main() {
	var cfg Config
	flag.StringVar(&cfg.Broker, "broker", "", "broker address")

	// get subcommand
	if len(os.Args) < 2 {
		usage()
		os.Exit(1)
	}
	subCmd := os.Args[1]
	os.Args = append(os.Args[0:1], os.Args[2:]...)

	switch subCmd {
	case "meta":
		flag.Var(&cfg.Meta.Topics, "topics", "topic names seperated by comma")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := meta(br, &cfg.Meta); err != nil {
			log.Fatal(err)
		}
	case "coord":
		flag.StringVar(&cfg.Coord.GroupName, "group", "", "group name")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := coord(br, &cfg.Coord); err != nil {
			log.Fatal(err)
		}
	case "offset":
		flag.StringVar(&cfg.Offset.GroupName, "group", "", "group name")
		flag.StringVar(&cfg.Offset.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Offset.Partition, "partition", 0, "partition")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := offset(br, &cfg.Offset); err != nil {
			log.Fatal(err)
		}
	case "commit":
		flag.StringVar(&cfg.Commit.GroupName, "group", "", "group name")
		flag.StringVar(&cfg.Commit.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Commit.Partition, "partition", 0, "partition")
		flag.IntVar(&cfg.Commit.Offset, "offset", 0, "offset")
		flag.IntVar(&cfg.Commit.Retention, "retention", 0, "retention")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := commit(br, &cfg.Commit); err != nil {
			log.Fatal(err)
		}
	case "time":
		flag.StringVar(&cfg.Time.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Time.Partition, "partition", 0, "partition")
		flag.StringVar(&cfg.Time.Time, "time", "", "time")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := timeOffset(br, &cfg.Time); err != nil {
			log.Fatal(err)
		}
	case "consume":
		flag.StringVar(&cfg.Consume.Topic, "topic", "", "topic name")
		flag.IntVar(&cfg.Consume.Partition, "partition", 0, "partition")
		flag.IntVar(&cfg.Consume.Offset, "offset", 0, "offset")
		flag.Parse()
		br := broker.New(broker.DefaultConfig().WithAddr(cfg.Broker))
		if err := consume(br, &cfg.Consume); err != nil {
			log.Fatal(err)
		}
	default:
		log.Fatalf("invalid subcommand %s", subCmd)
	}
}

func usage() {
	fmt.Println(`
kafpro is a command line tool for querying Kafka wire API

Usage:

	kafpro command [arguments]

The commands are:

	meta     TopicMetadataRequest
	consume  FetchRequest
	time     OffsetRequest
	offset   OffsetFetchRequestV1
	commit   OffsetCommitRequestV1
	coord    GroupCoordinatorRequest

`)
	flag.PrintDefaults()
}

func meta(br *broker.B, cfg *MetaConfig) error {
	resp, err := br.TopicMetadata(cfg.Topics...)
	if err != nil {
		return err
	}
	fmt.Println(toJSON(resp))
	return nil
}

func coord(br *broker.B, coord *CoordConfig) error {
	resp, err := br.GroupCoordinator(coord.GroupName)
	if err != nil {
		return err
	}
	fmt.Println(toJSON(resp))
	return nil
}

func offset(br *broker.B, cfg *OffsetConfig) error {
	req := &broker.Request{
		ClientID: clientID,
		RequestMessage: &broker.OffsetFetchRequestV1{
			ConsumerGroup: cfg.GroupName,
			PartitionInTopics: []broker.PartitionInTopic{
				{
					TopicName:  cfg.Topic,
					Partitions: []int32{int32(cfg.Partition)},
				},
			},
		},
	}
	resp := broker.OffsetFetchResponse{}
	if err := br.Do(req, &resp); err != nil {
		return err
	}
	fmt.Println(toJSON(&resp))
	for i := range resp {
		t := &resp[i]
		if t.TopicName == cfg.Topic {
			for j := range resp[i].OffsetMetadataInPartitions {
				p := &t.OffsetMetadataInPartitions[j]
				if p.Partition == int32(cfg.Partition) {
					if p.ErrorCode.HasError() {
						return p.ErrorCode
					}
				}
			}
		}
	}
	return nil
}

func commit(br *broker.B, cfg *CommitConfig) error {
	req := &broker.Request{
		ClientID: clientID,
		RequestMessage: &broker.OffsetCommitRequestV1{
			ConsumerGroupID: cfg.GroupName,
			OffsetCommitInTopicV1s: []broker.OffsetCommitInTopicV1{
				{
					TopicName: cfg.Topic,
					OffsetCommitInPartitionV1s: []broker.OffsetCommitInPartitionV1{
						{
							Partition: int32(cfg.Partition),
							Offset:    int64(cfg.Offset),
							// TimeStamp in milliseconds
							TimeStamp: time.Now().Add(time.Duration(cfg.Retention)*time.Millisecond).Unix() * 1000,
						},
					},
				},
			},
		},
	}
	resp := broker.OffsetCommitResponse{}
	if err := br.Do(req, &resp); err != nil {
		return err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName == cfg.Topic {
			for j := range t.ErrorInPartitions {
				p := &t.ErrorInPartitions[j]
				if int(p.Partition) == cfg.Partition {
					if p.ErrorCode.HasError() {
						return p.ErrorCode
					}
				}
			}
		}
	}
	return nil
}

func timeOffset(br *broker.B, cfg *TimeConfig) error {
	var t time.Time
	switch cfg.Time {
	case "latest":
		t = broker.Latest
	case "earliest":
		t = broker.Earliest
	default:
		var err error
		t, err = time.Parse("2006-01-02T15:04:05", cfg.Time)
		if err != nil {
			return fmt.Errorf("error parsing %s: %s", cfg.Time, err.Error())
		}
	}
	resp, err := br.OffsetByTime(cfg.Topic, int32(cfg.Partition), t)
	if err != nil {
		return err
	}
	fmt.Println(toJSON(&resp))
	return nil
}

func consume(br *broker.B, cfg *ConsumeConfig) error {
	req := &broker.Request{
		ClientID: clientID,
		RequestMessage: &broker.FetchRequest{
			ReplicaID:   -1,
			MaxWaitTime: int32(time.Second / time.Millisecond),
			MinBytes:    int32(1024),
			FetchOffsetInTopics: []broker.FetchOffsetInTopic{
				{
					TopicName: cfg.Topic,
					FetchOffsetInPartitions: []broker.FetchOffsetInPartition{
						{
							Partition:   int32(cfg.Partition),
							FetchOffset: int64(cfg.Offset),
							MaxBytes:    int32(1000),
						},
					},
				},
			},
		},
	}
	resp := broker.FetchResponse{}
	if err := br.Do(req, &resp); err != nil {
		return err
	}
	fmt.Println(toJSON(resp))
	for _, t := range resp {
		for _, p := range t.FetchMessageSetInPartitions {
			ms, err := p.MessageSet.Flatten()
			if err != nil {
				return err
			}
			fmt.Println(toJSON(ms))
		}
	}
	return nil
}

func toJSON(v interface{}) string {
	buf, _ := json.MarshalIndent(v, "", "\t")
	return string(buf)
}
<|endoftext|>


<commit_before>package xorm

import (
	"errors"
	"fmt"
	"strings"

	"github.com/go-xorm/core"
)

// func init() {
// 	RegisterDialect("sqlite3", &sqlite3{})
// }

type sqlite3 struct {
	core.Base
}

func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
	return db.Base.Init(d, db, uri, drivername, dataSourceName)
}

func (db *sqlite3) SqlType(c *core.Column) string {
	switch t := c.SQLType.Name; t {
	case core.Date, core.DateTime, core.TimeStamp, core.Time:
		return core.Numeric
	case core.TimeStampz:
		return core.Text
	case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText:
		return core.Text
	case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool:
		return core.Integer
	case core.Float, core.Double, core.Real:
		return core.Real
	case core.Decimal, core.Numeric:
		return core.Numeric
	case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
		return core.Blob
	case core.Serial, core.BigSerial:
		c.IsPrimaryKey = true
		c.IsAutoIncrement = true
		c.Nullable = false
		return core.Integer
	default:
		return t
	}
}

func (db *sqlite3) FormatBytes(bs []byte) string {
	return fmt.Sprintf("X'%x'", bs)
}

func (db *sqlite3) SupportInsertMany() bool {
	return true
}

func (db *sqlite3) QuoteStr() string {
	return "`"
}

func (db *sqlite3) AutoIncrStr() string {
	return "AUTOINCREMENT"
}

func (db *sqlite3) SupportEngine() bool {
	return false
}

func (db *sqlite3) SupportCharset() bool {
	return false
}

func (db *sqlite3) IndexOnTable() bool {
	return false
}

func (db *sqlite3) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
	args := []interface{}{idxName}
	return "SELECT name FROM sqlite_master WHERE type='index' and name = ?", args
}

func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
	args := []interface{}{tableName}
	return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
}

/*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
	args := []interface{}{tableName}
	sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
	return sql, args
}*/

func (db *sqlite3) IsColumnExist(tableName string, col *core.Column) (bool, error) {
	args := []interface{}{tableName}
	query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + col.Name + "`%') or (sql like '%[" + col.Name + "]%'))"
	rows, err := db.DB().Query(query, args...)
	if err != nil {
		return false, err
	}
	defer rows.Close()

	if rows.Next() {
		return true, nil
	}
	return false, nil
}

func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
	args := []interface{}{tableName}
	s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"

	rows, err := db.DB().Query(s, args...)
	if err != nil {
		return nil, nil, err
	}
	defer rows.Close()

	var name string
	for rows.Next() {
		err = rows.Scan(&name)
		if err != nil {
			return nil, nil, err
		}
		break
	}

	if name == "" {
		return nil, nil, errors.New("no table named " + tableName)
	}

	nStart := strings.Index(name, "(")
	nEnd := strings.LastIndex(name, ")")
	colCreates := strings.Split(name[nStart+1:nEnd], ",")
	cols := make(map[string]*core.Column)
	colSeq := make([]string, 0)
	for _, colStr := range colCreates {
		fields := strings.Fields(strings.TrimSpace(colStr))
		col := new(core.Column)
		col.Indexes = make(map[string]bool)
		col.Nullable = true
		for idx, field := range fields {
			if idx == 0 {
				col.Name = strings.Trim(field, "`[] ")
				continue
			} else if idx == 1 {
				col.SQLType = core.SQLType{field, 0, 0}
			}
			switch field {
			case "PRIMARY":
				col.IsPrimaryKey = true
			case "AUTOINCREMENT":
				col.IsAutoIncrement = true
			case "NULL":
				if fields[idx-1] == "NOT" {
					col.Nullable = false
				} else {
					col.Nullable = true
				}
			}
		}
		cols[col.Name] = col
		colSeq = append(colSeq, col.Name)
	}
	return colSeq, cols, nil
}

func (db *sqlite3) GetTables() ([]*core.Table, error) {
	args := []interface{}{}
	s := "SELECT name FROM sqlite_master WHERE type='table'"

	rows, err := db.DB().Query(s, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	tables := make([]*core.Table, 0)
	for rows.Next() {
		table := core.NewEmptyTable()
		err = rows.Scan(&table.Name)
		if err != nil {
			return nil, err
		}
		if table.Name == "sqlite_sequence" {
			continue
		}
		tables = append(tables, table)
	}
	return tables, nil
}

func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) {
	args := []interface{}{tableName}
	s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"

	rows, err := db.DB().Query(s, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	indexes := make(map[string]*core.Index, 0)
	for rows.Next() {
		var sql string
		err = rows.Scan(&sql)
		if err != nil {
			return nil, err
		}

		if sql == "" {
			continue
		}

		index := new(core.Index)
		nNStart := strings.Index(sql, "INDEX")
		nNEnd := strings.Index(sql, "ON")
		if nNStart == -1 || nNEnd == -1 {
			continue
		}

		indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
		//fmt.Println(indexName)
		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
			index.Name = indexName[5+len(tableName) : len(indexName)]
		} else {
			index.Name = indexName
		}

		if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
			index.Type = core.UniqueType
		} else {
			index.Type = core.IndexType
		}

		nStart := strings.Index(sql, "(")
		nEnd := strings.Index(sql, ")")
		colIndexes := strings.Split(sql[nStart+1:nEnd], ",")

		index.Cols = make([]string, 0)
		for _, col := range colIndexes {
			index.Cols = append(index.Cols, strings.Trim(col, "` []"))
		}
		indexes[index.Name] = index
	}

	return indexes, nil
}

func (db *sqlite3) Filters() []core.Filter {
	return []core.Filter{&core.IdFilter{}}
}
<commit_msg>bug fixed for sqlite3 dialect<commit_after>package xorm

import (
	"errors"
	"fmt"
	"strings"

	"github.com/go-xorm/core"
)

// func init() {
// 	RegisterDialect("sqlite3", &sqlite3{})
// }

type sqlite3 struct {
	core.Base
}

func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
	return db.Base.Init(d, db, uri, drivername, dataSourceName)
}

func (db *sqlite3) SqlType(c *core.Column) string {
	switch t := c.SQLType.Name; t {
	case core.Date, core.DateTime, core.TimeStamp, core.Time:
		return core.Numeric
	case core.TimeStampz:
		return core.Text
	case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText:
		return core.Text
	case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool:
		return core.Integer
	case core.Float, core.Double, core.Real:
		return core.Real
	case core.Decimal, core.Numeric:
		return core.Numeric
	case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
		return core.Blob
	case core.Serial, core.BigSerial:
		c.IsPrimaryKey = true
		c.IsAutoIncrement = true
		c.Nullable = false
		return core.Integer
	default:
		return t
	}
}

func (db *sqlite3) FormatBytes(bs []byte) string {
	return fmt.Sprintf("X'%x'", bs)
}

func (db *sqlite3) SupportInsertMany() bool {
	return true
}

func (db *sqlite3) QuoteStr() string {
	return "`"
}

func (db *sqlite3) AutoIncrStr() string {
	return "AUTOINCREMENT"
}

func (db *sqlite3) SupportEngine() bool {
	return false
}

func (db *sqlite3) SupportCharset() bool {
	return false
}

func (db *sqlite3) IndexOnTable() bool {
	return false
}

func (db *sqlite3) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
	args := []interface{}{idxName}
	return "SELECT name FROM sqlite_master WHERE type='index' and name = ?", args
}

func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
	args := []interface{}{tableName}
	return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
}

func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string {
	quote := db.Quote
	//var unique string
	var idxName string = index.Name
	if !strings.HasPrefix(idxName, "UQE_") &&
		!strings.HasPrefix(idxName, "IDX_") {
		if index.Type == core.UniqueType {
			idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
		} else {
			idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
		}
	}
	return fmt.Sprintf("DROP INDEX %v", quote(idxName))
}

/*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
	args := []interface{}{tableName}
	sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
	return sql, args
}*/

func (db *sqlite3) IsColumnExist(tableName string, col *core.Column) (bool, error) {
	args := []interface{}{tableName}
	query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + col.Name + "`%') or (sql like '%[" + col.Name + "]%'))"
	rows, err := db.DB().Query(query, args...)
	if err != nil {
		return false, err
	}
	defer rows.Close()

	if rows.Next() {
		return true, nil
	}
	return false, nil
}

func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
	args := []interface{}{tableName}
	s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"

	rows, err := db.DB().Query(s, args...)
	if err != nil {
		return nil, nil, err
	}
	defer rows.Close()

	var name string
	for rows.Next() {
		err = rows.Scan(&name)
		if err != nil {
			return nil, nil, err
		}
		break
	}

	if name == "" {
		return nil, nil, errors.New("no table named " + tableName)
	}

	nStart := strings.Index(name, "(")
	nEnd := strings.LastIndex(name, ")")
	colCreates := strings.Split(name[nStart+1:nEnd], ",")
	cols := make(map[string]*core.Column)
	colSeq := make([]string, 0)
	for _, colStr := range colCreates {
		fields := strings.Fields(strings.TrimSpace(colStr))
		col := new(core.Column)
		col.Indexes = make(map[string]bool)
		col.Nullable = true
		for idx, field := range fields {
			if idx == 0 {
				col.Name = strings.Trim(field, "`[] ")
				continue
			} else if idx == 1 {
				col.SQLType = core.SQLType{field, 0, 0}
			}
			switch field {
			case "PRIMARY":
				col.IsPrimaryKey = true
			case "AUTOINCREMENT":
				col.IsAutoIncrement = true
			case "NULL":
				if fields[idx-1] == "NOT" {
					col.Nullable = false
				} else {
					col.Nullable = true
				}
			}
		}
		cols[col.Name] = col
		colSeq = append(colSeq, col.Name)
	}
	return colSeq, cols, nil
}

func (db *sqlite3) GetTables() ([]*core.Table, error) {
	args := []interface{}{}
	s := "SELECT name FROM sqlite_master WHERE type='table'"

	rows, err := db.DB().Query(s, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	tables := make([]*core.Table, 0)
	for rows.Next() {
		table := core.NewEmptyTable()
		err = rows.Scan(&table.Name)
		if err != nil {
			return nil, err
		}
		if table.Name == "sqlite_sequence" {
			continue
		}
		tables = append(tables, table)
	}
	return tables, nil
}

func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) {
	args := []interface{}{tableName}
	s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"

	rows, err := db.DB().Query(s, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	indexes := make(map[string]*core.Index, 0)
	for rows.Next() {
		var sql string
		err = rows.Scan(&sql)
		if err != nil {
			return nil, err
		}

		if sql == "" {
			continue
		}

		index := new(core.Index)
		nNStart := strings.Index(sql, "INDEX")
		nNEnd := strings.Index(sql, "ON")
		if nNStart == -1 || nNEnd == -1 {
			continue
		}

		indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
		//fmt.Println(indexName)
		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
			index.Name = indexName[5+len(tableName) : len(indexName)]
		} else {
			index.Name = indexName
		}

		if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
			index.Type = core.UniqueType
		} else {
			index.Type = core.IndexType
		}

		nStart := strings.Index(sql, "(")
		nEnd := strings.Index(sql, ")")
		colIndexes := strings.Split(sql[nStart+1:nEnd], ",")

		index.Cols = make([]string, 0)
		for _, col := range colIndexes {
			index.Cols = append(index.Cols, strings.Trim(col, "` []"))
		}
		indexes[index.Name] = index
	}

	return indexes, nil
}

func (db *sqlite3) Filters() []core.Filter {
	return []core.Filter{&core.IdFilter{}}
}
<|endoftext|>