<commit_before>// Package jwriter contains a JSON writer.
package jwriter

import (
	"encoding/base64"
	"io"
	"strconv"
	"unicode/utf8"

	"github.com/mailru/easyjson/buffer"
)

// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but
// Flags field in Writer is used to set and pass them around.
type Flags int

const (
	NilMapAsEmpty   Flags = 1 << iota // Encode nil map as '{}' rather than 'null'.
	NilSliceAsEmpty                   // Encode nil slice as '[]' rather than 'null'.
)

// Writer is a JSON writer.
type Writer struct {
	Flags Flags

	Error        error
	Buffer       buffer.Buffer
	NoEscapeHTML bool
}

// Size returns the size of the data that was written out.
func (w *Writer) Size() int {
	return w.Buffer.Size()
}

// DumpTo outputs the data to given io.Writer, resetting the buffer.
func (w *Writer) DumpTo(out io.Writer) (written int, err error) {
	return w.Buffer.DumpTo(out)
}

// BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice
// as argument that it will try to reuse.
func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) {
	if w.Error != nil {
		return nil, w.Error
	}

	return w.Buffer.BuildBytes(reuse...), nil
}

// ReadCloser returns an io.ReadCloser that can be used to read the data.
// ReadCloser also resets the buffer.
func (w *Writer) ReadCloser() (io.ReadCloser, error) {
	if w.Error != nil {
		return nil, w.Error
	}

	return w.Buffer.ReadCloser(), nil
}

// RawByte appends raw binary data to the buffer.
func (w *Writer) RawByte(c byte) {
	w.Buffer.AppendByte(c)
}

// RawByte appends raw binary data to the buffer.
func (w *Writer) RawString(s string) {
	w.Buffer.AppendString(s)
}

// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for
// calling with results of MarshalJSON-like functions.
func (w *Writer) Raw(data []byte, err error) {
	switch {
	case w.Error != nil:
		return
	case err != nil:
		w.Error = err
	case len(data) > 0:
		w.Buffer.AppendBytes(data)
	default:
		w.RawString("null")
	}
}

// RawText encloses raw binary data in quotes and appends in to the buffer.
// Useful for calling with results of MarshalText-like functions.
func (w *Writer) RawText(data []byte, err error) {
	switch {
	case w.Error != nil:
		return
	case err != nil:
		w.Error = err
	case len(data) > 0:
		w.Buffer.AppendByte('"')
		w.Buffer.AppendBytes(data)
		w.Buffer.AppendByte('"')
	default:
		w.RawString("null")
	}
}

// Base64Bytes appends data to the buffer after base64 encoding it
func (w *Writer) Base64Bytes(data []byte) {
	if data == nil {
		w.Buffer.AppendString("null")
		return
	}
	w.Buffer.AppendByte('"')
	dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
	base64.StdEncoding.Encode(dst, data)
	w.Buffer.AppendBytes(dst)
	w.Buffer.AppendByte('"')
}

func (w *Writer) Uint8(n uint8) {
	w.Buffer.EnsureSpace(3)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint16(n uint16) {
	w.Buffer.EnsureSpace(5)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint32(n uint32) {
	w.Buffer.EnsureSpace(10)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint(n uint) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint64(n uint64) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
}

func (w *Writer) Int8(n int8) {
	w.Buffer.EnsureSpace(4)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int16(n int16) {
	w.Buffer.EnsureSpace(6)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int32(n int32) {
	w.Buffer.EnsureSpace(11)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int(n int) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int64(n int64) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
}

func (w *Writer) Uint8Str(n uint8) {
	w.Buffer.EnsureSpace(3)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Uint16Str(n uint16) {
	w.Buffer.EnsureSpace(5)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Uint32Str(n uint32) {
	w.Buffer.EnsureSpace(10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) UintStr(n uint) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Uint64Str(n uint64) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int8Str(n int8) {
	w.Buffer.EnsureSpace(4)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int16Str(n int16) {
	w.Buffer.EnsureSpace(6)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int32Str(n int32) {
	w.Buffer.EnsureSpace(11)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) IntStr(n int) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int64Str(n int64) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Float32(n float32) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
}

func (w *Writer) Float64(n float64) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
}

func (w *Writer) Bool(v bool) {
	w.Buffer.EnsureSpace(5)
	if v {
		w.Buffer.Buf = append(w.Buffer.Buf, "true"...)
	} else {
		w.Buffer.Buf = append(w.Buffer.Buf, "false"...)
	}
}

const chars = "0123456789abcdef"

func isNotEscapedSingleChar(c byte, escapeHTML bool) bool {
	// Note: might make sense to use a table if there are more chars to escape. With 4 chars
	// it benchmarks the same.
	if escapeHTML {
		return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
	} else {
		return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
	}
}

func (w *Writer) String(s string) {
	w.Buffer.AppendByte('"')

	// Portions of the string that contain no escapes are appended as
	// byte slices.

	p := 0 // last non-escape symbol

	for i := 0; i < len(s); {
		c := s[i]

		if isNotEscapedSingleChar(c, !w.NoEscapeHTML) {
			// single-width character, no escaping is required
			i++
			continue
		} else if c < utf8.RuneSelf {
			// single-with character, need to escape
			w.Buffer.AppendString(s[p:i])
			switch c {
			case '\t':
				w.Buffer.AppendString(`\t`)
			case '\r':
				w.Buffer.AppendString(`\r`)
			case '\n':
				w.Buffer.AppendString(`\n`)
			case '\\':
				w.Buffer.AppendString(`\\`)
			case '"':
				w.Buffer.AppendString(`\"`)
			default:
				w.Buffer.AppendString(`\u00`)
				w.Buffer.AppendByte(chars[c>>4])
				w.Buffer.AppendByte(chars[c&0xf])
			}

			i++
			p = i
			continue
		}

		// broken utf
		runeValue, runeWidth := utf8.DecodeRuneInString(s[i:])
		if runeValue == utf8.RuneError && runeWidth == 1 {
			w.Buffer.AppendString(s[p:i])
			w.Buffer.AppendString(`\ufffd`)
			i++
			p = i
			continue
		}

		// jsonp stuff - tab separator and line separator
		if runeValue == '\u2028' || runeValue == '\u2029' {
			w.Buffer.AppendString(s[p:i])
			w.Buffer.AppendString(`\u202`)
			w.Buffer.AppendByte(chars[runeValue&0xf])
			i += runeWidth
			p = i
			continue
		}
		i += runeWidth
	}
	w.Buffer.AppendString(s[p:])
	w.Buffer.AppendByte('"')
}
<commit_msg>escape TextMarshaler's results<commit_after>// Package jwriter contains a JSON writer.
package jwriter

import (
	"encoding/base64"
	"io"
	"strconv"
	"unicode/utf8"

	"github.com/mailru/easyjson/buffer"
)

// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but
// Flags field in Writer is used to set and pass them around.
type Flags int

const (
	NilMapAsEmpty   Flags = 1 << iota // Encode nil map as '{}' rather than 'null'.
	NilSliceAsEmpty                   // Encode nil slice as '[]' rather than 'null'.
)

// Writer is a JSON writer.
type Writer struct {
	Flags Flags

	Error        error
	Buffer       buffer.Buffer
	NoEscapeHTML bool
}

// Size returns the size of the data that was written out.
func (w *Writer) Size() int {
	return w.Buffer.Size()
}

// DumpTo outputs the data to given io.Writer, resetting the buffer.
func (w *Writer) DumpTo(out io.Writer) (written int, err error) {
	return w.Buffer.DumpTo(out)
}

// BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice
// as argument that it will try to reuse.
func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) {
	if w.Error != nil {
		return nil, w.Error
	}

	return w.Buffer.BuildBytes(reuse...), nil
}

// ReadCloser returns an io.ReadCloser that can be used to read the data.
// ReadCloser also resets the buffer.
func (w *Writer) ReadCloser() (io.ReadCloser, error) {
	if w.Error != nil {
		return nil, w.Error
	}

	return w.Buffer.ReadCloser(), nil
}

// RawByte appends raw binary data to the buffer.
func (w *Writer) RawByte(c byte) {
	w.Buffer.AppendByte(c)
}

// RawByte appends raw binary data to the buffer.
func (w *Writer) RawString(s string) {
	w.Buffer.AppendString(s)
}

// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for
// calling with results of MarshalJSON-like functions.
func (w *Writer) Raw(data []byte, err error) {
	switch {
	case w.Error != nil:
		return
	case err != nil:
		w.Error = err
	case len(data) > 0:
		w.Buffer.AppendBytes(data)
	default:
		w.RawString("null")
	}
}

// RawText encloses raw binary data in quotes and appends in to the buffer.
// Useful for calling with results of MarshalText-like functions.
func (w *Writer) RawText(data []byte, err error) {
	switch {
	case w.Error != nil:
		return
	case err != nil:
		w.Error = err
	case len(data) > 0:
		w.String(string(data))
	default:
		w.RawString("null")
	}
}

// Base64Bytes appends data to the buffer after base64 encoding it
func (w *Writer) Base64Bytes(data []byte) {
	if data == nil {
		w.Buffer.AppendString("null")
		return
	}
	w.Buffer.AppendByte('"')
	dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
	base64.StdEncoding.Encode(dst, data)
	w.Buffer.AppendBytes(dst)
	w.Buffer.AppendByte('"')
}

func (w *Writer) Uint8(n uint8) {
	w.Buffer.EnsureSpace(3)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint16(n uint16) {
	w.Buffer.EnsureSpace(5)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint32(n uint32) {
	w.Buffer.EnsureSpace(10)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint(n uint) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
}

func (w *Writer) Uint64(n uint64) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
}

func (w *Writer) Int8(n int8) {
	w.Buffer.EnsureSpace(4)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int16(n int16) {
	w.Buffer.EnsureSpace(6)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int32(n int32) {
	w.Buffer.EnsureSpace(11)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int(n int) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
}

func (w *Writer) Int64(n int64) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
}

func (w *Writer) Uint8Str(n uint8) {
	w.Buffer.EnsureSpace(3)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Uint16Str(n uint16) {
	w.Buffer.EnsureSpace(5)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Uint32Str(n uint32) {
	w.Buffer.EnsureSpace(10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) UintStr(n uint) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Uint64Str(n uint64) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int8Str(n int8) {
	w.Buffer.EnsureSpace(4)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int16Str(n int16) {
	w.Buffer.EnsureSpace(6)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int32Str(n int32) {
	w.Buffer.EnsureSpace(11)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) IntStr(n int) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Int64Str(n int64) {
	w.Buffer.EnsureSpace(21)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
	w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
	w.Buffer.Buf = append(w.Buffer.Buf, '"')
}

func (w *Writer) Float32(n float32) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
}

func (w *Writer) Float64(n float64) {
	w.Buffer.EnsureSpace(20)
	w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
}

func (w *Writer) Bool(v bool) {
	w.Buffer.EnsureSpace(5)
	if v {
		w.Buffer.Buf = append(w.Buffer.Buf, "true"...)
	} else {
		w.Buffer.Buf = append(w.Buffer.Buf, "false"...)
	}
}

const chars = "0123456789abcdef"

func isNotEscapedSingleChar(c byte, escapeHTML bool) bool {
	// Note: might make sense to use a table if there are more chars to escape. With 4 chars
	// it benchmarks the same.
	if escapeHTML {
		return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
	} else {
		return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
	}
}

func (w *Writer) String(s string) {
	w.Buffer.AppendByte('"')

	// Portions of the string that contain no escapes are appended as
	// byte slices.

	p := 0 // last non-escape symbol

	for i := 0; i < len(s); {
		c := s[i]

		if isNotEscapedSingleChar(c, !w.NoEscapeHTML) {
			// single-width character, no escaping is required
			i++
			continue
		} else if c < utf8.RuneSelf {
			// single-with character, need to escape
			w.Buffer.AppendString(s[p:i])
			switch c {
			case '\t':
				w.Buffer.AppendString(`\t`)
			case '\r':
				w.Buffer.AppendString(`\r`)
			case '\n':
				w.Buffer.AppendString(`\n`)
			case '\\':
				w.Buffer.AppendString(`\\`)
			case '"':
				w.Buffer.AppendString(`\"`)
			default:
				w.Buffer.AppendString(`\u00`)
				w.Buffer.AppendByte(chars[c>>4])
				w.Buffer.AppendByte(chars[c&0xf])
			}

			i++
			p = i
			continue
		}

		// broken utf
		runeValue, runeWidth := utf8.DecodeRuneInString(s[i:])
		if runeValue == utf8.RuneError && runeWidth == 1 {
			w.Buffer.AppendString(s[p:i])
			w.Buffer.AppendString(`\ufffd`)
			i++
			p = i
			continue
		}

		// jsonp stuff - tab separator and line separator
		if runeValue == '\u2028' || runeValue == '\u2029' {
			w.Buffer.AppendString(s[p:i])
			w.Buffer.AppendString(`\u202`)
			w.Buffer.AppendByte(chars[runeValue&0xf])
			i += runeWidth
			p = i
			continue
		}
		i += runeWidth
	}
	w.Buffer.AppendString(s[p:])
	w.Buffer.AppendByte('"')
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
)

func ping() {
	log.Println(">>> got ping")

	res, err := http.Post(fmt.Sprintf("%s/api/255/send", origin), "", nil)
	if err != nil {
		log.Println(err)
	} else if err = res.Body.Close(); err != nil {
		log.Println(err)
	} else {
		log.Println(">>> sent pong")
	}
}
<commit_msg>Fixes ping function.<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
)

func ping() error {
	log.Println(">>> got ping")

	res, err := http.Post(fmt.Sprintf("%s/api/255/send", origin), "", nil)
	if err != nil {
		return err
	} else if err = res.Body.Close(); err != nil {
		return err
	}
	log.Println(">>> sent pong")
	return nil
}
<|endoftext|>


<commit_before>package app

import (
	"crypto/sha1"
	"encoding/hex"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/fragmenta/server"
	"github.com/fragmenta/server/config"
	"github.com/fragmenta/server/log"
	"github.com/fragmenta/view"
)

// Serve static files (assets, images etc)
func fileHandler(w http.ResponseWriter, r *http.Request) error {

	// First try serving assets
	err := serveAsset(w, r)
	if err == nil {
		return nil
	}

	// If assets fail, try to serve file in public
	return serveFile(w, r)
}

// serveFile serves a file from ./public if it exists
func serveFile(w http.ResponseWriter, r *http.Request) error {

	// Try a local path in the public directory
	localPath := "./public" + path.Clean(r.URL.Path)
	s, err := os.Stat(localPath)
	if err != nil {
		// If file not found return 404
		if os.IsNotExist(err) {
			return server.NotFoundError(err)
		}

		// For other errors return not authorised
		return server.NotAuthorizedError(err)
	}

	// If not a file return immediately
	if s.IsDir() {
		return nil
	}

	// If the file exists and we can access it, serve it with cache control
	// if in production
	if config.Production() {
		// Cache for 30 days
		w.Header().Set("Cache-Control", "max-age:2592000, public")
		// For etag Just hash the path - static resources are assumed to have a fingerprint
		w.Header().Set("ETag", hash(r.URL.Path))
	}

	http.ServeFile(w, r, localPath)
	return nil
}

// hash returns the sha hash of a string
func hash(s string) string {
	sum := sha1.Sum([]byte(s))
	return hex.EncodeToString([]byte(sum[:]))
}

// serveAsset serves a file from ./public/assets usings appAssets
func serveAsset(w http.ResponseWriter, r *http.Request) error {

	p := path.Clean(r.URL.Path)

	// It must be under /assets, or we don't serve
	if !strings.HasPrefix(p, "/assets/") {
		return server.NotFoundError(nil)
	}

	// Try to find an asset in our list
	f := appAssets.File(path.Base(p))
	if f == nil {
		return server.NotFoundError(nil)
	}

	// Serve the local file, with cache control
	localPath := "./" + f.LocalPath()
	// If the file exists and we can access it, serve it with cache control in production
	if config.Production() {
		// Cache for 30 days
		w.Header().Set("Cache-Control", "max-age:2592000, public")
		// For etag Just hash the path - static resources are assumed to have a fingerprint
		w.Header().Set("ETag", hash(r.URL.Path))
	}
	http.ServeFile(w, r, localPath)
	return nil
}

// errHandler renders an error using error templates if available
func errHandler(w http.ResponseWriter, r *http.Request, e error) {

	// Cast the error to a status error if it is one, if not wrap it in a Status 500 error
	err := server.ToStatusError(e)
	log.Error(log.V{"error": err})

	view := view.NewWithPath("", w)
	view.AddKey("title", err.Title)
	view.AddKey("message", err.Message)
	// In production, provide no detail for security reasons
	if !config.Production() {
		view.AddKey("status", err.Status)
		view.AddKey("file", err.FileLine())
		view.AddKey("error", err.Err)
	}
	view.Template("app/views/error.html.got")
	w.WriteHeader(err.Status)
	view.Render()
}
<commit_msg>Removed redundant public, add quotes around etag headers<commit_after>package app

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/fragmenta/server"
	"github.com/fragmenta/server/config"
	"github.com/fragmenta/server/log"
	"github.com/fragmenta/view"
)

// Serve static files (assets, images etc)
func fileHandler(w http.ResponseWriter, r *http.Request) error {

	// First try serving assets
	err := serveAsset(w, r)
	if err == nil {
		return nil
	}

	// If assets fail, try to serve file in public
	return serveFile(w, r)
}

// serveFile serves a file from ./public if it exists
func serveFile(w http.ResponseWriter, r *http.Request) error {

	// Try a local path in the public directory
	localPath := "./public" + path.Clean(r.URL.Path)
	s, err := os.Stat(localPath)
	if err != nil {
		// If file not found return 404
		if os.IsNotExist(err) {
			return server.NotFoundError(err)
		}

		// For other errors return not authorised
		return server.NotAuthorizedError(err)
	}

	// If not a file return immediately
	if s.IsDir() {
		return nil
	}

	// If the file exists and we can access it, serve it with cache control
	// if in production
	if config.Production() {
		// Cache for 30 days
		w.Header().Set("Cache-Control", "max-age:2592000")
		// For etag Just hash the path - static resources are assumed to have a fingerprint
		w.Header().Set("ETag", fmt.Sprintf("\"%s\"", hash(r.URL.Path)))
	}

	http.ServeFile(w, r, localPath)
	return nil
}

// hash returns the sha hash of a string
func hash(s string) string {
	sum := sha1.Sum([]byte(s))
	return hex.EncodeToString([]byte(sum[:]))
}

// serveAsset serves a file from ./public/assets usings appAssets
func serveAsset(w http.ResponseWriter, r *http.Request) error {

	p := path.Clean(r.URL.Path)

	// It must be under /assets, or we don't serve
	if !strings.HasPrefix(p, "/assets/") {
		return server.NotFoundError(nil)
	}

	// Try to find an asset in our list
	f := appAssets.File(path.Base(p))
	if f == nil {
		return server.NotFoundError(nil)
	}

	// Serve the local file, with cache control
	localPath := "./" + f.LocalPath()
	// If the file exists and we can access it, serve it with cache control in production
	if config.Production() {
		// Cache for 30 days
		w.Header().Set("Cache-Control", "max-age:2592000")
		// For etag Just hash the path - static resources are assumed to have a fingerprint
		w.Header().Set("ETag", fmt.Sprintf("\"%s\"", hash(r.URL.Path)))
	}
	http.ServeFile(w, r, localPath)
	return nil
}

// errHandler renders an error using error templates if available
func errHandler(w http.ResponseWriter, r *http.Request, e error) {

	// Cast the error to a status error if it is one, if not wrap it in a Status 500 error
	err := server.ToStatusError(e)
	log.Error(log.V{"error": err})

	view := view.NewWithPath("", w)
	view.AddKey("title", err.Title)
	view.AddKey("message", err.Message)
	// In production, provide no detail for security reasons
	if !config.Production() {
		view.AddKey("status", err.Status)
		view.AddKey("file", err.FileLine())
		view.AddKey("error", err.Err)
	}
	view.Template("app/views/error.html.got")
	w.WriteHeader(err.Status)
	view.Render()
}
<|endoftext|>


<commit_before>package main

import (
	"conui"
	"core"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/gizak/termui"
	"github.com/nsf/termbox-go"
)

func NewSyncCommand() cli.Command {
	return cli.Command{
		Name:  "sync",
		Usage: "Synchronize files to devices",
		Action: func(c *cli.Context) {
			err := checkEnvVariables(c)
			if err != nil {
				panic(fatal{fmt.Sprintf("Could not set environment variables: %s", err)})
			}
			if !c.GlobalBool("no-file-log") {
				lp := cleanPath(c.GlobalString("log"))
				var err error
				GDS_LOG_FD, err = os.Create(lp)
				if err != nil {
					panic(fatal{fmt.Sprintf("Could not create log file: %s", err)})
				}
				log.Out = GDS_LOG_FD
			}
			lvl, err := logrus.ParseLevel(c.GlobalString("log-level"))
			if err != nil {
				panic(fatalShowHelp{fmt.Sprintf("Error parsing log level: %s", err)})
			}
			log.Level = lvl
			syncStart(c)
		},
	}
}

// loadInitialState prepares the applicaton for usage
func loadInitialState(c *cli.Context) *core.Context {
	cPath, err := getConfigFile(c.GlobalString("config"))
	if err != nil {
		panic(fatal{err})
	}
	log.WithFields(logrus.Fields{
		"path": cPath,
	}).Info("Using configuration file")

	c2, err := core.ContextFromPath(cPath)
	if err != nil {
		panic(fatal{fmt.Sprintf("Error loading config: %s", err.Error())})
	}

	c2.Files, err = core.NewFileList(c2)
	if err != nil {
		panic(fatal{fmt.Sprintf("Error retrieving FileList %s", err.Error())})
	}

	c2.Catalog, err = core.NewCatalog(c2)
	if err != nil {
		panic(fatal{err})
	}

	return c2
}

func dumpContextToFile(c *cli.Context, c2 *core.Context) {
	cf, err := getContextFile(c.GlobalString("context"))
	if err != nil {
		panic(fatal{fmt.Sprintf("Could not create context JSON output file: %s", err.Error())})
	}
	j, err := json.Marshal(c2)
	if err == nil {
		err = ioutil.WriteFile(cf, j, 0644)
	}
	if err != nil {
		panic(fatal{fmt.Sprintf("Could not marshal JSON to file: %s", err.Error())})
	}
}

// BuildConsole creates the UI widgets First is the main progress guage for the overall progress Widgets are then created for
// each of the devices, but are hidden initially.
func BuildConsole(c *core.Context) {
	var rows []termui.GridBufferer
	visible := c.OutputStreamNum
	for x, y := range c.Devices {
		conui.Widgets[x] = conui.NewDevicePanel(y.Name, y.SizeTotal)
		if visible > 0 {
			conui.Widgets[x].(*conui.DevicePanel).Visible = true
			if x == 0 {
				conui.Widgets[x].(*conui.DevicePanel).Selected = true
			}
			visible--
		}
	}
	conui.Widgets[len(c.Devices)] = conui.NewProgressGauge(c.Devices.DevicePoolSize())
	rows = append(rows, conui.Widgets[len(c.Devices)])
	for x, _ := range c.Devices {
		rows = append(rows, conui.Widgets[x].(*conui.DevicePanel))
	}
	// log.Debugln(spd.Sdump(rows))
	termui.Body.AddRows(
		termui.NewRow(
			termui.NewCol(12, 0, rows...),
		),
	)
	termui.Body.Align()
}

func eventListener(c *core.Context) {
	defer cleanupAtExit()
	for {
		select {
		case e := <-conui.Event:
			if e.Type == termui.EventKey && e.Ch == 'j' {
				conui.Widgets.SelectNext()
			}
			if e.Type == termui.EventKey && e.Ch == 'k' {
				conui.Widgets.SelectPrevious()
			}
			if e.Type == termui.EventKey && e.Key == termui.KeyEnter {
				conui.Widgets.Selected().Prompt = conui.Prompt{}
			}
			if e.Type == termui.EventKey && e.Ch == 'q' {
				conui.Close()
				os.Exit(0)
			}
			if e.Type == termui.EventResize {
				termui.Body.Width = termui.TermWidth()
				termui.Body.Align()
				go func() { conui.Redraw <- true }()
			}
		case <-conui.Redraw:
			termui.Render(termui.Body)
		}
	}
}

func update(c *core.Context) {
	for x := 0; x < len(c.Devices); x++ {
		c.SyncDeviceMount[x] = make(chan bool)
		go func(index int) {
			<-c.SyncDeviceMount[index]
			wg := conui.Widgets.MountPromptByIndex(index)
			d, err := c.Devices.DeviceByName(wg.Border.Label)
			if err != nil {
				log.Error(err)
			}
			err = ensureDeviceIsMounted(*d)
			if err != nil {
				log.Error(err)
			}
			c.SyncDeviceMount[index] <- true
		}(x)
		c.SyncProgress[x] = make(chan core.SyncProgress, 100)
		c.SyncFileProgress[x] = make(chan core.SyncFileProgress, 100)
		go func(index int) {
			// defer cleanupAtExit()
			for {
				select {
				case <-c.SyncProgress[index]:
				case <-c.SyncFileProgress[index]:
				}
			}
		}(x)
	}
	for {
		conui.Redraw <- true
		time.Sleep(time.Second / 5)
	}
}

func syncStart(c *cli.Context) {
	defer cleanupAtExit()
	log.WithFields(logrus.Fields{
		"version": 0.2,
		"date":    time.Now().Format(time.RFC3339),
	}).Infoln("Ghetto Device Storage")
	c2 := loadInitialState(c)

	// CONSOLE UI FROM THE 1980s
	conui.Init()
	BuildConsole(c2)
	go eventListener(c2)
	go update(c2)
	// Sync the things
	errs := core.Sync(c2, c.GlobalBool("no-dev-context"))
	if len(errs) > 0 {
		for _, e := range errs {
			log.Errorf("Sync error: %s", e.Error())
		}
	}

	// Fin
	dumpContextToFile(c, c2)
	log.Info("ALL DONE -- Sync complete!")
}
<commit_msg>cmd/sync.go: Fix SyncDeviceMount channel init<commit_after>package main

import (
	"conui"
	"core"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/gizak/termui"
	"github.com/nsf/termbox-go"
)

func NewSyncCommand() cli.Command {
	return cli.Command{
		Name:  "sync",
		Usage: "Synchronize files to devices",
		Action: func(c *cli.Context) {
			err := checkEnvVariables(c)
			if err != nil {
				panic(fatal{fmt.Sprintf("Could not set environment variables: %s", err)})
			}
			if !c.GlobalBool("no-file-log") {
				lp := cleanPath(c.GlobalString("log"))
				var err error
				GDS_LOG_FD, err = os.Create(lp)
				if err != nil {
					panic(fatal{fmt.Sprintf("Could not create log file: %s", err)})
				}
				log.Out = GDS_LOG_FD
			}
			lvl, err := logrus.ParseLevel(c.GlobalString("log-level"))
			if err != nil {
				panic(fatalShowHelp{fmt.Sprintf("Error parsing log level: %s", err)})
			}
			log.Level = lvl
			syncStart(c)
		},
	}
}

// loadInitialState prepares the applicaton for usage
func loadInitialState(c *cli.Context) *core.Context {
	cPath, err := getConfigFile(c.GlobalString("config"))
	if err != nil {
		panic(fatal{err})
	}
	log.WithFields(logrus.Fields{
		"path": cPath,
	}).Info("Using configuration file")

	c2, err := core.ContextFromPath(cPath)
	if err != nil {
		panic(fatal{fmt.Sprintf("Error loading config: %s", err.Error())})
	}

	c2.Files, err = core.NewFileList(c2)
	if err != nil {
		panic(fatal{fmt.Sprintf("Error retrieving FileList %s", err.Error())})
	}

	c2.Catalog, err = core.NewCatalog(c2)
	if err != nil {
		panic(fatal{err})
	}

	return c2
}

func dumpContextToFile(c *cli.Context, c2 *core.Context) {
	cf, err := getContextFile(c.GlobalString("context"))
	if err != nil {
		panic(fatal{fmt.Sprintf("Could not create context JSON output file: %s", err.Error())})
	}
	j, err := json.Marshal(c2)
	if err == nil {
		err = ioutil.WriteFile(cf, j, 0644)
	}
	if err != nil {
		panic(fatal{fmt.Sprintf("Could not marshal JSON to file: %s", err.Error())})
	}
}

// BuildConsole creates the UI widgets First is the main progress guage for the overall progress Widgets are then created for
// each of the devices, but are hidden initially.
func BuildConsole(c *core.Context) {
	var rows []termui.GridBufferer
	visible := c.OutputStreamNum
	for x, y := range c.Devices {
		conui.Widgets[x] = conui.NewDevicePanel(y.Name, y.SizeTotal)
		if visible > 0 {
			conui.Widgets[x].(*conui.DevicePanel).Visible = true
			if x == 0 {
				conui.Widgets[x].(*conui.DevicePanel).Selected = true
			}
			visible--
		}
	}
	conui.Widgets[len(c.Devices)] = conui.NewProgressGauge(c.Devices.DevicePoolSize())
	rows = append(rows, conui.Widgets[len(c.Devices)])
	for x, _ := range c.Devices {
		rows = append(rows, conui.Widgets[x].(*conui.DevicePanel))
	}
	// log.Debugln(spd.Sdump(rows))
	termui.Body.AddRows(
		termui.NewRow(
			termui.NewCol(12, 0, rows...),
		),
	)
	termui.Body.Align()
}

func eventListener(c *core.Context) {
	defer cleanupAtExit()
	for {
		select {
		case e := <-conui.Event:
			if e.Type == termui.EventKey && e.Ch == 'j' {
				conui.Widgets.SelectNext()
			}
			if e.Type == termui.EventKey && e.Ch == 'k' {
				conui.Widgets.SelectPrevious()
			}
			if e.Type == termui.EventKey && e.Key == termui.KeyEnter {
				conui.Widgets.Selected().Prompt = conui.Prompt{}
			}
			if e.Type == termui.EventKey && e.Ch == 'q' {
				conui.Close()
				os.Exit(0)
			}
			if e.Type == termui.EventResize {
				termui.Body.Width = termui.TermWidth()
				termui.Body.Align()
				go func() { conui.Redraw <- true }()
			}
		case <-conui.Redraw:
			termui.Render(termui.Body)
		}
	}
}

func update(c *core.Context) {
	for x := 0; x < len(c.Devices); x++ {
		c.SyncDeviceMount[x] = make(chan bool)
		go func(index int) {
			ns := time.Now()
			log.Debugln("Waiting for receive on SyncDeviceMount")
			<-c.SyncDeviceMount[index]
			log.Debugf("Receive from SyncDeviceMount after wait of %s", time.Since(ns))
			wg := conui.Widgets.MountPromptByIndex(index)
			d, err := c.Devices.DeviceByName(wg.Border.Label)
			if err != nil {
				log.Error(err)
			}
			err = ensureDeviceIsMounted(*d)
			if err != nil {
				log.Error(err)
			}
			c.SyncDeviceMount[index] <- true
		}(x)
		c.SyncProgress[x] = make(chan core.SyncProgress, 100)
		c.SyncFileProgress[x] = make(chan core.SyncFileProgress, 100)
		go func(index int) {
			// defer cleanupAtExit()
			for {
				select {
				case <-c.SyncProgress[index]:
				case <-c.SyncFileProgress[index]:
				}
			}
		}(x)
	}
	go func() {
		for {
			if !termbox.IsInit {
				break
			}
			conui.Redraw <- true
			time.Sleep(time.Second / 5)
		}
	}()
}

func syncStart(c *cli.Context) {
	defer cleanupAtExit()
	log.WithFields(logrus.Fields{
		"version": 0.2,
		"date":    time.Now().Format(time.RFC3339),
	}).Infoln("Ghetto Device Storage")
	c2 := loadInitialState(c)

	// CONSOLE UI FROM THE 1980s
	conui.Init()
	BuildConsole(c2)
	go eventListener(c2)
	update(c2)

	// Sync the things
	errs := core.Sync(c2, c.GlobalBool("no-dev-context"))
	if len(errs) > 0 {
		for _, e := range errs {
			log.Errorf("Sync error: %s", e.Error())
		}
	}

	// Fin
	dumpContextToFile(c, c2)
	log.Info("ALL DONE -- Sync complete!")
}
<|endoftext|>


<commit_before>package pixiecore

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const X86_HTTP_CLIENT = 0x10

// StaticBootConfiguration provides values for dhcp options that remain unchanged until restart
type StaticBootConfiguration struct {
	HTTPBootURL   []byte
	IPxeBootURL   []byte
	RecursiveDNS  []net.IP
	Preference    []byte
	UsePreference bool
}

// MakeStaticBootConfiguration creates a new StaticBootConfiguration with provided values
func MakeStaticBootConfiguration(httpBootURL, ipxeBootURL string, preference uint8, usePreference bool,
	dnsServerAddresses []net.IP) *StaticBootConfiguration {
	ret := &StaticBootConfiguration{HTTPBootURL: []byte(httpBootURL), IPxeBootURL: []byte(ipxeBootURL), UsePreference: usePreference}
	if usePreference {
		ret.Preference = make([]byte, 1)
		ret.Preference[0] = byte(preference)
	}
	ret.RecursiveDNS = dnsServerAddresses
	return ret
}

// GetBootURL returns Boot File URL, see RFC 5970
func (bc *StaticBootConfiguration) GetBootURL(id []byte, clientArchType uint16) ([]byte, error) {
	if clientArchType == X86_HTTP_CLIENT {
		return bc.HTTPBootURL, nil
	}
	return bc.IPxeBootURL, nil
}

// GetPreference returns server's Preference, see RFC 3315
func (bc *StaticBootConfiguration) GetPreference() []byte {
	return bc.Preference
}

// GetRecursiveDNS returns list of addresses of recursive DNS servers, see RFC 3646
func (bc *StaticBootConfiguration) GetRecursiveDNS() []net.IP {
	return bc.RecursiveDNS
}

// APIBootConfiguration provides an interface to retrieve Boot File URL from an external server based on
// client ID and architecture type
type APIBootConfiguration struct {
	Client        *http.Client
	URLPrefix     string
	RecursiveDNS  []net.IP
	Preference    []byte
	UsePreference bool
}

// MakeAPIBootConfiguration creates a new APIBootConfiguration initialized with provided values
func MakeAPIBootConfiguration(url string, timeout time.Duration, preference uint8, usePreference bool,
	dnsServerAddresses []net.IP) *APIBootConfiguration {
	if !strings.HasSuffix(url, "/") {
		url += "/"
	}
	ret := &APIBootConfiguration{
		Client:        &http.Client{Timeout: timeout},
		URLPrefix:     url + "v1",
		UsePreference: usePreference,
	}
	if usePreference {
		ret.Preference = make([]byte, 1)
		ret.Preference[0] = byte(preference)
	}
	ret.RecursiveDNS = dnsServerAddresses

	return ret
}

// GetBootURL returns Boot File URL, see RFC 5970
func (bc *APIBootConfiguration) GetBootURL(id []byte, clientArchType uint16) ([]byte, error) {
	reqURL := fmt.Sprintf("%s/boot/%x/%d", bc.URLPrefix, id, clientArchType)
	resp, err := bc.Client.Get(reqURL)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("%s: %s", reqURL, http.StatusText(resp.StatusCode))
	}
	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)
	url, _ := bc.makeURLAbsolute(buf.String())

	return []byte(url), nil
}

func (bc *APIBootConfiguration) makeURLAbsolute(urlStr string) (string, error) {
	u, err := url.Parse(urlStr)
	if err != nil {
		return "", fmt.Errorf("%q is not an URL", urlStr)
	}
	if !u.IsAbs() {
		base, err := url.Parse(bc.URLPrefix)
		if err != nil {
			return "", err
		}
		u = base.ResolveReference(u)
	}
	return u.String(), nil
}

// GetPreference returns server's Preference, see RFC 3315
func (bc *APIBootConfiguration) GetPreference() []byte {
	return bc.Preference
}

// GetRecursiveDNS returns list of addresses of recursive DNS servers, see RFC 3646
func (bc *APIBootConfiguration) GetRecursiveDNS() []net.IP {
	return bc.RecursiveDNS
}
<commit_msg>Rename X86_HTTP_CLIENT to appease golint.<commit_after>package pixiecore

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const x86HTTPClient = 0x10

// StaticBootConfiguration provides values for dhcp options that remain unchanged until restart
type StaticBootConfiguration struct {
	HTTPBootURL   []byte
	IPxeBootURL   []byte
	RecursiveDNS  []net.IP
	Preference    []byte
	UsePreference bool
}

// MakeStaticBootConfiguration creates a new StaticBootConfiguration with provided values
func MakeStaticBootConfiguration(httpBootURL, ipxeBootURL string, preference uint8, usePreference bool,
	dnsServerAddresses []net.IP) *StaticBootConfiguration {
	ret := &StaticBootConfiguration{HTTPBootURL: []byte(httpBootURL), IPxeBootURL: []byte(ipxeBootURL), UsePreference: usePreference}
	if usePreference {
		ret.Preference = make([]byte, 1)
		ret.Preference[0] = byte(preference)
	}
	ret.RecursiveDNS = dnsServerAddresses
	return ret
}

// GetBootURL returns Boot File URL, see RFC 5970
func (bc *StaticBootConfiguration) GetBootURL(id []byte, clientArchType uint16) ([]byte, error) {
	if clientArchType == x86HTTPClient {
		return bc.HTTPBootURL, nil
	}
	return bc.IPxeBootURL, nil
}

// GetPreference returns server's Preference, see RFC 3315
func (bc *StaticBootConfiguration) GetPreference() []byte {
	return bc.Preference
}

// GetRecursiveDNS returns list of addresses of recursive DNS servers, see RFC 3646
func (bc *StaticBootConfiguration) GetRecursiveDNS() []net.IP {
	return bc.RecursiveDNS
}

// APIBootConfiguration provides an interface to retrieve Boot File URL from an external server based on
// client ID and architecture type
type APIBootConfiguration struct {
	Client        *http.Client
	URLPrefix     string
	RecursiveDNS  []net.IP
	Preference    []byte
	UsePreference bool
}

// MakeAPIBootConfiguration creates a new APIBootConfiguration initialized with provided values
func MakeAPIBootConfiguration(url string, timeout time.Duration, preference uint8, usePreference bool,
	dnsServerAddresses []net.IP) *APIBootConfiguration {
	if !strings.HasSuffix(url, "/") {
		url += "/"
	}
	ret := &APIBootConfiguration{
		Client:        &http.Client{Timeout: timeout},
		URLPrefix:     url + "v1",
		UsePreference: usePreference,
	}
	if usePreference {
		ret.Preference = make([]byte, 1)
		ret.Preference[0] = byte(preference)
	}
	ret.RecursiveDNS = dnsServerAddresses

	return ret
}

// GetBootURL returns Boot File URL, see RFC 5970
func (bc *APIBootConfiguration) GetBootURL(id []byte, clientArchType uint16) ([]byte, error) {
	reqURL := fmt.Sprintf("%s/boot/%x/%d", bc.URLPrefix, id, clientArchType)
	resp, err := bc.Client.Get(reqURL)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("%s: %s", reqURL, http.StatusText(resp.StatusCode))
	}
	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)
	url, _ := bc.makeURLAbsolute(buf.String())

	return []byte(url), nil
}

func (bc *APIBootConfiguration) makeURLAbsolute(urlStr string) (string, error) {
	u, err := url.Parse(urlStr)
	if err != nil {
		return "", fmt.Errorf("%q is not an URL", urlStr)
	}
	if !u.IsAbs() {
		base, err := url.Parse(bc.URLPrefix)
		if err != nil {
			return "", err
		}
		u = base.ResolveReference(u)
	}
	return u.String(), nil
}

// GetPreference returns server's Preference, see RFC 3315
func (bc *APIBootConfiguration) GetPreference() []byte {
	return bc.Preference
}

// GetRecursiveDNS returns list of addresses of recursive DNS servers, see RFC 3646
func (bc *APIBootConfiguration) GetRecursiveDNS() []net.IP {
	return bc.RecursiveDNS
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/golang/glog"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1"

	kubernikus_v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
)

type SharedClientFactory struct {
	clients          *sync.Map
	secretsInterface typedv1.SecretInterface
}

func NewSharedClientFactory(secrets typedv1.SecretInterface, klusterEvents cache.SharedIndexInformer) *SharedClientFactory {
	factory := &SharedClientFactory{
		clients:          new(sync.Map),
		secretsInterface: secrets,
	}

	if klusterEvents != nil {
		klusterEvents.AddEventHandler(cache.ResourceEventHandlerFuncs{
			DeleteFunc: func(obj interface{}) {
				if kluster, ok := obj.(*kubernikus_v1.Kluster); ok {
					glog.V(5).Info("Deleting shared kubernetes client for kluster %s", kluster.GetName())
					factory.clients.Delete(kluster.GetUID())
				}
			},
		})
	}

	return factory
}

func (f *SharedClientFactory) ClientFor(k *kubernikus_v1.Kluster) (kubernetes.Interface, error) {
	if client, found := f.clients.Load(k.GetUID()); found {
		return client.(kubernetes.Interface), nil
	}
	glog.V(5).Info("Creating new shared kubernetes client for kluster %s", k.GetName())
	secret, err := f.secretsInterface.Get(k.GetName(), metav1.GetOptions{})
	if err != nil {
		return nil, err
	}
	apiserverCACert, ok := secret.Data["tls-ca.pem"]
	if !ok {
		return nil, errors.New("tls-ca.pem not found in secret")
	}
	clientCert, ok := secret.Data["apiserver-clients-cluster-admin.pem"]
	if !ok {
		return nil, errors.New("tls-ca.pem not found in secret")
	}
	clientKey, ok := secret.Data["apiserver-clients-cluster-admin-key.pem"]
	if !ok {
		return nil, errors.New("tls-ca.pem not found in secret")
	}

	c := rest.Config{
		Host: k.Spec.KubernikusInfo.ServerURL,
		TLSClientConfig: rest.TLSClientConfig{
			CertData: clientCert,
			KeyData:  clientKey,
			CAData:   apiserverCACert,
		},
	}

	clientset, err := kubernetes.NewForConfig(&c)
	if err != nil {
		return nil, err
	}

	f.clients.Store(k.GetUID(), clientset)
	return clientset, nil

}

func NewConfig(kubeconfig, context string) (*rest.Config, error) {
	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	overrides := &clientcmd.ConfigOverrides{CurrentContext: context}

	if len(kubeconfig) > 0 {
		rules.ExplicitPath = kubeconfig
	}

	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig()
	if err != nil {
		glog.Fatalf("Couldn't get Kubernetes default config: %s", err)
	}

	return config, nil
}

func NewClient(kubeconfig, context string) (kubernetes.Interface, error) {
	config, err := NewConfig(kubeconfig, context)
	if err != nil {
		return nil, err
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Using Kubernetes Api at %s", config.Host)

	return clientset, nil
}

func NewClientConfigV1(name, user, url string, key, cert, ca []byte) clientcmdapiv1.Config {
	return clientcmdapiv1.Config{
		APIVersion:     "v1",
		Kind:           "Config",
		CurrentContext: name,
		Clusters: []clientcmdapiv1.NamedCluster{
			clientcmdapiv1.NamedCluster{
				Name: name,
				Cluster: clientcmdapiv1.Cluster{
					Server: url,
					CertificateAuthorityData: ca,
				},
			},
		},
		Contexts: []clientcmdapiv1.NamedContext{
			clientcmdapiv1.NamedContext{
				Name: name,
				Context: clientcmdapiv1.Context{
					Cluster:  name,
					AuthInfo: user,
				},
			},
		},
		AuthInfos: []clientcmdapiv1.NamedAuthInfo{
			clientcmdapiv1.NamedAuthInfo{
				Name: user,
				AuthInfo: clientcmdapiv1.AuthInfo{
					ClientCertificateData: cert,
					ClientKeyData:         key,
				},
			},
		},
	}
}

func EnsureTPR(clientset kubernetes.Interface) error {
	tpr := &v1beta1.ThirdPartyResource{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kluster." + kubernikus_v1.GroupName,
		},
		Versions: []v1beta1.APIVersion{
			{Name: v1.SchemeGroupVersion.Version},
		},
		Description: "Managed kubernetes cluster",
	}

	_, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Create(tpr)
	if err != nil && !apierrors.IsAlreadyExists(err) {
		return err
	}
	return nil
}

func WaitForTPR(clientset kubernetes.Interface) error {
	return wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) {
		_, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Get("kluster."+kubernikus_v1.GroupName, metav1.GetOptions{})
		if err == nil {
			return true, nil
		}
		if apierrors.IsNotFound(err) {
			return false, nil
		}
		return false, err
	})
}

func WaitForServer(client kubernetes.Interface, stopCh <-chan struct{}) error {
	var healthzContent string

	err := wait.PollUntil(time.Second, func() (bool, error) {
		healthStatus := 0
		resp := client.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
		if healthStatus != http.StatusOK {
			glog.Errorf("Server isn't healthy yet.  Waiting a little while.")
			return false, nil
		}
		content, _ := resp.Raw()
		healthzContent = string(content)

		return true, nil
	}, stopCh)
	if err != nil {
		return fmt.Errorf("Failed to contact apiserver. Last health: %v  Error: %v", healthzContent, err)
	}

	return nil
}
<commit_msg>allows to ommit kubeconfig context<commit_after>package kubernetes

import (
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/golang/glog"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1"

	kubernikus_v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
)

type SharedClientFactory struct {
	clients          *sync.Map
	secretsInterface typedv1.SecretInterface
}

func NewSharedClientFactory(secrets typedv1.SecretInterface, klusterEvents cache.SharedIndexInformer) *SharedClientFactory {
	factory := &SharedClientFactory{
		clients:          new(sync.Map),
		secretsInterface: secrets,
	}

	if klusterEvents != nil {
		klusterEvents.AddEventHandler(cache.ResourceEventHandlerFuncs{
			DeleteFunc: func(obj interface{}) {
				if kluster, ok := obj.(*kubernikus_v1.Kluster); ok {
					glog.V(5).Info("Deleting shared kubernetes client for kluster %s", kluster.GetName())
					factory.clients.Delete(kluster.GetUID())
				}
			},
		})
	}

	return factory
}

func (f *SharedClientFactory) ClientFor(k *kubernikus_v1.Kluster) (kubernetes.Interface, error) {
	if client, found := f.clients.Load(k.GetUID()); found {
		return client.(kubernetes.Interface), nil
	}
	glog.V(5).Info("Creating new shared kubernetes client for kluster %s", k.GetName())
	secret, err := f.secretsInterface.Get(k.GetName(), metav1.GetOptions{})
	if err != nil {
		return nil, err
	}
	apiserverCACert, ok := secret.Data["tls-ca.pem"]
	if !ok {
		return nil, errors.New("tls-ca.pem not found in secret")
	}
	clientCert, ok := secret.Data["apiserver-clients-cluster-admin.pem"]
	if !ok {
		return nil, errors.New("tls-ca.pem not found in secret")
	}
	clientKey, ok := secret.Data["apiserver-clients-cluster-admin-key.pem"]
	if !ok {
		return nil, errors.New("tls-ca.pem not found in secret")
	}

	c := rest.Config{
		Host: k.Spec.KubernikusInfo.ServerURL,
		TLSClientConfig: rest.TLSClientConfig{
			CertData: clientCert,
			KeyData:  clientKey,
			CAData:   apiserverCACert,
		},
	}

	clientset, err := kubernetes.NewForConfig(&c)
	if err != nil {
		return nil, err
	}

	f.clients.Store(k.GetUID(), clientset)
	return clientset, nil

}

func NewConfig(kubeconfig, context string) (*rest.Config, error) {
	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	overrides := &clientcmd.ConfigOverrides{}

	if len(context) > 0 {
		overrides.CurrentContext = context
	}

	if len(kubeconfig) > 0 {
		rules.ExplicitPath = kubeconfig
	}

	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig()
	if err != nil {
		glog.Fatalf("Couldn't get Kubernetes default config: %s", err)
	}

	return config, nil
}

func NewClient(kubeconfig, context string) (kubernetes.Interface, error) {
	config, err := NewConfig(kubeconfig, context)
	if err != nil {
		return nil, err
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Using Kubernetes Api at %s", config.Host)

	return clientset, nil
}

func NewClientConfigV1(name, user, url string, key, cert, ca []byte) clientcmdapiv1.Config {
	return clientcmdapiv1.Config{
		APIVersion:     "v1",
		Kind:           "Config",
		CurrentContext: name,
		Clusters: []clientcmdapiv1.NamedCluster{
			clientcmdapiv1.NamedCluster{
				Name: name,
				Cluster: clientcmdapiv1.Cluster{
					Server: url,
					CertificateAuthorityData: ca,
				},
			},
		},
		Contexts: []clientcmdapiv1.NamedContext{
			clientcmdapiv1.NamedContext{
				Name: name,
				Context: clientcmdapiv1.Context{
					Cluster:  name,
					AuthInfo: user,
				},
			},
		},
		AuthInfos: []clientcmdapiv1.NamedAuthInfo{
			clientcmdapiv1.NamedAuthInfo{
				Name: user,
				AuthInfo: clientcmdapiv1.AuthInfo{
					ClientCertificateData: cert,
					ClientKeyData:         key,
				},
			},
		},
	}
}

func EnsureTPR(clientset kubernetes.Interface) error {
	tpr := &v1beta1.ThirdPartyResource{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kluster." + kubernikus_v1.GroupName,
		},
		Versions: []v1beta1.APIVersion{
			{Name: v1.SchemeGroupVersion.Version},
		},
		Description: "Managed kubernetes cluster",
	}

	_, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Create(tpr)
	if err != nil && !apierrors.IsAlreadyExists(err) {
		return err
	}
	return nil
}

func WaitForTPR(clientset kubernetes.Interface) error {
	return wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) {
		_, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Get("kluster."+kubernikus_v1.GroupName, metav1.GetOptions{})
		if err == nil {
			return true, nil
		}
		if apierrors.IsNotFound(err) {
			return false, nil
		}
		return false, err
	})
}

func WaitForServer(client kubernetes.Interface, stopCh <-chan struct{}) error {
	var healthzContent string

	err := wait.PollUntil(time.Second, func() (bool, error) {
		healthStatus := 0
		resp := client.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
		if healthStatus != http.StatusOK {
			glog.Errorf("Server isn't healthy yet.  Waiting a little while.")
			return false, nil
		}
		content, _ := resp.Raw()
		healthzContent = string(content)

		return true, nil
	}, stopCh)
	if err != nil {
		return fmt.Errorf("Failed to contact apiserver. Last health: %v  Error: %v", healthzContent, err)
	}

	return nil
}
<|endoftext|>


<commit_before>package login

import (
	"errors"
	"fmt"
	"io"

	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/client/restclient"
	kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/cli/config"
	"github.com/openshift/origin/pkg/cmd/templates"
	osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd"
)

type LogoutOptions struct {
	StartingKubeConfig *kclientcmdapi.Config
	Config             *restclient.Config
	Out                io.Writer

	PathOptions *kclientcmd.PathOptions
}

var (
	logoutLong = templates.LongDesc(`
		Log out of the active session out by clearing saved tokens

		An authentication token is stored in the config file after login - this command will delete
		that token on the server, and then remove the token from the configuration file.

		If you are using an alternative authentication method like Kerberos or client certificates,
		your ticket or client certificate will not be removed from the current system since these
		are typically managed by other programs. Instead, you can delete your config file to remove
		the local copy of that certificate or the record of your server login.

		After logging out, if you want to log back into the server use '%[1]s'.`)

	logoutExample = templates.Examples(`
	  # Logout
	  %[1]s`)
)

// NewCmdLogout implements the OpenShift cli logout command
func NewCmdLogout(name, fullName, ocLoginFullCommand string, f *osclientcmd.Factory, reader io.Reader, out io.Writer) *cobra.Command {
	options := &LogoutOptions{
		Out: out,
	}

	cmds := &cobra.Command{
		Use:     name,
		Short:   "End the current server session",
		Long:    fmt.Sprintf(logoutLong, ocLoginFullCommand),
		Example: fmt.Sprintf(logoutExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			if err := options.Complete(f, cmd, args); err != nil {
				kcmdutil.CheckErr(err)
			}

			if err := options.Validate(args); err != nil {
				kcmdutil.CheckErr(err)
			}

			if err := options.RunLogout(); err != nil {
				kcmdutil.CheckErr(err)
			}

		},
	}

	// TODO: support --all which performs the same logic on all users in your config file.

	return cmds
}

func (o *LogoutOptions) Complete(f *osclientcmd.Factory, cmd *cobra.Command, args []string) error {
	kubeconfig, err := f.OpenShiftClientConfig().RawConfig()
	o.StartingKubeConfig = &kubeconfig
	if err != nil {
		return err
	}

	o.Config, err = f.OpenShiftClientConfig().ClientConfig()
	if err != nil {
		return err
	}

	o.PathOptions = config.NewPathOptions(cmd)

	return nil
}

func (o LogoutOptions) Validate(args []string) error {
	if len(args) > 0 {
		return errors.New("No arguments are allowed")
	}

	if o.StartingKubeConfig == nil {
		return errors.New("Must have a config file already created")
	}

	if len(o.Config.BearerToken) == 0 {
		return errors.New("You must have a token in order to logout.")
	}

	return nil
}

func (o LogoutOptions) RunLogout() error {
	token := o.Config.BearerToken

	client, err := client.New(o.Config)
	if err != nil {
		return err
	}

	userInfo, err := whoAmI(o.Config)
	if err != nil {
		return err
	}

	if err := client.OAuthAccessTokens().Delete(token); err != nil {
		glog.V(1).Infof("%v\n", err)
	}

	configErr := deleteTokenFromConfig(*o.StartingKubeConfig, o.PathOptions, token)
	if configErr == nil {
		glog.V(1).Infof("Removed token from your local configuration.\n\n")

		// only return error instead of successful message if removing token from client
		// config fails. Any error that occurs deleting token using api is logged above.
		fmt.Fprintf(o.Out, "Logged %q out on %q\n", userInfo.Name, o.Config.Host)
	}

	return configErr
}

func deleteTokenFromConfig(config kclientcmdapi.Config, pathOptions *kclientcmd.PathOptions, bearerToken string) error {
	for key, value := range config.AuthInfos {
		if value.Token == bearerToken {
			value.Token = ""
			config.AuthInfos[key] = value
			// don't break, its possible that more than one user stanza has the same token.
		}
	}

	return kclientcmd.ModifyConfig(pathOptions, config, true)
}
<commit_msg>Removed line breaks in glog messages<commit_after>package login

import (
	"errors"
	"fmt"
	"io"

	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/client/restclient"
	kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/cli/config"
	"github.com/openshift/origin/pkg/cmd/templates"
	osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd"
)

type LogoutOptions struct {
	StartingKubeConfig *kclientcmdapi.Config
	Config             *restclient.Config
	Out                io.Writer

	PathOptions *kclientcmd.PathOptions
}

var (
	logoutLong = templates.LongDesc(`
		Log out of the active session out by clearing saved tokens

		An authentication token is stored in the config file after login - this command will delete
		that token on the server, and then remove the token from the configuration file.

		If you are using an alternative authentication method like Kerberos or client certificates,
		your ticket or client certificate will not be removed from the current system since these
		are typically managed by other programs. Instead, you can delete your config file to remove
		the local copy of that certificate or the record of your server login.

		After logging out, if you want to log back into the server use '%[1]s'.`)

	logoutExample = templates.Examples(`
	  # Logout
	  %[1]s`)
)

// NewCmdLogout implements the OpenShift cli logout command
func NewCmdLogout(name, fullName, ocLoginFullCommand string, f *osclientcmd.Factory, reader io.Reader, out io.Writer) *cobra.Command {
	options := &LogoutOptions{
		Out: out,
	}

	cmds := &cobra.Command{
		Use:     name,
		Short:   "End the current server session",
		Long:    fmt.Sprintf(logoutLong, ocLoginFullCommand),
		Example: fmt.Sprintf(logoutExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			if err := options.Complete(f, cmd, args); err != nil {
				kcmdutil.CheckErr(err)
			}

			if err := options.Validate(args); err != nil {
				kcmdutil.CheckErr(err)
			}

			if err := options.RunLogout(); err != nil {
				kcmdutil.CheckErr(err)
			}

		},
	}

	// TODO: support --all which performs the same logic on all users in your config file.

	return cmds
}

func (o *LogoutOptions) Complete(f *osclientcmd.Factory, cmd *cobra.Command, args []string) error {
	kubeconfig, err := f.OpenShiftClientConfig().RawConfig()
	o.StartingKubeConfig = &kubeconfig
	if err != nil {
		return err
	}

	o.Config, err = f.OpenShiftClientConfig().ClientConfig()
	if err != nil {
		return err
	}

	o.PathOptions = config.NewPathOptions(cmd)

	return nil
}

func (o LogoutOptions) Validate(args []string) error {
	if len(args) > 0 {
		return errors.New("No arguments are allowed")
	}

	if o.StartingKubeConfig == nil {
		return errors.New("Must have a config file already created")
	}

	if len(o.Config.BearerToken) == 0 {
		return errors.New("You must have a token in order to logout.")
	}

	return nil
}

func (o LogoutOptions) RunLogout() error {
	token := o.Config.BearerToken

	client, err := client.New(o.Config)
	if err != nil {
		return err
	}

	userInfo, err := whoAmI(o.Config)
	if err != nil {
		return err
	}

	if err := client.OAuthAccessTokens().Delete(token); err != nil {
		glog.V(1).Infof("%v", err)
	}

	configErr := deleteTokenFromConfig(*o.StartingKubeConfig, o.PathOptions, token)
	if configErr == nil {
		glog.V(1).Infof("Removed token from your local configuration.")

		// only return error instead of successful message if removing token from client
		// config fails. Any error that occurs deleting token using api is logged above.
		fmt.Fprintf(o.Out, "Logged %q out on %q\n", userInfo.Name, o.Config.Host)
	}

	return configErr
}

func deleteTokenFromConfig(config kclientcmdapi.Config, pathOptions *kclientcmd.PathOptions, bearerToken string) error {
	for key, value := range config.AuthInfos {
		if value.Token == bearerToken {
			value.Token = ""
			config.AuthInfos[key] = value
			// don't break, its possible that more than one user stanza has the same token.
		}
	}

	return kclientcmd.ModifyConfig(pathOptions, config, true)
}
<|endoftext|>


<commit_before>package mango

import (
	"encoding/json"
	"testing"

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

type M map[string]interface{}
type S []interface{}

func DeepEqual(t *testing.T, map1, map2 interface{}) bool {
	j1, err1 := json.Marshal(map1)
	j2, err2 := json.Marshal(map2)
	if assert.NoError(t, err1) && assert.NoError(t, err2) {
		assert.Equal(t, string(j1), string(j2))
	}
	return false
}

func TestQueryMarshaling(t *testing.T) {
	q1 := Equal("DirID", "ab123")
	DeepEqual(t, q1.ToMango(), M{"DirID": "ab123"})
	q2 := Gt("Size", 1000)
	DeepEqual(t, q2.ToMango(), M{"Size": M{"$gt": 1000}})
	q3 := And(q1, q2)
	DeepEqual(t, q3.ToMango(),
		M{"$and": S{
			M{"DirID": "ab123"},
			M{"Size": M{"$gt": 1000}},
		}})

	q4 := Not(Equal("DirID", "ab123"))
	DeepEqual(t, q4.ToMango(), M{"$not": M{"DirID": "ab123"}})
}

func TestSortMarshaling(t *testing.T) {
	s1 := SortBy{
		{"dir_id", Asc},
		{"foo_bar", Desc},
	}
	j1, err := json.Marshal(s1)
	if assert.NoError(t, err) {
		assert.Equal(t, j1, []byte(`[{"dir_id":"asc","foo_bar":"desc"}]`))
	}
}
<commit_msg>Fix test<commit_after>package mango

import (
	"encoding/json"
	"testing"

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

type M map[string]interface{}
type S []interface{}

func DeepEqual(t *testing.T, map1, map2 interface{}) bool {
	j1, err1 := json.Marshal(map1)
	j2, err2 := json.Marshal(map2)
	if assert.NoError(t, err1) && assert.NoError(t, err2) {
		assert.Equal(t, string(j1), string(j2))
	}
	return false
}

func TestQueryMarshaling(t *testing.T) {
	q1 := Equal("DirID", "ab123")
	DeepEqual(t, q1.ToMango(), M{"DirID": "ab123"})
	q2 := Gt("Size", 1000)
	DeepEqual(t, q2.ToMango(), M{"Size": M{"$gt": 1000}})
	q3 := And(q1, q2)
	DeepEqual(t, q3.ToMango(),
		M{"$and": S{
			M{"DirID": "ab123"},
			M{"Size": M{"$gt": 1000}},
		}})

	q4 := Not(Equal("DirID", "ab123"))
	DeepEqual(t, q4.ToMango(), M{"$not": M{"DirID": "ab123"}})
}

func TestSortMarshaling(t *testing.T) {
	s1 := SortBy{
		{"dir_id", Asc},
		{"foo_bar", Desc},
	}
	j1, err := json.Marshal(s1)
	if assert.NoError(t, err) {
		assert.Equal(t, j1, []byte(`[{"dir_id":"asc"},{"foo_bar":"desc"}]`))
	}
}
<|endoftext|>


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

package skyconv

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"time"

	"github.com/skygeario/skygear-server/pkg/server/skydb"
)

// MapFrom tries to map a map to a FromMapper
func MapFrom(i interface{}, fromMapper FromMapper) error {
	if m, ok := i.(map[string]interface{}); ok {
		return fromMapper.FromMap(m)
	}

	return fmt.Errorf("want map, got type = %T", i)
}

// FromMapper defines whether a type can be converted from a map
type FromMapper interface {
	FromMap(m map[string]interface{}) error
}

// ToMapper defines whether a type can be converted to a map
type ToMapper interface {
	ToMap(m map[string]interface{})
}

// ToMap converts a ToMapper to map and returns it
func ToMap(mapper ToMapper) map[string]interface{} {
	mm := map[string]interface{}{}
	mapper.ToMap(mm)
	return mm
}

// MapData is record data that can be converted from a map
type MapData map[string]interface{}

// FromMap implements FromMapper
func (data *MapData) FromMap(m map[string]interface{}) (err error) {
	var walkedData map[string]interface{}
	walkedData, err = walkData(m)
	if err != nil {
		return
	}

	*data = walkedData
	return nil
}

// ToMap implements ToMapper
func (data MapData) ToMap(m map[string]interface{}) {
	for key, value := range data {
		if mapper, ok := value.(ToMapper); ok {
			mm := map[string]interface{}{}
			mapper.ToMap(mm)
			m[key] = mm
		} else {
			m[key] = value
		}
	}
}

// MapTime is time.Time that can be converted from and to a map.
type MapTime time.Time

// FromMap implements FromMapper
func (t *MapTime) FromMap(m map[string]interface{}) error {
	datei, ok := m["$date"]
	if !ok {
		return errors.New("missing compulsory field $date")
	}
	dateStr, ok := datei.(string)
	if !ok {
		return fmt.Errorf("got type($date) = %T, want string", datei)
	}
	dt, err := time.Parse(time.RFC3339Nano, dateStr)
	if err != nil {
		return fmt.Errorf("failed to parse $date = %#v", dateStr)
	}

	*(*time.Time)(t) = dt.In(time.UTC)
	return nil
}

// ToMap implements ToMapper
func (t MapTime) ToMap(m map[string]interface{}) {
	m["$type"] = "date"
	m["$date"] = time.Time(t)
}

// MapAsset is skydb.Asset that can be converted from and to a map.
type MapAsset skydb.Asset

// FromMap implements FromMapper
func (asset *MapAsset) FromMap(m map[string]interface{}) error {
	namei, ok := m["$name"]
	if !ok {
		return errors.New("missing compulsory field $name")
	}
	name, ok := namei.(string)
	if !ok {
		return fmt.Errorf("got type($name) = %T, want string", namei)
	}
	if name == "" {
		return errors.New("asset's $name should not be empty")
	}

	asset.Name = name
	return nil
}

// ToMap implements ToMapper
func (asset *MapAsset) ToMap(m map[string]interface{}) {
	m["$type"] = "asset"
	m["$name"] = asset.Name
	m["$content_type"] = asset.ContentType
	url := (*skydb.Asset)(asset).SignedURL()
	if url != "" {
		m["$url"] = url
	}
}

// MapReference is skydb.Reference that can be converted from and to a map.
type MapReference skydb.Reference

// FromMap implements FromMapper
func (ref *MapReference) FromMap(m map[string]interface{}) error {
	idi, ok := m["$id"]
	if !ok {
		return errors.New("referencing without $id")
	}
	id, ok := idi.(string)
	if !ok {
		return fmt.Errorf("got reference type($id) = %T, want string", idi)
	}
	ss := strings.SplitN(id, "/", 2)
	if len(ss) == 1 {
		return fmt.Errorf(`ref: "_id" should be of format '{type}/{id}', got %#v`, id)
	}

	ref.ID.Type = ss[0]
	ref.ID.Key = ss[1]
	return nil
}

// ToMap implements ToMapper
func (ref MapReference) ToMap(m map[string]interface{}) {
	m["$type"] = "ref"
	m["$id"] = ref.ID
}

// MapLocation is skydb.Location that can be converted from and to a map.
type MapLocation skydb.Location

// FromMap implements FromMapper
func (loc *MapLocation) FromMap(m map[string]interface{}) error {
	getFloat := func(m map[string]interface{}, key string) (float64, error) {
		i, ok := m[key]
		if !ok {
			return 0, fmt.Errorf("missing compulsory field %s", key)
		}

		f, ok := i.(float64)
		if !ok {
			return 0, fmt.Errorf("got type(%s) = %T, want number", key, i)
		}

		return f, nil
	}

	lng, err := getFloat(m, "$lng")
	if err != nil {
		return err
	}

	lat, err := getFloat(m, "$lat")
	if err != nil {
		return err
	}

	*loc = MapLocation{lng, lat}
	return nil
}

// ToMap implements ToMapper
func (loc MapLocation) ToMap(m map[string]interface{}) {
	m["$type"] = "geo"
	m["$lng"] = loc[0]
	m["$lat"] = loc[1]
}

func walkData(m map[string]interface{}) (mapReturned map[string]interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()

	return walkMap(m), err
}

// MapKeyPath is string keypath that can be converted from a map
type MapKeyPath string

// FromMap implements FromMapper
func (p *MapKeyPath) FromMap(m map[string]interface{}) error {
	keyPath, _ := m["$val"].(string)
	if keyPath == "" {
		return errors.New("empty key path")
	}

	*p = MapKeyPath(keyPath)
	return nil
}

// ToMap implements ToMapper
func (p MapKeyPath) ToMap(m map[string]interface{}) {
	m["$type"] = "keypath"
	m["$val"] = string(p)
}

// MapRelation is a type specifying a relation between two users, but do not conform to any actual struct in skydb.
type MapRelation struct {
	Name      string
	Direction string
}

// FromMap implements FromMapper
func (rel *MapRelation) FromMap(m map[string]interface{}) error {
	name, _ := m["$name"].(string)
	if name == "" {
		return errors.New("empty relation name")
	}

	direction, _ := m["$direction"].(string)
	if direction == "" {
		return errors.New("empty direction")
	}

	*rel = MapRelation{name, direction}
	return nil
}

// ToMap implements ToMapper
func (rel *MapRelation) ToMap(m map[string]interface{}) {
	m["$type"] = "relation"
	m["$name"] = rel.Name
	m["$direction"] = rel.Direction
}

// MapSequence is skydb.Sequence that can convert to map
type MapSequence struct{}

// ToMap implements ToMapper
func (seq MapSequence) ToMap(m map[string]interface{}) {
	m["$type"] = "seq"
}

// MapUnknown is skydb.Unknown that can convert to map
type MapUnknown skydb.Unknown

// FromMap implements FromMapper
func (val *MapUnknown) FromMap(m map[string]interface{}) error {
	underlyingType, _ := m["$underlying_type"].(string)
	*val = MapUnknown{underlyingType}
	return nil
}

// ToMap implements ToMapper
func (val MapUnknown) ToMap(m map[string]interface{}) {
	m["$type"] = "unknown"
	m["$underlying_type"] = val.UnderlyingType
}

type MapACLEntry skydb.RecordACLEntry

// FromMap initializes a RecordACLEntry from a unmarshalled JSON of
// access control definition
func (ace *MapACLEntry) FromMap(m map[string]interface{}) error {
	level, _ := m["level"].(string)
	var entryLevel skydb.ACLLevel
	switch level {
	case "read":
		entryLevel = skydb.ReadLevel
	case "write":
		entryLevel = skydb.WriteLevel
	case "":
		return errors.New("empty level")
	default:
		return fmt.Errorf("unknown level = %s", level)
	}

	relation, hasRelation := m["relation"].(string)
	userID, hasUserID := m["user_id"].(string)
	role, hasRole := m["role"].(string)
	public, hasPublic := m["public"].(bool)
	if !hasRelation && !hasUserID && !hasRole && !hasPublic {
		return errors.New("ACLEntry must have relation, user_id, role or public")
	}

	ace.Level = entryLevel
	if hasRelation {
		ace.Relation = relation
	}
	if hasRole {
		ace.Role = role
	}
	if hasUserID {
		ace.UserID = userID
	}
	if hasPublic {
		ace.Public = public
	}
	return nil
}

func walkMap(m map[string]interface{}) map[string]interface{} {
	for key, value := range m {
		m[key] = ParseLiteral(value)
	}

	return m
}

func walkSlice(items []interface{}) []interface{} {
	for i, item := range items {
		items[i] = ParseLiteral(item)
	}

	return items
}

// ParseLiteral deduces whether i is a skydb data value and returns a
// parsed value.
func ParseLiteral(i interface{}) interface{} {
	switch value := i.(type) {
	default:
		// considered a bug if this line is reached
		panic(fmt.Errorf("unsupported value = %T", value))
	case nil, bool, float64, string:
		// the set of value that json unmarshaller returns
		// http://golang.org/pkg/encoding/json/#Unmarshal
		return value
	case map[string]interface{}:
		kindi, typed := value["$type"]
		if !typed {
			// regular dictionary, go deeper
			return walkMap(value)
		}

		kind, ok := kindi.(string)
		if !ok {
			panic(fmt.Errorf(`got "$type"'s type = %T, want string`, kindi))
		}

		switch kind {
		case "keypath":
			var keyPath string
			mapFromOrPanic((*MapKeyPath)(&keyPath), value)
			return keyPath
		case "blob":
			panic(fmt.Errorf("unimplemented $type = %s", kind))
		case "asset":
			var asset skydb.Asset
			mapFromOrPanic((*MapAsset)(&asset), value)
			return &asset
		case "ref":
			var ref skydb.Reference
			mapFromOrPanic((*MapReference)(&ref), value)
			return ref
		case "date":
			var t time.Time
			mapFromOrPanic((*MapTime)(&t), value)
			return t
		case "geo":
			var loc skydb.Location
			mapFromOrPanic((*MapLocation)(&loc), value)
			return loc
		case "seq":
			return skydb.Sequence{}
		case "unknown":
			var val skydb.Unknown
			mapFromOrPanic((*MapUnknown)(&val), value)
			return val
		case "relation":
			var rel MapRelation
			mapFromOrPanic((*MapRelation)(&rel), value)
			return &rel
		default:
			panic(fmt.Errorf("unknown $type = %s", kind))
		}
	case []interface{}:
		return walkSlice(value)
	}
}

func mapFromOrPanic(fromMapper FromMapper, m map[string]interface{}) {
	if err := fromMapper.FromMap(m); err != nil {
		panic(err)
	}
}
<commit_msg>Read content type from JSON map if it exist<commit_after>// Copyright 2015-present Oursky Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package skyconv

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"time"

	"github.com/skygeario/skygear-server/pkg/server/skydb"
)

// MapFrom tries to map a map to a FromMapper
func MapFrom(i interface{}, fromMapper FromMapper) error {
	if m, ok := i.(map[string]interface{}); ok {
		return fromMapper.FromMap(m)
	}

	return fmt.Errorf("want map, got type = %T", i)
}

// FromMapper defines whether a type can be converted from a map
type FromMapper interface {
	FromMap(m map[string]interface{}) error
}

// ToMapper defines whether a type can be converted to a map
type ToMapper interface {
	ToMap(m map[string]interface{})
}

// ToMap converts a ToMapper to map and returns it
func ToMap(mapper ToMapper) map[string]interface{} {
	mm := map[string]interface{}{}
	mapper.ToMap(mm)
	return mm
}

// MapData is record data that can be converted from a map
type MapData map[string]interface{}

// FromMap implements FromMapper
func (data *MapData) FromMap(m map[string]interface{}) (err error) {
	var walkedData map[string]interface{}
	walkedData, err = walkData(m)
	if err != nil {
		return
	}

	*data = walkedData
	return nil
}

// ToMap implements ToMapper
func (data MapData) ToMap(m map[string]interface{}) {
	for key, value := range data {
		if mapper, ok := value.(ToMapper); ok {
			mm := map[string]interface{}{}
			mapper.ToMap(mm)
			m[key] = mm
		} else {
			m[key] = value
		}
	}
}

// MapTime is time.Time that can be converted from and to a map.
type MapTime time.Time

// FromMap implements FromMapper
func (t *MapTime) FromMap(m map[string]interface{}) error {
	datei, ok := m["$date"]
	if !ok {
		return errors.New("missing compulsory field $date")
	}
	dateStr, ok := datei.(string)
	if !ok {
		return fmt.Errorf("got type($date) = %T, want string", datei)
	}
	dt, err := time.Parse(time.RFC3339Nano, dateStr)
	if err != nil {
		return fmt.Errorf("failed to parse $date = %#v", dateStr)
	}

	*(*time.Time)(t) = dt.In(time.UTC)
	return nil
}

// ToMap implements ToMapper
func (t MapTime) ToMap(m map[string]interface{}) {
	m["$type"] = "date"
	m["$date"] = time.Time(t)
}

// MapAsset is skydb.Asset that can be converted from and to a map.
type MapAsset skydb.Asset

// FromMap implements FromMapper
func (asset *MapAsset) FromMap(m map[string]interface{}) error {
	namei, ok := m["$name"]
	if !ok {
		return errors.New("missing compulsory field $name")
	}
	name, ok := namei.(string)
	if !ok {
		return fmt.Errorf("got type($name) = %T, want string", namei)
	}
	if name == "" {
		return errors.New("asset's $name should not be empty")
	}
	asset.Name = name

	contentTypei, ok := m["$content_type"]
	if ok {
		contentType, ok := contentTypei.(string)
		if !ok {
			return fmt.Errorf("got type($contentType) = %T, want string", contentTypei)
		}
		asset.ContentType = contentType
	}

	return nil
}

// ToMap implements ToMapper
func (asset *MapAsset) ToMap(m map[string]interface{}) {
	m["$type"] = "asset"
	m["$name"] = asset.Name
	m["$content_type"] = asset.ContentType
	url := (*skydb.Asset)(asset).SignedURL()
	if url != "" {
		m["$url"] = url
	}
}

// MapReference is skydb.Reference that can be converted from and to a map.
type MapReference skydb.Reference

// FromMap implements FromMapper
func (ref *MapReference) FromMap(m map[string]interface{}) error {
	idi, ok := m["$id"]
	if !ok {
		return errors.New("referencing without $id")
	}
	id, ok := idi.(string)
	if !ok {
		return fmt.Errorf("got reference type($id) = %T, want string", idi)
	}
	ss := strings.SplitN(id, "/", 2)
	if len(ss) == 1 {
		return fmt.Errorf(`ref: "_id" should be of format '{type}/{id}', got %#v`, id)
	}

	ref.ID.Type = ss[0]
	ref.ID.Key = ss[1]
	return nil
}

// ToMap implements ToMapper
func (ref MapReference) ToMap(m map[string]interface{}) {
	m["$type"] = "ref"
	m["$id"] = ref.ID
}

// MapLocation is skydb.Location that can be converted from and to a map.
type MapLocation skydb.Location

// FromMap implements FromMapper
func (loc *MapLocation) FromMap(m map[string]interface{}) error {
	getFloat := func(m map[string]interface{}, key string) (float64, error) {
		i, ok := m[key]
		if !ok {
			return 0, fmt.Errorf("missing compulsory field %s", key)
		}

		f, ok := i.(float64)
		if !ok {
			return 0, fmt.Errorf("got type(%s) = %T, want number", key, i)
		}

		return f, nil
	}

	lng, err := getFloat(m, "$lng")
	if err != nil {
		return err
	}

	lat, err := getFloat(m, "$lat")
	if err != nil {
		return err
	}

	*loc = MapLocation{lng, lat}
	return nil
}

// ToMap implements ToMapper
func (loc MapLocation) ToMap(m map[string]interface{}) {
	m["$type"] = "geo"
	m["$lng"] = loc[0]
	m["$lat"] = loc[1]
}

func walkData(m map[string]interface{}) (mapReturned map[string]interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()

	return walkMap(m), err
}

// MapKeyPath is string keypath that can be converted from a map
type MapKeyPath string

// FromMap implements FromMapper
func (p *MapKeyPath) FromMap(m map[string]interface{}) error {
	keyPath, _ := m["$val"].(string)
	if keyPath == "" {
		return errors.New("empty key path")
	}

	*p = MapKeyPath(keyPath)
	return nil
}

// ToMap implements ToMapper
func (p MapKeyPath) ToMap(m map[string]interface{}) {
	m["$type"] = "keypath"
	m["$val"] = string(p)
}

// MapRelation is a type specifying a relation between two users, but do not conform to any actual struct in skydb.
type MapRelation struct {
	Name      string
	Direction string
}

// FromMap implements FromMapper
func (rel *MapRelation) FromMap(m map[string]interface{}) error {
	name, _ := m["$name"].(string)
	if name == "" {
		return errors.New("empty relation name")
	}

	direction, _ := m["$direction"].(string)
	if direction == "" {
		return errors.New("empty direction")
	}

	*rel = MapRelation{name, direction}
	return nil
}

// ToMap implements ToMapper
func (rel *MapRelation) ToMap(m map[string]interface{}) {
	m["$type"] = "relation"
	m["$name"] = rel.Name
	m["$direction"] = rel.Direction
}

// MapSequence is skydb.Sequence that can convert to map
type MapSequence struct{}

// ToMap implements ToMapper
func (seq MapSequence) ToMap(m map[string]interface{}) {
	m["$type"] = "seq"
}

// MapUnknown is skydb.Unknown that can convert to map
type MapUnknown skydb.Unknown

// FromMap implements FromMapper
func (val *MapUnknown) FromMap(m map[string]interface{}) error {
	underlyingType, _ := m["$underlying_type"].(string)
	*val = MapUnknown{underlyingType}
	return nil
}

// ToMap implements ToMapper
func (val MapUnknown) ToMap(m map[string]interface{}) {
	m["$type"] = "unknown"
	m["$underlying_type"] = val.UnderlyingType
}

type MapACLEntry skydb.RecordACLEntry

// FromMap initializes a RecordACLEntry from a unmarshalled JSON of
// access control definition
func (ace *MapACLEntry) FromMap(m map[string]interface{}) error {
	level, _ := m["level"].(string)
	var entryLevel skydb.ACLLevel
	switch level {
	case "read":
		entryLevel = skydb.ReadLevel
	case "write":
		entryLevel = skydb.WriteLevel
	case "":
		return errors.New("empty level")
	default:
		return fmt.Errorf("unknown level = %s", level)
	}

	relation, hasRelation := m["relation"].(string)
	userID, hasUserID := m["user_id"].(string)
	role, hasRole := m["role"].(string)
	public, hasPublic := m["public"].(bool)
	if !hasRelation && !hasUserID && !hasRole && !hasPublic {
		return errors.New("ACLEntry must have relation, user_id, role or public")
	}

	ace.Level = entryLevel
	if hasRelation {
		ace.Relation = relation
	}
	if hasRole {
		ace.Role = role
	}
	if hasUserID {
		ace.UserID = userID
	}
	if hasPublic {
		ace.Public = public
	}
	return nil
}

func walkMap(m map[string]interface{}) map[string]interface{} {
	for key, value := range m {
		m[key] = ParseLiteral(value)
	}

	return m
}

func walkSlice(items []interface{}) []interface{} {
	for i, item := range items {
		items[i] = ParseLiteral(item)
	}

	return items
}

// ParseLiteral deduces whether i is a skydb data value and returns a
// parsed value.
func ParseLiteral(i interface{}) interface{} {
	switch value := i.(type) {
	default:
		// considered a bug if this line is reached
		panic(fmt.Errorf("unsupported value = %T", value))
	case nil, bool, float64, string:
		// the set of value that json unmarshaller returns
		// http://golang.org/pkg/encoding/json/#Unmarshal
		return value
	case map[string]interface{}:
		kindi, typed := value["$type"]
		if !typed {
			// regular dictionary, go deeper
			return walkMap(value)
		}

		kind, ok := kindi.(string)
		if !ok {
			panic(fmt.Errorf(`got "$type"'s type = %T, want string`, kindi))
		}

		switch kind {
		case "keypath":
			var keyPath string
			mapFromOrPanic((*MapKeyPath)(&keyPath), value)
			return keyPath
		case "blob":
			panic(fmt.Errorf("unimplemented $type = %s", kind))
		case "asset":
			var asset skydb.Asset
			mapFromOrPanic((*MapAsset)(&asset), value)
			return &asset
		case "ref":
			var ref skydb.Reference
			mapFromOrPanic((*MapReference)(&ref), value)
			return ref
		case "date":
			var t time.Time
			mapFromOrPanic((*MapTime)(&t), value)
			return t
		case "geo":
			var loc skydb.Location
			mapFromOrPanic((*MapLocation)(&loc), value)
			return loc
		case "seq":
			return skydb.Sequence{}
		case "unknown":
			var val skydb.Unknown
			mapFromOrPanic((*MapUnknown)(&val), value)
			return val
		case "relation":
			var rel MapRelation
			mapFromOrPanic((*MapRelation)(&rel), value)
			return &rel
		default:
			panic(fmt.Errorf("unknown $type = %s", kind))
		}
	case []interface{}:
		return walkSlice(value)
	}
}

func mapFromOrPanic(fromMapper FromMapper, m map[string]interface{}) {
	if err := fromMapper.FromMap(m); err != nil {
		panic(err)
	}
}
<|endoftext|>