<commit_before>// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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.

// The claat command generates one or more codelabs from "source" documents,
// specified as either Google Doc IDs or local markdown files.
// The command also allows one to preview generated codelabs from local drive
// using "claat serve".
// See more details at https://github.com/googlecodelabs/tools.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"github.com/googlecodelabs/tools/claat/cmd"

	// allow parsers to register themselves
	_ "github.com/googlecodelabs/tools/claat/parser/gdoc"
	_ "github.com/googlecodelabs/tools/claat/parser/md"
)

var (
	useElements string // set by linker -X
	version string // set by linker -X

	// Flags.
	addr      = flag.String("addr", "localhost:9090", "hostname and port to bind web server to")
	authToken = flag.String("auth", "", "OAuth2 Bearer token; alternative credentials override.")
	expenv    = flag.String("e", "web", "codelab environment")
	extra     = flag.String("extra", "", "Additional arguments to pass to format templates. JSON object of string,string key values.")
	globalGA  = flag.String("ga", "UA-49880327-14", "global Google Analytics account")
	output    = flag.String("o", ".", "output directory or '-' for stdout")
	prefix    = flag.String("prefix", "../../", "URL prefix for html format")
	tmplout   = flag.String("f", "html", "output format")
)

func main() {
	log.SetFlags(0)
	rand.Seed(time.Now().UnixNano())
	if len(os.Args) == 1 {
		log.Fatalf("Need subcommand. Try '-h' for options.")
	}
	if os.Args[1] == "-h" || os.Args[1] == "--help" {
		usage()
		return
	}

	useElems, err := strconv.ParseBool(useElements)
	if err != nil {
		os.Exit(1)
	}
	if useElems && *tmplout == "html"{
		*tmplout = "htmlElements"
	}

	flag.Usage = usage
	flag.CommandLine.Parse(os.Args[2:])

	extraVars, err := ParseExtraVars(*extra)
	if err != nil {
		os.Exit(1)
	}

	exitCode := 0
	switch os.Args[1] {
	case "export":
		exitCode = cmd.CmdExport(cmd.CmdExportOptions{
			AuthToken: *authToken,
			Expenv:    *expenv,
			ExtraVars: extraVars,
			GlobalGA:  *globalGA,
			Output:    *output,
			Prefix:    *prefix,
			Srcs:      flag.Args(),
			Tmplout:   *tmplout,
		})
	case "serve":
		exitCode = cmd.CmdServe(*addr, useElems)
	case "build":
		exitCode = cmd.CmdBuild()
	case "update":
		exitCode = cmd.CmdUpdate(cmd.CmdUpdateOptions{
			AuthToken: *authToken,
			ExtraVars: extraVars,
			GlobalGA:  *globalGA,
			Prefix:    *prefix,
		})
	case "help":
		usage()
	case "version":
		fmt.Println(version)
	default:
		log.Fatalf("Unknown subcommand. Try '-h' for options.")
	}

	os.Exit(exitCode)
}

// ParseExtraVars parses extra template variables from command line.
// extra is any additional arguments to pass to format templates. Should be formatted as JSON objects of string:string KV pairs.
func ParseExtraVars(extra string) (map[string]string, error) {
	vars := make(map[string]string)
	if extra == "" {
		return vars, nil
	}
	b := []byte(extra)
	err := json.Unmarshal(b, &vars)
	if err != nil {
		log.Printf("Error parsing additional template data: %v", err)
		return nil, err
	}
	return vars, nil
}

// usage prints usageText and program arguments to stderr.
func usage() {
	fmt.Fprint(os.Stderr, usageText)
	flag.PrintDefaults()
}

const usageText = `Usage: claat <cmd> [options] src [src ...]

Available commands are: export, serve, update, version.

## Export command

Export takes one or more 'src' documents and converts them
to the format specified with -f option.

The following formats are built-in:

- html (Polymer-based app)
- md (Markdown)
- offline (plain HTML markup for offline consumption)

Note that the built-in templates of the formats are not guaranteed to be stable.
They can be found in https://github.com/googlecodelabs/tools/tree/master/claat/render.
Please avoid using default templates in production. Use your own copies.

To use a custom format, specify a local file path to a Go template file.
More info on Go templates: https://golang.org/pkg/text/template/.

Each 'src' can be either a remote HTTP resource or a local file.
Source formats currently supported are:

- Google Doc (Codelab Format, go/codelab-guide)
- Markdown

When 'src' is a Google Doc, it must be specified as a doc ID,
omitting https://docs.google.com/... part.

Instead of writing to an output directory, use "-o -" to specify
stdout. In this case images and metadata are not exported.
When writing to a directory, existing files will be overwritten.

The program exits with non-zero code if at least one src could not be exported.

## Build command

Install all the dependencies needed by the codelab unless already installed.
This is done automatically by the serve command.
To clean up and rebuild all the dependencies, remove bower_components
directory and run the build command again

## Serve command

Serve provides a simple web server for viewing exported codelabs.
It takes no arguments and presents the current directory contents.
Clicking on a directory representing an exported codelab will load
all the required dependencies and render the generated codelab as
it would appear in production.

The serve command always runs the build command first.

The serve command takes a -addr host:port option, to specify the
desired hostname or IP address and port number to bind to.

## Update command

Update scans one or more 'src' local directories for codelab.json metadata
files, recursively. A directory containing the metadata file is expected
to be a codelab previously created with the export command.

Current directory is assumed if no 'src' argument is given.

Each found codelab is then re-exported using parameters from the metadata file.
Unused codelab assets will be deleted, as well as the entire codelab directory,
if codelab ID has changed since last update or export.

In the latter case, where codelab ID has changed, the new directory
will be placed alongside the old one. In other words, it will have the same ancestor
as the old one.

While -prefix and -ga can override existing codelab metadata, the other
arguments have no effect during update.

The program does not follow symbolic links and exits with non-zero code
if no metadata found or at least one src could not be updated.

## Flags

`
<commit_msg>fix problem with go get producing bad executable (#145)<commit_after>// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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.

// The claat command generates one or more codelabs from "source" documents,
// specified as either Google Doc IDs or local markdown files.
// The command also allows one to preview generated codelabs from local drive
// using "claat serve".
// See more details at https://github.com/googlecodelabs/tools.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"github.com/googlecodelabs/tools/claat/cmd"

	// allow parsers to register themselves
	_ "github.com/googlecodelabs/tools/claat/parser/gdoc"
	_ "github.com/googlecodelabs/tools/claat/parser/md"
)

var (
	useElements string // set by linker -X
	version string // set by linker -X

	// Flags.
	addr      = flag.String("addr", "localhost:9090", "hostname and port to bind web server to")
	authToken = flag.String("auth", "", "OAuth2 Bearer token; alternative credentials override.")
	expenv    = flag.String("e", "web", "codelab environment")
	extra     = flag.String("extra", "", "Additional arguments to pass to format templates. JSON object of string,string key values.")
	globalGA  = flag.String("ga", "UA-49880327-14", "global Google Analytics account")
	output    = flag.String("o", ".", "output directory or '-' for stdout")
	prefix    = flag.String("prefix", "../../", "URL prefix for html format")
	tmplout   = flag.String("f", "html", "output format")
)

func main() {
	log.SetFlags(0)
	rand.Seed(time.Now().UnixNano())
	if len(os.Args) == 1 {
		log.Fatalf("Need subcommand. Try '-h' for options.")
	}
	if os.Args[1] == "-h" || os.Args[1] == "--help" {
		usage()
		return
	}

	useElems, err := strconv.ParseBool(useElements)
	if err != nil {
		useElems = false
	}
	if useElems && *tmplout == "html"{
		*tmplout = "htmlElements"
	}

	flag.Usage = usage
	flag.CommandLine.Parse(os.Args[2:])

	extraVars, err := ParseExtraVars(*extra)
	if err != nil {
		os.Exit(1)
	}

	exitCode := 0
	switch os.Args[1] {
	case "export":
		exitCode = cmd.CmdExport(cmd.CmdExportOptions{
			AuthToken: *authToken,
			Expenv:    *expenv,
			ExtraVars: extraVars,
			GlobalGA:  *globalGA,
			Output:    *output,
			Prefix:    *prefix,
			Srcs:      flag.Args(),
			Tmplout:   *tmplout,
		})
	case "serve":
		exitCode = cmd.CmdServe(*addr, useElems)
	case "build":
		exitCode = cmd.CmdBuild()
	case "update":
		exitCode = cmd.CmdUpdate(cmd.CmdUpdateOptions{
			AuthToken: *authToken,
			ExtraVars: extraVars,
			GlobalGA:  *globalGA,
			Prefix:    *prefix,
		})
	case "help":
		usage()
	case "version":
		fmt.Println(version)
	default:
		log.Fatalf("Unknown subcommand. Try '-h' for options.")
	}

	os.Exit(exitCode)
}

// ParseExtraVars parses extra template variables from command line.
// extra is any additional arguments to pass to format templates. Should be formatted as JSON objects of string:string KV pairs.
func ParseExtraVars(extra string) (map[string]string, error) {
	vars := make(map[string]string)
	if extra == "" {
		return vars, nil
	}
	b := []byte(extra)
	err := json.Unmarshal(b, &vars)
	if err != nil {
		log.Printf("Error parsing additional template data: %v", err)
		return nil, err
	}
	return vars, nil
}

// usage prints usageText and program arguments to stderr.
func usage() {
	fmt.Fprint(os.Stderr, usageText)
	flag.PrintDefaults()
}

const usageText = `Usage: claat <cmd> [options] src [src ...]

Available commands are: export, serve, update, version.

## Export command

Export takes one or more 'src' documents and converts them
to the format specified with -f option.

The following formats are built-in:

- html (Polymer-based app)
- md (Markdown)
- offline (plain HTML markup for offline consumption)

Note that the built-in templates of the formats are not guaranteed to be stable.
They can be found in https://github.com/googlecodelabs/tools/tree/master/claat/render.
Please avoid using default templates in production. Use your own copies.

To use a custom format, specify a local file path to a Go template file.
More info on Go templates: https://golang.org/pkg/text/template/.

Each 'src' can be either a remote HTTP resource or a local file.
Source formats currently supported are:

- Google Doc (Codelab Format, go/codelab-guide)
- Markdown

When 'src' is a Google Doc, it must be specified as a doc ID,
omitting https://docs.google.com/... part.

Instead of writing to an output directory, use "-o -" to specify
stdout. In this case images and metadata are not exported.
When writing to a directory, existing files will be overwritten.

The program exits with non-zero code if at least one src could not be exported.

## Build command

Install all the dependencies needed by the codelab unless already installed.
This is done automatically by the serve command.
To clean up and rebuild all the dependencies, remove bower_components
directory and run the build command again

## Serve command

Serve provides a simple web server for viewing exported codelabs.
It takes no arguments and presents the current directory contents.
Clicking on a directory representing an exported codelab will load
all the required dependencies and render the generated codelab as
it would appear in production.

The serve command always runs the build command first.

The serve command takes a -addr host:port option, to specify the
desired hostname or IP address and port number to bind to.

## Update command

Update scans one or more 'src' local directories for codelab.json metadata
files, recursively. A directory containing the metadata file is expected
to be a codelab previously created with the export command.

Current directory is assumed if no 'src' argument is given.

Each found codelab is then re-exported using parameters from the metadata file.
Unused codelab assets will be deleted, as well as the entire codelab directory,
if codelab ID has changed since last update or export.

In the latter case, where codelab ID has changed, the new directory
will be placed alongside the old one. In other words, it will have the same ancestor
as the old one.

While -prefix and -ga can override existing codelab metadata, the other
arguments have no effect during update.

The program does not follow symbolic links and exits with non-zero code
if no metadata found or at least one src could not be updated.

## Flags

`
<|endoftext|>


<commit_before>package util

import (
	b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
)

// Key is a string representation of multihash for use with maps.
type Key string

// String is utililty function for printing out keys as strings (Pretty).
func (k Key) String() string {
	return k.Pretty()
}

// Pretty returns Key in a b58 encoded string
func (k Key) Pretty() string {
	return b58.Encode([]byte(k))
}

// DsKey returns a Datastore key
func (k Key) DsKey() ds.Key {
	return ds.NewKey(string(k))
}

// KeyFromDsKey returns a Datastore key
func KeyFromDsKey(dsk ds.Key) Key {
	return Key(dsk.BaseNamespace())
}

// B58KeyConverter -- for KeyTransform datastores
// (static as only one obj needed)
var B58KeyConverter = b58KeyConverter{}

type b58KeyConverter struct{}

// ConvertKey returns a B58 encoded Datastore key
// TODO: this is hacky because it encodes every path component. some
// path components may be proper strings already...
func (b58KeyConverter) ConvertKey(dsk ds.Key) ds.Key {
	k := ds.NewKey("/")
	for _, n := range dsk.Namespaces() {
		k = k.ChildString(b58.Encode([]byte(n)))
	}
	return k
}

// InvertKey returns a b58 decoded Datastore key
// TODO: this is hacky because it encodes every path component. some
// path components may be proper strings already...
func (b58KeyConverter) InvertKey(dsk ds.Key) ds.Key {
	k := ds.NewKey("/")
	for _, n := range dsk.Namespaces() {
		k = k.ChildString(string(b58.Decode(n)))
	}
	return k
}

// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits
func Hash(data []byte) mh.Multihash {
	h, err := mh.Sum(data, mh.SHA2_256, -1)
	if err != nil {
		// this error can be safely ignored (panic) because multihash only fails
		// from the selection of hash function. If the fn + length are valid, it
		// won't error.
		panic("multihash failed to hash using SHA2_256.")
	}
	return h
}

// IsValidHash checks whether a given hash is valid (b58 decodable, len > 0)
func IsValidHash(s string) bool {
	out := b58.Decode(s)
	if out == nil || len(out) == 0 {
		return false
	}
	return true
}

// XOR takes two byte slices, XORs them together, returns the resulting slice.
func XOR(a, b []byte) []byte {
	c := make([]byte, len(a))
	for i := 0; i < len(a); i++ {
		c[i] = a[i] ^ b[i]
	}
	return c
}
<commit_msg>key marshalling + b58 encoding<commit_after>package util

import (
	"encoding/json"
	"fmt"

	b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
)

// Key is a string representation of multihash for use with maps.
type Key string

// String is utililty function for printing out keys as strings (Pretty).
func (k Key) String() string {
	return k.Pretty()
}

// Pretty returns Key in a b58 encoded string
// TODO: deprecate Pretty. bad name.
func (k Key) Pretty() string {
	return k.B58String()
}

// B58String returns Key in a b58 encoded string
func (k Key) B58String() string {
	return B58KeyEncode(k)
}

// B58KeyDecode returns Key from a b58 encoded string
func B58KeyDecode(s string) Key {
	return Key(string(b58.Decode(s)))
}

// B58KeyEncode returns Key in a b58 encoded string
func B58KeyEncode(k Key) string {
	return b58.Encode([]byte(k))
}

// DsKey returns a Datastore key
func (k Key) DsKey() ds.Key {
	return ds.NewKey(string(k))
}

// UnmarshalJSON returns a JSON-encoded Key (string)
func (k *Key) UnmarshalJSON(mk []byte) error {
	var s string
	err := json.Unmarshal(mk, &s)
	if err != nil {
		return err
	}

	*k = Key(string(b58.Decode(s)))
	if len(*k) == 0 && len(s) > 2 { // if b58.Decode fails, k == ""
		return fmt.Errorf("Key.UnmarshalJSON: invalid b58 string: %v", mk)
	}
	log.Info("Unmarshal in: %s \"%s\"", *k, s)
	return nil
}

// MarshalJSON returns a JSON-encoded Key (string)
func (k *Key) MarshalJSON() ([]byte, error) {
	b, err := json.Marshal(b58.Encode([]byte(*k)))
	log.Info("Marshal out: %s %s", *k, b)
	return b, err
}

// KeyFromDsKey returns a Datastore key
func KeyFromDsKey(dsk ds.Key) Key {
	return Key(dsk.BaseNamespace())
}

// B58KeyConverter -- for KeyTransform datastores
// (static as only one obj needed)
var B58KeyConverter = b58KeyConverter{}

type b58KeyConverter struct{}

// ConvertKey returns a B58 encoded Datastore key
// TODO: this is hacky because it encodes every path component. some
// path components may be proper strings already...
func (b58KeyConverter) ConvertKey(dsk ds.Key) ds.Key {
	k := ds.NewKey("/")
	for _, n := range dsk.Namespaces() {
		k = k.ChildString(b58.Encode([]byte(n)))
	}
	return k
}

// InvertKey returns a b58 decoded Datastore key
// TODO: this is hacky because it encodes every path component. some
// path components may be proper strings already...
func (b58KeyConverter) InvertKey(dsk ds.Key) ds.Key {
	k := ds.NewKey("/")
	for _, n := range dsk.Namespaces() {
		k = k.ChildString(string(b58.Decode(n)))
	}
	return k
}

// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits
func Hash(data []byte) mh.Multihash {
	h, err := mh.Sum(data, mh.SHA2_256, -1)
	if err != nil {
		// this error can be safely ignored (panic) because multihash only fails
		// from the selection of hash function. If the fn + length are valid, it
		// won't error.
		panic("multihash failed to hash using SHA2_256.")
	}
	return h
}

// IsValidHash checks whether a given hash is valid (b58 decodable, len > 0)
func IsValidHash(s string) bool {
	out := b58.Decode(s)
	if out == nil || len(out) == 0 {
		return false
	}
	return true
}

// XOR takes two byte slices, XORs them together, returns the resulting slice.
func XOR(a, b []byte) []byte {
	c := make([]byte, len(a))
	for i := 0; i < len(a); i++ {
		c[i] = a[i] ^ b[i]
	}
	return c
}
<|endoftext|>


<commit_before>package utils

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/plusvic/go-ansi"
)

// Coordinator coordinates the output of multiple threads to stdout.
type Coordinator struct {
	Threads int
}

// StringReader is the
type StringReader interface {
	ReadString() string
}

// StringArrayReader is a wrapper around a slice of strings that implements
// the StringReader interface.
type StringArrayReader struct {
	strings []string
	pos     int
}

// NewStringArrayReader creates a new StringArrayReader.
func NewStringArrayReader(strings []string) *StringArrayReader {
	return &StringArrayReader{strings: strings}
}

// ReadString reads one string from StringArrayReader. It returns an empty
// string if all the strings have been read already. This implies that StringArrayReader
// can not contain empty strings.
func (sar *StringArrayReader) ReadString() string {
	if sar.pos == len(sar.strings) {
		return ""
	}
	s := sar.strings[sar.pos]
	sar.pos++
	return s
}

// StringIOReader is a wrapper around a bufio.Scanner that implements the
// StringReader interface.
type StringIOReader struct {
	scanner *bufio.Scanner
}

// NewStringIOReader creates a new StringIOReader.
func NewStringIOReader(r io.Reader) *StringIOReader {
	return &StringIOReader{scanner: bufio.NewScanner(r)}
}

// ReadString reads one string from StringIOReader. It returns an empty
// string if all the strings have been read already. This implies that StringIOReader
// can not contain empty strings.
func (sir *StringIOReader) ReadString() string {
	for sir.scanner.Scan() {
		s := strings.TrimSpace(sir.scanner.Text())
		if s != "" {
			return s
		}
	}
	return ""
}

type FilteredStringReader struct {
	reader StringReader
	re     *regexp.Regexp
}

func NewFilteredStringReader(r StringReader, re *regexp.Regexp) *FilteredStringReader {
	return &FilteredStringReader{reader: r, re: re}
}

func (fsr *FilteredStringReader) ReadString() string {
	s := fsr.reader.ReadString()
	for !fsr.re.MatchString(s) && s != "" {
		s = fsr.reader.ReadString()
	}
	return s
}

type DoerState struct {
	Progress string
}

type Doer interface {
	Do(string, *DoerState) string
}

// NewCoordinator ...
func NewCoordinator(threads int) *Coordinator {
	return &Coordinator{Threads: threads}
}

// DoWithArgReader ...
func (c *Coordinator) DoWithArgReader(doer Doer, argReader StringReader) {

	args := make([]string, 0)
	for arg := argReader.ReadString(); arg != ""; arg = argReader.ReadString() {
		args = append(args, arg)
	}

	argsCh := make(chan string)
	go func() {
		for _, arg := range args {
			argsCh <- arg
		}
		close(argsCh)
	}()

	c.DoWithArgCh(doer, argsCh)
}

// DoWithArgCh ...
func (c *Coordinator) DoWithArgCh(doer Doer, argsCh <-chan string) {

	resultsCh := make(chan string, c.Threads)
	doerStates := make([]DoerState, c.Threads)
	doersWg := &sync.WaitGroup{}

	for i := 0; i < c.Threads; i++ {
		doersWg.Add(1)
		go func(i int) {
			for arg := range argsCh {
				resultsCh <- doer.Do(arg, &doerStates[i])
				doerStates[i].Progress = ""
			}
			doersWg.Done()
		}(i)
	}

	printingWg := &sync.WaitGroup{}
	printingWg.Add(1)

	go printResults(resultsCh, doerStates, printingWg)

	doersWg.Wait()
	close(resultsCh)
	printingWg.Wait()
}

func printResults(resCh chan string, doerStates []DoerState, wg *sync.WaitGroup) {
Loop:
	for {
		select {
		case res, ok := <-resCh:
			if !ok {
				break Loop
			}
			ansi.Printf("%s", res)
			ansi.EraseInLine(0) // Clear to the end of the line.
			fmt.Println()
		default:
			// Print progress for pending workers
			lines := 0
			for _, ds := range doerStates {
				if ds.Progress != "" {
					ansi.Printf("%s", ds.Progress)
					ansi.EraseInLine(0) // Clear to the end of the line.
					fmt.Println()
					lines++
				}
			}
			time.Sleep(time.Millisecond * 250)
			if lines > 0 {
				// Move cursor up, to the line it was before printing worker's progress
				ansi.CursorNextLine(lines)
			}
		}
	}
	ansi.Printf("%c[0J", 27)
	wg.Done()
}
<commit_msg>Fix issue while printing doers progress.<commit_after>package utils

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/plusvic/go-ansi"
)

// Coordinator coordinates the output of multiple threads to stdout.
type Coordinator struct {
	Threads int
}

// StringReader is the
type StringReader interface {
	ReadString() string
}

// StringArrayReader is a wrapper around a slice of strings that implements
// the StringReader interface.
type StringArrayReader struct {
	strings []string
	pos     int
}

// NewStringArrayReader creates a new StringArrayReader.
func NewStringArrayReader(strings []string) *StringArrayReader {
	return &StringArrayReader{strings: strings}
}

// ReadString reads one string from StringArrayReader. It returns an empty
// string if all the strings have been read already. This implies that StringArrayReader
// can not contain empty strings.
func (sar *StringArrayReader) ReadString() string {
	if sar.pos == len(sar.strings) {
		return ""
	}
	s := sar.strings[sar.pos]
	sar.pos++
	return s
}

// StringIOReader is a wrapper around a bufio.Scanner that implements the
// StringReader interface.
type StringIOReader struct {
	scanner *bufio.Scanner
}

// NewStringIOReader creates a new StringIOReader.
func NewStringIOReader(r io.Reader) *StringIOReader {
	return &StringIOReader{scanner: bufio.NewScanner(r)}
}

// ReadString reads one string from StringIOReader. It returns an empty
// string if all the strings have been read already. This implies that StringIOReader
// can not contain empty strings.
func (sir *StringIOReader) ReadString() string {
	for sir.scanner.Scan() {
		s := strings.TrimSpace(sir.scanner.Text())
		if s != "" {
			return s
		}
	}
	return ""
}

type FilteredStringReader struct {
	reader StringReader
	re     *regexp.Regexp
}

func NewFilteredStringReader(r StringReader, re *regexp.Regexp) *FilteredStringReader {
	return &FilteredStringReader{reader: r, re: re}
}

func (fsr *FilteredStringReader) ReadString() string {
	s := fsr.reader.ReadString()
	for !fsr.re.MatchString(s) && s != "" {
		s = fsr.reader.ReadString()
	}
	return s
}

type DoerState struct {
	Progress string
}

type Doer interface {
	Do(string, *DoerState) string
}

// NewCoordinator ...
func NewCoordinator(threads int) *Coordinator {
	return &Coordinator{Threads: threads}
}

// DoWithArgReader ...
func (c *Coordinator) DoWithArgReader(doer Doer, argReader StringReader) {

	args := make([]string, 0)
	for arg := argReader.ReadString(); arg != ""; arg = argReader.ReadString() {
		args = append(args, arg)
	}

	argsCh := make(chan string)
	go func() {
		for _, arg := range args {
			argsCh <- arg
		}
		close(argsCh)
	}()

	c.DoWithArgCh(doer, argsCh)
}

// DoWithArgCh ...
func (c *Coordinator) DoWithArgCh(doer Doer, argsCh <-chan string) {

	resultsCh := make(chan string, c.Threads)
	doerStates := make([]DoerState, c.Threads)
	doersWg := &sync.WaitGroup{}

	for i := 0; i < c.Threads; i++ {
		doersWg.Add(1)
		go func(i int) {
			for arg := range argsCh {
				resultsCh <- doer.Do(arg, &doerStates[i])
				doerStates[i].Progress = ""
			}
			doersWg.Done()
		}(i)
	}

	printingWg := &sync.WaitGroup{}
	printingWg.Add(1)

	go printResults(resultsCh, doerStates, printingWg)

	doersWg.Wait()
	close(resultsCh)
	printingWg.Wait()
}

func printResults(resCh chan string, doerStates []DoerState, wg *sync.WaitGroup) {
Loop:
	for {
		select {
		case res, ok := <-resCh:
			if !ok {
				break Loop
			}
			ansi.Printf("%s", res)
			ansi.EraseInLine(0) // Clear to the end of the line.
			fmt.Println()
		default:
			// Print progress for pending workers
			lines := 0
			for _, ds := range doerStates {
				if ds.Progress != "" {
					ansi.Printf("%s", ds.Progress)
					ansi.EraseInLine(0) // Clear to the end of the line.
					fmt.Println()
					lines++
				}
			}
			time.Sleep(time.Millisecond * 250)
			if lines > 0 {
				// Move cursor up, to the line it was before printing worker's progress
				ansi.CursorPreviousLine(lines)
			}
		}
	}
	wg.Done()
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach Authors.
//
// 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

/*
Package client provides clients for accessing the various externally-facing
Cockroach database endpoints.

DB Client

The DB client is a fully-featured client of Cockroach's key-value database. It
provides a simple, synchronous interface well-suited to parallel updates and
queries.

The simplest way to use the client is through the Run method. Run synchronously
invokes the call, fills in the reply and returns an error. The example
below shows a get and a put.

	db, err := client.Open("rpcs://root@localhost:26257")
	if err != nil {
		log.Fatal(err)
	}
	if err := db.Put("a", "hello"); err != nil {
		log.Fatal(err)
	}
	if gr, err := db.Get("a"); err != nil {
		log.Fatal(err)
	} else {
		log.Printf("%s", gr.ValueBytes())  // "hello"
	}

The API is synchronous, but accommodates efficient parallel updates and queries
using Batch objects. An arbitrary number of calls may be added to a Batch which
is executed using DB.Run. Note however that the individual calls within a batch
are not guaranteed to have atomic semantics. A transaction must be used to
guarantee atomicity. A simple example of using a Batch which does two scans in
parallel and then sends a sequence of puts in parallel:

	db, err := client.Open("rpcs://root@localhost:26257")
	if err != nil {
		log.Fatal(err)
	}

	b1 := &client.Batch{}
	b1.Scan("a", "c\x00", 1000)
	b1.Scan("x", "z\x00", 1000)

	// Run sends both scans in parallel and returns the first error or nil.
	if err := db.Run(b1); err != nil {
		log.Fatal(err)
	}

	acResult := b1.Results[0]
	xzResult := b1.Results[1]

	// Append maximum value from "a"-"c" to all values from "x"-"z".
	max := []byte(nil)
	for _, row := range acResult.Rows {
		if bytes.Compare(max, row.ValueBytes()) < 0 {
			max = row.ValueBytes()
		}
	}

	b2 := &client.Batch{}
	for _, row := range xzResult.Rows {
		b2.Put(row.Key, bytes.Join([][]byte{row.ValueBytes(), max}, []byte(nil)))
	}

	// Run all puts for parallel execution.
	if err := db.Run(b2); err != nil {
		log.Fatal(err)
	}

Transactions are supported through the DB.Txn() method, which takes a retryable
function, itself composed of the same simple mix of API calls typical of a
non-transactional operation. Within the context of the Txn() call, all method
invocations are transparently given necessary transactional details, and
conflicts are handled with backoff/retry loops and transaction restarts as
necessary. An example of using transactions with parallel writes:

	db, err := client.Open("rpcs://root@localhost:26257")
	if err != nil {
		log.Fatal(err)
	}

	err := db.Txn(func(txn *client.Txn) error {
		b := &client.Batch{}
		for i := 0; i < 100; i++ {
			key := fmt.Sprintf("testkey-%02d", i)
			b.Put(key, "test value")
		}

		// Note that the Txn client is flushed automatically when this function
		// returns success (i.e. nil). Calling CommitInBatch explicitly can
		// sometimes reduce the number of RPCs.
		return txn.CommitInBatch(b)
	})
	if err != nil {
		log.Fatal(err)
	}

Note that with Cockroach's lock-free transactions, clients should expect
retries as a matter of course. This is why the transaction functionality is
exposed through a retryable function. The retryable function should have no
side effects which are not idempotent.

Transactions should endeavor to use batches to perform multiple operations in a
single RPC. In addition to the reduced number of RPCs to the server, this
allows writes to the same range to be batched together. In cases where the
entire transaction affects only a single range, transactions can commit in a
single round trip.
*/
package client
<commit_msg>Mark cockroach/client as deprecated in its documentation.<commit_after>// Copyright 2014 The Cockroach Authors.
//
// 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

/*
Package client and its KV API has been deprecated for external usage. Please use
a postgres-compatible SQL driver (e.g. github.com/lib/pq). For more details, see
http://www.cockroachlabs.com/blog/sql-in-cockroachdb-mapping-table-data-to-key-value-storage/.

Package client provides clients for accessing the various externally-facing
Cockroach database endpoints.

DB Client

The DB client is a fully-featured client of Cockroach's key-value database. It
provides a simple, synchronous interface well-suited to parallel updates and
queries.

The simplest way to use the client is through the Run method. Run synchronously
invokes the call, fills in the reply and returns an error. The example
below shows a get and a put.

	db, err := client.Open("rpcs://root@localhost:26257")
	if err != nil {
		log.Fatal(err)
	}
	if err := db.Put("a", "hello"); err != nil {
		log.Fatal(err)
	}
	if gr, err := db.Get("a"); err != nil {
		log.Fatal(err)
	} else {
		log.Printf("%s", gr.ValueBytes())  // "hello"
	}

The API is synchronous, but accommodates efficient parallel updates and queries
using Batch objects. An arbitrary number of calls may be added to a Batch which
is executed using DB.Run. Note however that the individual calls within a batch
are not guaranteed to have atomic semantics. A transaction must be used to
guarantee atomicity. A simple example of using a Batch which does two scans in
parallel and then sends a sequence of puts in parallel:

	db, err := client.Open("rpcs://root@localhost:26257")
	if err != nil {
		log.Fatal(err)
	}

	b1 := &client.Batch{}
	b1.Scan("a", "c\x00", 1000)
	b1.Scan("x", "z\x00", 1000)

	// Run sends both scans in parallel and returns the first error or nil.
	if err := db.Run(b1); err != nil {
		log.Fatal(err)
	}

	acResult := b1.Results[0]
	xzResult := b1.Results[1]

	// Append maximum value from "a"-"c" to all values from "x"-"z".
	max := []byte(nil)
	for _, row := range acResult.Rows {
		if bytes.Compare(max, row.ValueBytes()) < 0 {
			max = row.ValueBytes()
		}
	}

	b2 := &client.Batch{}
	for _, row := range xzResult.Rows {
		b2.Put(row.Key, bytes.Join([][]byte{row.ValueBytes(), max}, []byte(nil)))
	}

	// Run all puts for parallel execution.
	if err := db.Run(b2); err != nil {
		log.Fatal(err)
	}

Transactions are supported through the DB.Txn() method, which takes a retryable
function, itself composed of the same simple mix of API calls typical of a
non-transactional operation. Within the context of the Txn() call, all method
invocations are transparently given necessary transactional details, and
conflicts are handled with backoff/retry loops and transaction restarts as
necessary. An example of using transactions with parallel writes:

	db, err := client.Open("rpcs://root@localhost:26257")
	if err != nil {
		log.Fatal(err)
	}

	err := db.Txn(func(txn *client.Txn) error {
		b := &client.Batch{}
		for i := 0; i < 100; i++ {
			key := fmt.Sprintf("testkey-%02d", i)
			b.Put(key, "test value")
		}

		// Note that the Txn client is flushed automatically when this function
		// returns success (i.e. nil). Calling CommitInBatch explicitly can
		// sometimes reduce the number of RPCs.
		return txn.CommitInBatch(b)
	})
	if err != nil {
		log.Fatal(err)
	}

Note that with Cockroach's lock-free transactions, clients should expect
retries as a matter of course. This is why the transaction functionality is
exposed through a retryable function. The retryable function should have no
side effects which are not idempotent.

Transactions should endeavor to use batches to perform multiple operations in a
single RPC. In addition to the reduced number of RPCs to the server, this
allows writes to the same range to be batched together. In cases where the
entire transaction affects only a single range, transactions can commit in a
single round trip.
*/
package client
<|endoftext|>


<commit_before>package modbusone

import (
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"time"
)

//RTUClient implements Client/Master side logic for RTU over a SerialContext to
//be used by a ProtocalHandler
type RTUClient struct {
	com                  SerialContext
	packetReader         io.Reader
	SlaveID              byte
	serverProcessingTime time.Duration
	actions              chan rtuAction
}

//RTUClient is a Server
var _ Server = &RTUClient{}

//NewRTUCLient create a new client communicating over SerialContext with the
//give slaveID as default.
func NewRTUCLient(com SerialContext, slaveID byte) *RTUClient {
	r := RTUClient{
		com:                  com,
		packetReader:         NewRTUPacketReader(com, true, StartingSerialBufferSide),
		SlaveID:              slaveID,
		serverProcessingTime: time.Second,
		actions:              make(chan rtuAction),
	}
	return &r
}

//SetServerProcessingTime sets the time to wait for a server response, the total
//wait time also includes the time needed for data transmission
func (c *RTUClient) SetServerProcessingTime(t time.Duration) {
	c.serverProcessingTime = t
}

//GetTransactionTimeOut returns the total time to wait for a transaction
//(server response) to time out, given the expected length of RTU packets.
//This function is also used internally to calculate timeout.
func (c *RTUClient) GetTransactionTimeOut(reqLen, ansLen int) time.Duration {
	l := reqLen + ansLen
	return c.com.BytesDelay(l) + c.serverProcessingTime
}

type rtuAction struct {
	t       clientActionType
	data    RTU
	err     error
	errChan chan<- error
}

//ErrServerTimeOut is the time out error for StartTransaction
var ErrServerTimeOut = errors.New("server timed out")

type clientActionType int

const (
	clientStart clientActionType = iota
	clientRead
	clientError
)

func (a clientActionType) String() string {
	switch a {
	case clientStart:
		return "start"
	case clientRead:
		return "read"
	case clientError:
		return "error"
	}
	return fmt.Sprintf("clientActionType %d", a)
}

//Serve serves RTUClient side handlers, must close SerialContext after error is
//returned, to clean up.
func (c *RTUClient) Serve(handler ProtocalHandler) error {
	delay := c.com.MinDelay()

	go func() {
		//Reader loop that always ready to received data. This make sure that read
		//data is always new(ish), to dump data out that is received during an
		//unexpected time.
		rb := make([]byte, MaxRTUSize)
		for {
			n, err := c.packetReader.Read(rb)
			if err != nil {
				debugf("RTUClient read err:%v\n", err)
				c.actions <- rtuAction{t: clientRead, err: err}
				break
			}
			r := RTU(rb[:n])
			debugf("RTUClient read packet:%v\n", hex.EncodeToString(r))
			c.actions <- rtuAction{t: clientRead, data: r}
		}
	}()

	for {
		act := <-c.actions
		switch act.t {
		default:
			debugf("RTUClient drop unexpected: %v", act)
			continue
		case clientError:
			return act.err
		case clientStart:
		}
		ap := act.data.fastGetPDU()
		afc := ap.GetFunctionCode()
		if afc.IsWriteToServer() {
			data, err := handler.OnRead(ap)
			if err != nil {
				act.errChan <- err
				continue
			}
			act.data = MakeRTU(act.data[0], ap.MakeWriteRequest(data))
			ap = act.data.fastGetPDU()
		}
		time.Sleep(delay)
		_, err := c.com.Write(act.data)
		if err != nil {
			act.errChan <- err
			return err
		}
		if act.data[0] == 0 {
			continue // do not wait for read on multicast
		}
		timeOutChan := time.After(c.GetTransactionTimeOut(len(act.data), MaxRTUSize))

	READ_LOOP:
		for {
		SELECT:
			select {
			case <-timeOutChan:
				act.errChan <- ErrServerTimeOut
				break READ_LOOP
			case react := <-c.actions:
				switch react.t {
				default:
					err := fmt.Errorf("unexpected action:%s", react.t)
					act.errChan <- err
					return err
				case clientError:
					return react.err
				case clientRead:
					//test for read error
					if react.err != nil {
						return react.err
					}
				}
				if react.data[0] != act.data[0] {
					debugf("RTUClient unexpected slaveId:%v in %v\n", act.data[0], hex.EncodeToString(react.data))
					break SELECT
				}
				rp, err := react.data.GetPDU()
				if err != nil {
					act.errChan <- err
					break READ_LOOP
				}
				hasErr, fc := rp.GetFunctionCode().SeparateError()
				if hasErr && fc == afc {
					handler.OnError(ap, rp)
					act.errChan <- fmt.Errorf("server reply with exception:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if !MatchPDU(act.data.fastGetPDU(), rp) {
					act.errChan <- fmt.Errorf("unexpected reply:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if !afc.IsWriteToServer() {
					//read from server, write here
					bs, err := rp.GetReplyValues()
					if err != nil {
						act.errChan <- err
						break READ_LOOP
					}
					err = handler.OnWrite(ap, bs)
					act.errChan <- err //success if nill
					break READ_LOOP
				}
				act.errChan <- nil //success
				break READ_LOOP
			}
		}
	}
}

//DoTransaction starts a transaction, and returns a channel that returns an error
//or nil, with the default slaveID.
//
//DoTransaction is blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *RTUClient) DoTransaction(req PDU) error {
	errChan := make(chan error)
	c.StartTransactionToServer(c.SlaveID, req, errChan)
	return <-errChan
}

//StartTransactionToServer starts a transaction, with a custom slaveID.
//errChan is required and usable, an error is set is the transaction failed, or
//nil for success.
//
//StartTransactionToServer is not blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *RTUClient) StartTransactionToServer(slaveID byte, req PDU, errChan chan error) {
	c.actions <- rtuAction{t: clientStart, data: MakeRTU(slaveID, req), errChan: errChan}
}

//RTUTransactionStarter is an interface implemented by RTUClient.
type RTUTransactionStarter interface {
	StartTransactionToServer(slaveID byte, req PDU, errChan chan error)
}

//DoTransactions runs the reqs transactions in order.
//If any error is encountered, it returns early and reports the index number and
//error message
func DoTransactions(c RTUTransactionStarter, slaveID byte, reqs []PDU) (int, error) {
	errChan := make(chan error)
	for i, r := range reqs {
		c.StartTransactionToServer(slaveID, r, errChan)
		err := <-errChan
		if err != nil {
			return i, err
		}
	}
	return len(reqs), nil
}

//MakePDURequestHeaders generates the list of PDU request headers by spliting quantity
//into allowed sizes.
//Returns an error if quantitiy is out of range.
func MakePDURequestHeaders(fc FunctionCode, address, quantity uint16, appendTO []PDU) ([]PDU, error) {
	return MakePDURequestHeadersSized(fc, address, quantity, fc.MaxPerPacket(), appendTO)
}

//MakePDURequestHeaders generates the list of PDU request headers by spliting quantity
//into sizes of maxPerPacket or less.
//Returns an error if quantitiy is out of range.
//
//You can use FunctionCode.MaxPerPacketSized to calculate one with the wanted byte length.
func MakePDURequestHeadersSized(fc FunctionCode, address, quantity uint16, maxPerPacket uint16, appendTO []PDU) ([]PDU, error) {
	if uint(address)+uint(quantity) > uint(fc.MaxRange()) {
		return nil, fmt.Errorf("quantitiy is out of range")
	}
	q := maxPerPacket
	for quantity > 0 {
		if quantity < maxPerPacket {
			q = quantity
		}
		pdu, err := fc.MakeRequestHeader(address, q)
		if err != nil {
			return nil, err
		}
		appendTO = append(appendTO, pdu)
		address += q
		quantity -= q
	}
	return appendTO, nil
}
<commit_msg>fix doc<commit_after>package modbusone

import (
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"time"
)

//RTUClient implements Client/Master side logic for RTU over a SerialContext to
//be used by a ProtocalHandler
type RTUClient struct {
	com                  SerialContext
	packetReader         io.Reader
	SlaveID              byte
	serverProcessingTime time.Duration
	actions              chan rtuAction
}

//RTUClient is a Server
var _ Server = &RTUClient{}

//NewRTUCLient create a new client communicating over SerialContext with the
//give slaveID as default.
func NewRTUCLient(com SerialContext, slaveID byte) *RTUClient {
	r := RTUClient{
		com:                  com,
		packetReader:         NewRTUPacketReader(com, true, StartingSerialBufferSide),
		SlaveID:              slaveID,
		serverProcessingTime: time.Second,
		actions:              make(chan rtuAction),
	}
	return &r
}

//SetServerProcessingTime sets the time to wait for a server response, the total
//wait time also includes the time needed for data transmission
func (c *RTUClient) SetServerProcessingTime(t time.Duration) {
	c.serverProcessingTime = t
}

//GetTransactionTimeOut returns the total time to wait for a transaction
//(server response) to time out, given the expected length of RTU packets.
//This function is also used internally to calculate timeout.
func (c *RTUClient) GetTransactionTimeOut(reqLen, ansLen int) time.Duration {
	l := reqLen + ansLen
	return c.com.BytesDelay(l) + c.serverProcessingTime
}

type rtuAction struct {
	t       clientActionType
	data    RTU
	err     error
	errChan chan<- error
}

//ErrServerTimeOut is the time out error for StartTransaction
var ErrServerTimeOut = errors.New("server timed out")

type clientActionType int

const (
	clientStart clientActionType = iota
	clientRead
	clientError
)

func (a clientActionType) String() string {
	switch a {
	case clientStart:
		return "start"
	case clientRead:
		return "read"
	case clientError:
		return "error"
	}
	return fmt.Sprintf("clientActionType %d", a)
}

//Serve serves RTUClient side handlers, must close SerialContext after error is
//returned, to clean up.
func (c *RTUClient) Serve(handler ProtocalHandler) error {
	delay := c.com.MinDelay()

	go func() {
		//Reader loop that always ready to received data. This make sure that read
		//data is always new(ish), to dump data out that is received during an
		//unexpected time.
		rb := make([]byte, MaxRTUSize)
		for {
			n, err := c.packetReader.Read(rb)
			if err != nil {
				debugf("RTUClient read err:%v\n", err)
				c.actions <- rtuAction{t: clientRead, err: err}
				break
			}
			r := RTU(rb[:n])
			debugf("RTUClient read packet:%v\n", hex.EncodeToString(r))
			c.actions <- rtuAction{t: clientRead, data: r}
		}
	}()

	for {
		act := <-c.actions
		switch act.t {
		default:
			debugf("RTUClient drop unexpected: %v", act)
			continue
		case clientError:
			return act.err
		case clientStart:
		}
		ap := act.data.fastGetPDU()
		afc := ap.GetFunctionCode()
		if afc.IsWriteToServer() {
			data, err := handler.OnRead(ap)
			if err != nil {
				act.errChan <- err
				continue
			}
			act.data = MakeRTU(act.data[0], ap.MakeWriteRequest(data))
			ap = act.data.fastGetPDU()
		}
		time.Sleep(delay)
		_, err := c.com.Write(act.data)
		if err != nil {
			act.errChan <- err
			return err
		}
		if act.data[0] == 0 {
			continue // do not wait for read on multicast
		}
		timeOutChan := time.After(c.GetTransactionTimeOut(len(act.data), MaxRTUSize))

	READ_LOOP:
		for {
		SELECT:
			select {
			case <-timeOutChan:
				act.errChan <- ErrServerTimeOut
				break READ_LOOP
			case react := <-c.actions:
				switch react.t {
				default:
					err := fmt.Errorf("unexpected action:%s", react.t)
					act.errChan <- err
					return err
				case clientError:
					return react.err
				case clientRead:
					//test for read error
					if react.err != nil {
						return react.err
					}
				}
				if react.data[0] != act.data[0] {
					debugf("RTUClient unexpected slaveId:%v in %v\n", act.data[0], hex.EncodeToString(react.data))
					break SELECT
				}
				rp, err := react.data.GetPDU()
				if err != nil {
					act.errChan <- err
					break READ_LOOP
				}
				hasErr, fc := rp.GetFunctionCode().SeparateError()
				if hasErr && fc == afc {
					handler.OnError(ap, rp)
					act.errChan <- fmt.Errorf("server reply with exception:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if !MatchPDU(act.data.fastGetPDU(), rp) {
					act.errChan <- fmt.Errorf("unexpected reply:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if !afc.IsWriteToServer() {
					//read from server, write here
					bs, err := rp.GetReplyValues()
					if err != nil {
						act.errChan <- err
						break READ_LOOP
					}
					err = handler.OnWrite(ap, bs)
					act.errChan <- err //success if nill
					break READ_LOOP
				}
				act.errChan <- nil //success
				break READ_LOOP
			}
		}
	}
}

//DoTransaction starts a transaction, and returns a channel that returns an error
//or nil, with the default slaveID.
//
//DoTransaction is blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *RTUClient) DoTransaction(req PDU) error {
	errChan := make(chan error)
	c.StartTransactionToServer(c.SlaveID, req, errChan)
	return <-errChan
}

//StartTransactionToServer starts a transaction, with a custom slaveID.
//errChan is required and usable, an error is set is the transaction failed, or
//nil for success.
//
//StartTransactionToServer is not blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *RTUClient) StartTransactionToServer(slaveID byte, req PDU, errChan chan error) {
	c.actions <- rtuAction{t: clientStart, data: MakeRTU(slaveID, req), errChan: errChan}
}

//RTUTransactionStarter is an interface implemented by RTUClient.
type RTUTransactionStarter interface {
	StartTransactionToServer(slaveID byte, req PDU, errChan chan error)
}

//DoTransactions runs the reqs transactions in order.
//If any error is encountered, it returns early and reports the index number and
//error message
func DoTransactions(c RTUTransactionStarter, slaveID byte, reqs []PDU) (int, error) {
	errChan := make(chan error)
	for i, r := range reqs {
		c.StartTransactionToServer(slaveID, r, errChan)
		err := <-errChan
		if err != nil {
			return i, err
		}
	}
	return len(reqs), nil
}

//MakePDURequestHeaders generates the list of PDU request headers by spliting quantity
//into allowed sizes.
//Returns an error if quantitiy is out of range.
func MakePDURequestHeaders(fc FunctionCode, address, quantity uint16, appendTO []PDU) ([]PDU, error) {
	return MakePDURequestHeadersSized(fc, address, quantity, fc.MaxPerPacket(), appendTO)
}

//MakePDURequestHeadersSized generates the list of PDU request headers by spliting quantity
//into sizes of maxPerPacket or less.
//Returns an error if quantitiy is out of range.
//
//You can use FunctionCode.MaxPerPacketSized to calculate one with the wanted byte length.
func MakePDURequestHeadersSized(fc FunctionCode, address, quantity uint16, maxPerPacket uint16, appendTO []PDU) ([]PDU, error) {
	if uint(address)+uint(quantity) > uint(fc.MaxRange()) {
		return nil, fmt.Errorf("quantitiy is out of range")
	}
	q := maxPerPacket
	for quantity > 0 {
		if quantity < maxPerPacket {
			q = quantity
		}
		pdu, err := fc.MakeRequestHeader(address, q)
		if err != nil {
			return nil, err
		}
		appendTO = append(appendTO, pdu)
		address += q
		quantity -= q
	}
	return appendTO, nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"socialapi/config"
	"time"

	"github.com/koding/bongo"
)

type ChannelMessage struct {
	// unique identifier of the channel message
	Id int64 `json:"id"`

	// Body of the mesage
	Body string `json:"body"`

	// Generated Slug for body
	Slug string `json:"slug"                        sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// type of the message
	TypeConstant string `json:"typeConstant"        sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creator of the channel message
	AccountId int64 `json:"accountId"               sql:"NOT NULL"`

	// in which channel this message is created
	InitialChannelId int64 `json:"initialChannelId" sql:"NOT NULL"`

	// Creation date of the message
	CreatedAt time.Time `json:"createdAt"           sql:"DEFAULT:CURRENT_TIMESTAMP"`

	// Modification date of the message
	UpdatedAt time.Time `json:"updatedAt"           sql:"DEFAULT:CURRENT_TIMESTAMP"`

	// Deletion date of the channel message
	DeletedAt time.Time `json:"deletedAt"`
}

func (c *ChannelMessage) BeforeCreate() {
	c.DeletedAt = ZeroDate()
}

func (c *ChannelMessage) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *ChannelMessage) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *ChannelMessage) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *ChannelMessage) GetId() int64 {
	return c.Id
}

func (c ChannelMessage) TableName() string {
	return "api.channel_message"
}

const (
	ChannelMessage_TYPE_POST            = "post"
	ChannelMessage_TYPE_REPLY           = "reply"
	ChannelMessage_TYPE_JOIN            = "join"
	ChannelMessage_TYPE_LEAVE           = "leave"
	ChannelMessage_TYPE_CHAT            = "chat"
	ChannelMessage_TYPE_PRIVATE_MESSAGE = "privatemessage"
)

func NewChannelMessage() *ChannelMessage {
	return &ChannelMessage{}
}

func (c *ChannelMessage) ById(id int64) error {
	return bongo.B.ById(c, id)
}

func (c *ChannelMessage) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *ChannelMessage) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func bodyLenCheck(body string) error {
	if len(body) < config.Get().Limits.MessageBodyMinLen {
		return fmt.Errorf("Message Body Length should be greater than %d, yours is %d ", config.Get().Limits.MessageBodyMinLen, len(body))
	}

	return nil
}

// todo create a new message while updating the channel_message and delete other
// cases, since deletion is a soft delete, old instances will still be there
func (c *ChannelMessage) Update() error {
	if err := bodyLenCheck(c.Body); err != nil {
		return err
	}
	// only update body
	err := bongo.B.UpdatePartial(c,
		map[string]interface{}{
			"body": c.Body,
		},
	)
	return err
}

func (c *ChannelMessage) Create() error {
	if err := bodyLenCheck(c.Body); err != nil {
		return err
	}

	var err error
	c, err = Slugify(c)
	if err != nil {
		return err
	}

	return bongo.B.Create(c)
}

func (c *ChannelMessage) Delete() error {
	return bongo.B.Delete(c)
}

func (c *ChannelMessage) FetchByIds(ids []int64) ([]ChannelMessage, error) {
	var messages []ChannelMessage

	if len(ids) == 0 {
		return messages, nil
	}

	if err := bongo.B.FetchByIds(c, &messages, ids); err != nil {
		return nil, err
	}
	return messages, nil
}

func (c *ChannelMessage) BuildMessages(query *Query, messages []ChannelMessage) ([]*ChannelMessageContainer, error) {
	containers := make([]*ChannelMessageContainer, len(messages))
	if len(containers) == 0 {
		return containers, nil
	}

	for i, message := range messages {
		d := NewChannelMessage()
		*d = message
		data, err := d.BuildMessage(query)
		if err != nil {
			return containers, err
		}
		containers[i] = data
	}

	return containers, nil
}

func (c *ChannelMessage) BuildMessage(query *Query) (*ChannelMessageContainer, error) {
	cmc, err := c.FetchRelatives(query)
	if err != nil {
		return nil, err
	}

	mr := NewMessageReply()
	mr.MessageId = c.Id
	q := query
	q.Limit = 3
	replies, err := mr.List(query)
	if err != nil {
		return nil, err
	}

	repliesCount, err := mr.Count()
	if err != nil {
		return nil, err
	}
	cmc.RepliesCount = repliesCount

	populatedChannelMessagesReplies := make([]*ChannelMessageContainer, len(replies))
	for rl := 0; rl < len(replies); rl++ {
		cmrc, err := replies[rl].FetchRelatives(query)
		if err != nil {
			return nil, err
		}
		populatedChannelMessagesReplies[rl] = cmrc
	}

	cmc.Replies = populatedChannelMessagesReplies
	return cmc, nil
}

func (c *ChannelMessage) BuildEmptyMessageContainer() (*ChannelMessageContainer, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel message id is not set")
	}
	container := NewChannelMessageContainer()
	container.Message = c

	oldId, err := AccountOldIdById(c.AccountId)
	if err != nil {
		return nil, err
	}

	container.AccountOldId = oldId

	interactionContainer := NewInteractionContainer()
	interactionContainer.ActorsPreview = make([]string, 0)
	interactionContainer.IsInteracted = false
	interactionContainer.ActorsCount = 0

	container.Interactions = make(map[string]*InteractionContainer)
	container.Interactions["like"] = interactionContainer

	return container, nil
}

func (c *ChannelMessage) FetchRelatives(query *Query) (*ChannelMessageContainer, error) {
	container, err := c.BuildEmptyMessageContainer()
	if err != nil {
		return nil, err
	}

	i := NewInteraction()
	i.MessageId = c.Id

	// get preview
	query.Type = "like"
	query.Limit = 3
	interactorIds, err := i.List(query)
	if err != nil {
		return nil, err
	}

	oldIds, err := FetchOldIdsByAccountIds(interactorIds)
	if err != nil {
		return nil, err
	}

	interactionContainer := NewInteractionContainer()
	interactionContainer.ActorsPreview = oldIds

	// check if the current user is interacted in this thread
	isInteracted, err := i.IsInteracted(query.AccountId)
	if err != nil {
		return nil, err
	}

	interactionContainer.IsInteracted = isInteracted

	// fetch interaction count
	count, err := i.Count(query.Type)
	if err != nil {
		return nil, err
	}

	interactionContainer.ActorsCount = count

	container.Interactions["like"] = interactionContainer
	return container, nil
}

func (c *ChannelMessage) FetchMessagesByChannelId(channelId int64, q *Query) ([]ChannelMessage, error) {
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"account_id":         q.AccountId,
			"initial_channel_id": channelId,
			"type_constant":      q.Type,
		},
		Limit: q.Limit,
		Skip:  q.Skip,
		Sort: map[string]string{
			"created_at": "DESC",
		},
	}

	var messages []ChannelMessage
	if err := c.Some(&messages, query); err != nil {
		return nil, err
	}

	if messages == nil {
		return make([]ChannelMessage, 0), nil
	}
	return messages, nil
}
<commit_msg>Social: refactor FetchMessagesByChannelId function, make it more generalized<commit_after>package models

import (
	"errors"
	"fmt"
	"socialapi/config"
	"time"

	"github.com/koding/bongo"
)

type ChannelMessage struct {
	// unique identifier of the channel message
	Id int64 `json:"id"`

	// Body of the mesage
	Body string `json:"body"`

	// Generated Slug for body
	Slug string `json:"slug"                        sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// type of the message
	TypeConstant string `json:"typeConstant"        sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creator of the channel message
	AccountId int64 `json:"accountId"               sql:"NOT NULL"`

	// in which channel this message is created
	InitialChannelId int64 `json:"initialChannelId" sql:"NOT NULL"`

	// Creation date of the message
	CreatedAt time.Time `json:"createdAt"           sql:"DEFAULT:CURRENT_TIMESTAMP"`

	// Modification date of the message
	UpdatedAt time.Time `json:"updatedAt"           sql:"DEFAULT:CURRENT_TIMESTAMP"`

	// Deletion date of the channel message
	DeletedAt time.Time `json:"deletedAt"`
}

func (c *ChannelMessage) BeforeCreate() {
	c.DeletedAt = ZeroDate()
}

func (c *ChannelMessage) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *ChannelMessage) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *ChannelMessage) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *ChannelMessage) GetId() int64 {
	return c.Id
}

func (c ChannelMessage) TableName() string {
	return "api.channel_message"
}

const (
	ChannelMessage_TYPE_POST            = "post"
	ChannelMessage_TYPE_REPLY           = "reply"
	ChannelMessage_TYPE_JOIN            = "join"
	ChannelMessage_TYPE_LEAVE           = "leave"
	ChannelMessage_TYPE_CHAT            = "chat"
	ChannelMessage_TYPE_PRIVATE_MESSAGE = "privatemessage"
)

func NewChannelMessage() *ChannelMessage {
	return &ChannelMessage{}
}

func (c *ChannelMessage) ById(id int64) error {
	return bongo.B.ById(c, id)
}

func (c *ChannelMessage) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *ChannelMessage) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func bodyLenCheck(body string) error {
	if len(body) < config.Get().Limits.MessageBodyMinLen {
		return fmt.Errorf("Message Body Length should be greater than %d, yours is %d ", config.Get().Limits.MessageBodyMinLen, len(body))
	}

	return nil
}

// todo create a new message while updating the channel_message and delete other
// cases, since deletion is a soft delete, old instances will still be there
func (c *ChannelMessage) Update() error {
	if err := bodyLenCheck(c.Body); err != nil {
		return err
	}
	// only update body
	err := bongo.B.UpdatePartial(c,
		map[string]interface{}{
			"body": c.Body,
		},
	)
	return err
}

func (c *ChannelMessage) Create() error {
	if err := bodyLenCheck(c.Body); err != nil {
		return err
	}

	var err error
	c, err = Slugify(c)
	if err != nil {
		return err
	}

	return bongo.B.Create(c)
}

func (c *ChannelMessage) Delete() error {
	return bongo.B.Delete(c)
}

func (c *ChannelMessage) FetchByIds(ids []int64) ([]ChannelMessage, error) {
	var messages []ChannelMessage

	if len(ids) == 0 {
		return messages, nil
	}

	if err := bongo.B.FetchByIds(c, &messages, ids); err != nil {
		return nil, err
	}
	return messages, nil
}

func (c *ChannelMessage) BuildMessages(query *Query, messages []ChannelMessage) ([]*ChannelMessageContainer, error) {
	containers := make([]*ChannelMessageContainer, len(messages))
	if len(containers) == 0 {
		return containers, nil
	}

	for i, message := range messages {
		d := NewChannelMessage()
		*d = message
		data, err := d.BuildMessage(query)
		if err != nil {
			return containers, err
		}
		containers[i] = data
	}

	return containers, nil
}

func (c *ChannelMessage) BuildMessage(query *Query) (*ChannelMessageContainer, error) {
	cmc, err := c.FetchRelatives(query)
	if err != nil {
		return nil, err
	}

	mr := NewMessageReply()
	mr.MessageId = c.Id
	q := query
	q.Limit = 3
	replies, err := mr.List(query)
	if err != nil {
		return nil, err
	}

	repliesCount, err := mr.Count()
	if err != nil {
		return nil, err
	}
	cmc.RepliesCount = repliesCount

	populatedChannelMessagesReplies := make([]*ChannelMessageContainer, len(replies))
	for rl := 0; rl < len(replies); rl++ {
		cmrc, err := replies[rl].FetchRelatives(query)
		if err != nil {
			return nil, err
		}
		populatedChannelMessagesReplies[rl] = cmrc
	}

	cmc.Replies = populatedChannelMessagesReplies
	return cmc, nil
}

func (c *ChannelMessage) BuildEmptyMessageContainer() (*ChannelMessageContainer, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel message id is not set")
	}
	container := NewChannelMessageContainer()
	container.Message = c

	oldId, err := AccountOldIdById(c.AccountId)
	if err != nil {
		return nil, err
	}

	container.AccountOldId = oldId

	interactionContainer := NewInteractionContainer()
	interactionContainer.ActorsPreview = make([]string, 0)
	interactionContainer.IsInteracted = false
	interactionContainer.ActorsCount = 0

	container.Interactions = make(map[string]*InteractionContainer)
	container.Interactions["like"] = interactionContainer

	return container, nil
}

func (c *ChannelMessage) FetchRelatives(query *Query) (*ChannelMessageContainer, error) {
	container, err := c.BuildEmptyMessageContainer()
	if err != nil {
		return nil, err
	}

	i := NewInteraction()
	i.MessageId = c.Id

	// get preview
	query.Type = "like"
	query.Limit = 3
	interactorIds, err := i.List(query)
	if err != nil {
		return nil, err
	}

	oldIds, err := FetchOldIdsByAccountIds(interactorIds)
	if err != nil {
		return nil, err
	}

	interactionContainer := NewInteractionContainer()
	interactionContainer.ActorsPreview = oldIds

	// check if the current user is interacted in this thread
	isInteracted, err := i.IsInteracted(query.AccountId)
	if err != nil {
		return nil, err
	}

	interactionContainer.IsInteracted = isInteracted

	// fetch interaction count
	count, err := i.Count(query.Type)
	if err != nil {
		return nil, err
	}

	interactionContainer.ActorsCount = count

	container.Interactions["like"] = interactionContainer
	return container, nil
}

func generateMessageListQuery(channelId int64, q *Query) *bongo.Query {
	messageType := q.Type
	if messageType == "" {
		messageType = ChannelMessage_TYPE_POST
	}

	return &bongo.Query{
		Selector: map[string]interface{}{
			"account_id":         q.AccountId,
			"initial_channel_id": channelId,
			"type_constant":      messageType,
		},
		Limit: q.Limit,
		Skip:  q.Skip,
		Sort: map[string]string{
			"created_at": "DESC",
		},
	}
}

func (c *ChannelMessage) FetchMessagesByChannelId(channelId int64, q *Query) ([]ChannelMessage, error) {
	query := generateMessageListQuery(channelId, q)

	var messages []ChannelMessage
	if err := c.Some(&messages, query); err != nil {
		return nil, err
	}

	if messages == nil {
		return make([]ChannelMessage, 0), nil
	}
	return messages, nil
}
<|endoftext|>


<commit_before>package module

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"testing"

	getter "github.com/hashicorp/go-getter"
	version "github.com/hashicorp/go-version"
)

// map of module names and version for test module.
// only one version for now, as we only lookup latest from the registry
var testMods = map[string]string{
	"registry/foo/bar": "0.2.3",
	"registry/foo/baz": "1.10.0",
}

func latestVersion(versions []string) string {
	var col version.Collection
	for _, v := range versions {
		ver, err := version.NewVersion(v)
		if err != nil {
			panic(err)
		}
		col = append(col, ver)
	}

	sort.Sort(col)
	return col[len(col)-1].String()
}

// Just enough like a registry to exercise our code.
// Returns the location of the latest version
func mockRegistry() *httptest.Server {
	mux := http.NewServeMux()
	server := httptest.NewServer(mux)

	mux.Handle("/v1/modules/",
		http.StripPrefix("/v1/modules/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			p := strings.TrimLeft(r.URL.Path, "/")
			// handle download request
			download := regexp.MustCompile(`^(\w+/\w+/\w+)/download$`)

			// download lookup
			matches := download.FindStringSubmatch(p)
			if len(matches) != 2 {
				w.WriteHeader(http.StatusBadRequest)
				return
			}

			version, ok := testMods[matches[1]]
			if !ok {
				w.WriteHeader(http.StatusNotFound)
				return
			}

			location := fmt.Sprintf("%s/download/%s/%s", server.URL, matches[1], version)
			w.Header().Set(xTerraformGet, location)
			w.WriteHeader(http.StatusNoContent)
			// no body
			return
		})),
	)

	return server
}

func TestDetectRegistry(t *testing.T) {
	server := mockRegistry()
	defer server.Close()

	detector := registryDetector{
		api:    server.URL + "/v1/modules/",
		client: server.Client(),
	}

	for _, tc := range []struct {
		source   string
		location string
		found    bool
		err      bool
	}{
		{
			source:   "registry/foo/bar",
			location: "download/registry/foo/bar/0.2.3",
			found:    true,
		},
		{
			source:   "registry/foo/baz",
			location: "download/registry/foo/baz/1.10.0",
			found:    true,
		},
		// this should not be found, but not stop detection
		{
			source: "registry/foo/notfound",
			found:  false,
		},

		// a full url should not be detected
		{
			source: "http://example.com/registry/foo/notfound",
			found:  false,
		},

		// paths should not be detected
		{
			source: "./local/foo/notfound",
			found:  false,
		},
		{
			source: "/local/foo/notfound",
			found:  false,
		},

		// wrong number of parts can't be regisry IDs
		{
			source: "something/registry/foo/notfound",
			found:  false,
		},
	} {

		t.Run(tc.source, func(t *testing.T) {
			loc, ok, err := detector.Detect(tc.source, "")
			if (err == nil) == tc.err {
				t.Fatalf("expected error? %t; got error :%v", tc.err, err)
			}

			if ok != tc.found {
				t.Fatalf("expected OK == %t", tc.found)
			}

			loc = strings.TrimPrefix(loc, server.URL+"/")
			if strings.TrimPrefix(loc, server.URL) != tc.location {
				t.Fatalf("expected location: %q, got %q", tc.location, loc)
			}
		})

	}
}

// check that the full set of detectors works as expected
func TestDetectors(t *testing.T) {
	server := mockRegistry()
	defer server.Close()

	wd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	regDetector := &registryDetector{
		api:    server.URL + "/v1/modules/",
		client: server.Client(),
	}

	detectors := []getter.Detector{
		new(getter.GitHubDetector),
		new(getter.BitBucketDetector),
		new(getter.S3Detector),
		new(localDetector),
		regDetector,
	}

	for _, tc := range []struct {
		source   string
		location string
		fixture  string
		err      bool
	}{
		{
			source:   "registry/foo/bar",
			location: "download/registry/foo/bar/0.2.3",
		},
		// this should not be found, but not stop detection
		{
			source: "registry/foo/notfound",
			err:    true,
		},
		// a full url should be unchanged
		{
			source: "http://example.com/registry/foo/notfound?" +
				"checksum=sha256:f19056b80a426d797ff9e470da069c171a6c6befa83e2da7f6c706207742acab",
			location: "http://example.com/registry/foo/notfound?" +
				"checksum=sha256:f19056b80a426d797ff9e470da069c171a6c6befa83e2da7f6c706207742acab",
		},

		// forced getters will return untouched
		{
			source:   "git::http://example.com/registry/foo/notfound?param=value",
			location: "git::http://example.com/registry/foo/notfound?param=value",
		},

		// local paths should be detected as such, even if they're match
		// registry modules.
		{
			source: "./registry/foo/bar",
			err:    true,
		},
		{
			source: "/registry/foo/bar",
			err:    true,
		},

		// wrong number of parts can't be regisry IDs
		{
			source: "something/registry/foo/notfound",
			err:    true,
		},

		// make sure a local module that looks like a registry id takes precedence
		{
			source:  "namespace/identifier/provider",
			fixture: "discover-subdirs",
			// this should be found locally
			location: "file://" + filepath.Join(wd, fixtureDir, "discover-subdirs/namespace/identifier/provider"),
		},
	} {

		t.Run(tc.source, func(t *testing.T) {
			dir := wd
			if tc.fixture != "" {
				dir = filepath.Join(wd, fixtureDir, tc.fixture)
				if err := os.Chdir(dir); err != nil {
					t.Fatal(err)
				}
				defer os.Chdir(wd)
			}

			loc, err := getter.Detect(tc.source, dir, detectors)
			if (err == nil) == tc.err {
				t.Fatalf("expected error? %t; got error :%v", tc.err, err)
			}

			loc = strings.TrimPrefix(loc, server.URL+"/")
			if strings.TrimPrefix(loc, server.URL) != tc.location {
				t.Fatalf("expected location: %q, got %q", tc.location, loc)
			}
		})

	}
}
<commit_msg>Add basic ACC test<commit_after>package module

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"testing"

	getter "github.com/hashicorp/go-getter"
	version "github.com/hashicorp/go-version"
)

// map of module names and version for test module.
// only one version for now, as we only lookup latest from the registry
var testMods = map[string]string{
	"registry/foo/bar": "0.2.3",
	"registry/foo/baz": "1.10.0",
}

func latestVersion(versions []string) string {
	var col version.Collection
	for _, v := range versions {
		ver, err := version.NewVersion(v)
		if err != nil {
			panic(err)
		}
		col = append(col, ver)
	}

	sort.Sort(col)
	return col[len(col)-1].String()
}

// Just enough like a registry to exercise our code.
// Returns the location of the latest version
func mockRegistry() *httptest.Server {
	mux := http.NewServeMux()
	server := httptest.NewServer(mux)

	mux.Handle("/v1/modules/",
		http.StripPrefix("/v1/modules/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			p := strings.TrimLeft(r.URL.Path, "/")
			// handle download request
			download := regexp.MustCompile(`^(\w+/\w+/\w+)/download$`)

			// download lookup
			matches := download.FindStringSubmatch(p)
			if len(matches) != 2 {
				w.WriteHeader(http.StatusBadRequest)
				return
			}

			version, ok := testMods[matches[1]]
			if !ok {
				w.WriteHeader(http.StatusNotFound)
				return
			}

			location := fmt.Sprintf("%s/download/%s/%s", server.URL, matches[1], version)
			w.Header().Set(xTerraformGet, location)
			w.WriteHeader(http.StatusNoContent)
			// no body
			return
		})),
	)

	return server
}

func TestDetectRegistry(t *testing.T) {
	server := mockRegistry()
	defer server.Close()

	detector := registryDetector{
		api:    server.URL + "/v1/modules/",
		client: server.Client(),
	}

	for _, tc := range []struct {
		source   string
		location string
		found    bool
		err      bool
	}{
		{
			source:   "registry/foo/bar",
			location: "download/registry/foo/bar/0.2.3",
			found:    true,
		},
		{
			source:   "registry/foo/baz",
			location: "download/registry/foo/baz/1.10.0",
			found:    true,
		},
		// this should not be found, but not stop detection
		{
			source: "registry/foo/notfound",
			found:  false,
		},

		// a full url should not be detected
		{
			source: "http://example.com/registry/foo/notfound",
			found:  false,
		},

		// paths should not be detected
		{
			source: "./local/foo/notfound",
			found:  false,
		},
		{
			source: "/local/foo/notfound",
			found:  false,
		},

		// wrong number of parts can't be regisry IDs
		{
			source: "something/registry/foo/notfound",
			found:  false,
		},
	} {

		t.Run(tc.source, func(t *testing.T) {
			loc, ok, err := detector.Detect(tc.source, "")
			if (err == nil) == tc.err {
				t.Fatalf("expected error? %t; got error :%v", tc.err, err)
			}

			if ok != tc.found {
				t.Fatalf("expected OK == %t", tc.found)
			}

			loc = strings.TrimPrefix(loc, server.URL+"/")
			if strings.TrimPrefix(loc, server.URL) != tc.location {
				t.Fatalf("expected location: %q, got %q", tc.location, loc)
			}
		})

	}
}

// check that the full set of detectors works as expected
func TestDetectors(t *testing.T) {
	server := mockRegistry()
	defer server.Close()

	wd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	regDetector := &registryDetector{
		api:    server.URL + "/v1/modules/",
		client: server.Client(),
	}

	detectors := []getter.Detector{
		new(getter.GitHubDetector),
		new(getter.BitBucketDetector),
		new(getter.S3Detector),
		new(localDetector),
		regDetector,
	}

	for _, tc := range []struct {
		source   string
		location string
		fixture  string
		err      bool
	}{
		{
			source:   "registry/foo/bar",
			location: "download/registry/foo/bar/0.2.3",
		},
		// this should not be found, but not stop detection
		{
			source: "registry/foo/notfound",
			err:    true,
		},
		// a full url should be unchanged
		{
			source: "http://example.com/registry/foo/notfound?" +
				"checksum=sha256:f19056b80a426d797ff9e470da069c171a6c6befa83e2da7f6c706207742acab",
			location: "http://example.com/registry/foo/notfound?" +
				"checksum=sha256:f19056b80a426d797ff9e470da069c171a6c6befa83e2da7f6c706207742acab",
		},

		// forced getters will return untouched
		{
			source:   "git::http://example.com/registry/foo/notfound?param=value",
			location: "git::http://example.com/registry/foo/notfound?param=value",
		},

		// local paths should be detected as such, even if they're match
		// registry modules.
		{
			source: "./registry/foo/bar",
			err:    true,
		},
		{
			source: "/registry/foo/bar",
			err:    true,
		},

		// wrong number of parts can't be regisry IDs
		{
			source: "something/registry/foo/notfound",
			err:    true,
		},

		// make sure a local module that looks like a registry id takes precedence
		{
			source:  "namespace/identifier/provider",
			fixture: "discover-subdirs",
			// this should be found locally
			location: "file://" + filepath.Join(wd, fixtureDir, "discover-subdirs/namespace/identifier/provider"),
		},
	} {

		t.Run(tc.source, func(t *testing.T) {
			dir := wd
			if tc.fixture != "" {
				dir = filepath.Join(wd, fixtureDir, tc.fixture)
				if err := os.Chdir(dir); err != nil {
					t.Fatal(err)
				}
				defer os.Chdir(wd)
			}

			loc, err := getter.Detect(tc.source, dir, detectors)
			if (err == nil) == tc.err {
				t.Fatalf("expected error? %t; got error :%v", tc.err, err)
			}

			loc = strings.TrimPrefix(loc, server.URL+"/")
			if strings.TrimPrefix(loc, server.URL) != tc.location {
				t.Fatalf("expected location: %q, got %q", tc.location, loc)
			}
		})

	}
}

func TestAccRegistryDiscover(t *testing.T) {
	if os.Getenv("TF_ACC") == "" {
		t.Skip("skipping ACC test")
	}

	// simply check that we get a valid github URL for this from the registry
	loc, err := getter.Detect("hashicorp/consul/aws", "./", detectors)
	if err != nil {
		t.Fatal(err)
	}

	u, err := url.Parse(loc)
	if err != nil {
		t.Fatal(err)
	}

	if !strings.HasSuffix(u.Host, "github.com") {
		t.Fatalf("expected host 'github.com', got: %q", u.Host)
	}

	if !strings.Contains(u.String(), "consul") {
		t.Fatalf("url doesn't contain 'consul': %s", u.String())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/coreos/etcd/store"
	"github.com/coreos/go-raft"
	"net/http"
	"strconv"
	"strings"
)

//-------------------------------------------------------------------
// Handlers to handle etcd-store related request via etcd url
//-------------------------------------------------------------------

func NewEtcdMuxer() *http.ServeMux {
	// external commands
	etcdMux := http.NewServeMux()
	etcdMux.HandleFunc("/"+version+"/keys/", Multiplexer)
	etcdMux.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
	etcdMux.HandleFunc("/leader", LeaderHttpHandler)
	etcdMux.HandleFunc("/machines", MachinesHttpHandler)
	etcdMux.HandleFunc("/", VersionHttpHandler)
	etcdMux.HandleFunc("/stats", StatsHttpHandler)
	etcdMux.HandleFunc("/test/", TestHttpHandler)
	return etcdMux
}

// Multiplex GET/POST/DELETE request to corresponding handlers
func Multiplexer(w http.ResponseWriter, req *http.Request) {

	switch req.Method {
	case "GET":
		GetHttpHandler(&w, req)
	case "POST":
		SetHttpHandler(&w, req)
	case "DELETE":
		DeleteHttpHandler(&w, req)
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
}

//--------------------------------------
// State sensitive handlers
// Set/Delete will dispatch to leader
//--------------------------------------

// Set Command Handler
func SetHttpHandler(w *http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/keys/"):]

	if store.CheckKeyword(key) {

		(*w).WriteHeader(http.StatusBadRequest)

		(*w).Write(newJsonError(400, "Set"))
		return
	}

	debugf("[recv] POST %v/v1/keys/%s [%s]", info.EtcdURL, key, req.RemoteAddr)

	value := req.FormValue("value")

	if len(value) == 0 {
		(*w).WriteHeader(http.StatusBadRequest)

		(*w).Write(newJsonError(200, "Set"))
		return
	}

	prevValue := req.FormValue("prevValue")

	strDuration := req.FormValue("ttl")

	expireTime, err := durationToExpireTime(strDuration)

	if err != nil {

		(*w).WriteHeader(http.StatusBadRequest)

		(*w).Write(newJsonError(202, "Set"))
		return
	}

	if len(prevValue) != 0 {
		command := &TestAndSetCommand{
			Key:        key,
			Value:      value,
			PrevValue:  prevValue,
			ExpireTime: expireTime,
		}

		dispatch(command, w, req, true)

	} else {
		command := &SetCommand{
			Key:        key,
			Value:      value,
			ExpireTime: expireTime,
		}

		dispatch(command, w, req, true)
	}

}

// Delete Handler
func DeleteHttpHandler(w *http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/keys/"):]

	debugf("[recv] DELETE %v/v1/keys/%s [%s]", info.EtcdURL, key, req.RemoteAddr)

	command := &DeleteCommand{
		Key: key,
	}

	dispatch(command, w, req, true)
}

// Dispatch the command to leader
func dispatch(c Command, w *http.ResponseWriter, req *http.Request, etcd bool) {

	if raftServer.State() == raft.Leader {
		if body, err := raftServer.Do(c); err != nil {

			if _, ok := err.(store.NotFoundError); ok {
				(*w).WriteHeader(http.StatusNotFound)
				(*w).Write(newJsonError(100, err.Error()))
				return
			}

			if _, ok := err.(store.TestFail); ok {
				(*w).WriteHeader(http.StatusBadRequest)
				(*w).Write(newJsonError(101, err.Error()))
				return
			}

			if _, ok := err.(store.NotFile); ok {
				(*w).WriteHeader(http.StatusBadRequest)
				(*w).Write(newJsonError(102, err.Error()))
				return
			}
			if err.Error() == errors[103] {
				(*w).WriteHeader(http.StatusBadRequest)
				(*w).Write(newJsonError(103, ""))
				return
			}
			(*w).WriteHeader(http.StatusInternalServerError)
			(*w).Write(newJsonError(300, err.Error()))
			return
		} else {

			if body == nil {
				(*w).WriteHeader(http.StatusNotFound)
				(*w).Write(newJsonError(300, "Empty result from raft"))
			} else {
				body, ok := body.([]byte)
				// this should not happen
				if !ok {
					panic("wrong type")
				}
				(*w).WriteHeader(http.StatusOK)
				(*w).Write(body)
			}
			return
		}
	} else {
		leader := raftServer.Leader()
		// current no leader
		if leader == "" {
			(*w).WriteHeader(http.StatusInternalServerError)
			(*w).Write(newJsonError(300, ""))
			return
		}

		// tell the client where is the leader

		path := req.URL.Path

		var url string

		if etcd {
			etcdAddr, _ := nameToEtcdURL(leader)
			if etcdAddr == "" {
				panic(leader)
			}
			url = etcdAddr + path
		} else {
			raftAddr, _ := nameToRaftURL(leader)
			url = raftAddr + path
		}

		debugf("Redirect to %s", url)

		http.Redirect(*w, req, url, http.StatusTemporaryRedirect)
		return
	}
	(*w).WriteHeader(http.StatusInternalServerError)
	(*w).Write(newJsonError(300, ""))
	return
}

//--------------------------------------
// State non-sensitive handlers
// will not dispatch to leader
// TODO: add sensitive version for these
// command?
//--------------------------------------

// Handler to return the current leader's raft address
func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) {
	leader := raftServer.Leader()

	if leader != "" {
		w.WriteHeader(http.StatusOK)
		raftURL, _ := nameToRaftURL(leader)
		w.Write([]byte(raftURL))
	} else {

		// not likely, but it may happen
		w.WriteHeader(http.StatusInternalServerError)
		w.Write(newJsonError(301, ""))
	}
}

// Handler to return all the known machines in the current cluster
func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) {
	machines := getMachines()

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(strings.Join(machines, ", ")))
}

// Handler to return the current version of etcd
func VersionHttpHandler(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf("etcd %s", releaseVersion)))
}

// Handler to return the basic stats of etcd
func StatsHttpHandler(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write(etcdStore.Stats())
}

// Get Handler
func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/keys/"):]

	debugf("[recv] GET %s/v1/keys/%s [%s]", info.EtcdURL, key, req.RemoteAddr)

	command := &GetCommand{
		Key: key,
	}

	if body, err := command.Apply(raftServer); err != nil {

		if _, ok := err.(store.NotFoundError); ok {
			(*w).WriteHeader(http.StatusNotFound)
			(*w).Write(newJsonError(100, err.Error()))
			return
		}

		(*w).WriteHeader(http.StatusInternalServerError)
		(*w).Write(newJsonError(300, ""))

	} else {
		body, ok := body.([]byte)
		if !ok {
			panic("wrong type")
		}

		(*w).WriteHeader(http.StatusOK)
		(*w).Write(body)

	}

}

// Watch handler
func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/watch/"):]

	command := &WatchCommand{
		Key: key,
	}

	if req.Method == "GET" {
		debugf("[recv] GET %s/watch/%s [%s]", info.EtcdURL, key, req.RemoteAddr)
		command.SinceIndex = 0

	} else if req.Method == "POST" {
		// watch from a specific index

		debugf("[recv] POST %s/watch/%s [%s]", info.EtcdURL, key, req.RemoteAddr)
		content := req.FormValue("index")

		sinceIndex, err := strconv.ParseUint(string(content), 10, 64)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write(newJsonError(203, "Watch From Index"))
		}
		command.SinceIndex = sinceIndex

	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	if body, err := command.Apply(raftServer); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write(newJsonError(500, key))
	} else {
		w.WriteHeader(http.StatusOK)

		body, ok := body.([]byte)
		if !ok {
			panic("wrong type")
		}

		w.Write(body)
	}

}

// TestHandler
func TestHttpHandler(w http.ResponseWriter, req *http.Request) {
	testType := req.URL.Path[len("/test/"):]

	if testType == "speed" {
		directSet()
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("speed test success"))
		return
	}

	w.WriteHeader(http.StatusBadRequest)
}
<commit_msg>handler version at /version<commit_after>package main

import (
	"fmt"
	"github.com/coreos/etcd/store"
	"github.com/coreos/go-raft"
	"net/http"
	"strconv"
	"strings"
)

//-------------------------------------------------------------------
// Handlers to handle etcd-store related request via etcd url
//-------------------------------------------------------------------

func NewEtcdMuxer() *http.ServeMux {
	// external commands
	etcdMux := http.NewServeMux()
	etcdMux.HandleFunc("/"+version+"/keys/", Multiplexer)
	etcdMux.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
	etcdMux.HandleFunc("/leader", LeaderHttpHandler)
	etcdMux.HandleFunc("/machines", MachinesHttpHandler)
	etcdMux.HandleFunc("/version", VersionHttpHandler)
	etcdMux.HandleFunc("/stats", StatsHttpHandler)
	etcdMux.HandleFunc("/test/", TestHttpHandler)
	return etcdMux
}

// Multiplex GET/POST/DELETE request to corresponding handlers
func Multiplexer(w http.ResponseWriter, req *http.Request) {

	switch req.Method {
	case "GET":
		GetHttpHandler(&w, req)
	case "POST":
		SetHttpHandler(&w, req)
	case "DELETE":
		DeleteHttpHandler(&w, req)
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
}

//--------------------------------------
// State sensitive handlers
// Set/Delete will dispatch to leader
//--------------------------------------

// Set Command Handler
func SetHttpHandler(w *http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/keys/"):]

	if store.CheckKeyword(key) {

		(*w).WriteHeader(http.StatusBadRequest)

		(*w).Write(newJsonError(400, "Set"))
		return
	}

	debugf("[recv] POST %v/v1/keys/%s [%s]", info.EtcdURL, key, req.RemoteAddr)

	value := req.FormValue("value")

	if len(value) == 0 {
		(*w).WriteHeader(http.StatusBadRequest)

		(*w).Write(newJsonError(200, "Set"))
		return
	}

	prevValue := req.FormValue("prevValue")

	strDuration := req.FormValue("ttl")

	expireTime, err := durationToExpireTime(strDuration)

	if err != nil {

		(*w).WriteHeader(http.StatusBadRequest)

		(*w).Write(newJsonError(202, "Set"))
		return
	}

	if len(prevValue) != 0 {
		command := &TestAndSetCommand{
			Key:        key,
			Value:      value,
			PrevValue:  prevValue,
			ExpireTime: expireTime,
		}

		dispatch(command, w, req, true)

	} else {
		command := &SetCommand{
			Key:        key,
			Value:      value,
			ExpireTime: expireTime,
		}

		dispatch(command, w, req, true)
	}

}

// Delete Handler
func DeleteHttpHandler(w *http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/keys/"):]

	debugf("[recv] DELETE %v/v1/keys/%s [%s]", info.EtcdURL, key, req.RemoteAddr)

	command := &DeleteCommand{
		Key: key,
	}

	dispatch(command, w, req, true)
}

// Dispatch the command to leader
func dispatch(c Command, w *http.ResponseWriter, req *http.Request, etcd bool) {

	if raftServer.State() == raft.Leader {
		if body, err := raftServer.Do(c); err != nil {

			if _, ok := err.(store.NotFoundError); ok {
				(*w).WriteHeader(http.StatusNotFound)
				(*w).Write(newJsonError(100, err.Error()))
				return
			}

			if _, ok := err.(store.TestFail); ok {
				(*w).WriteHeader(http.StatusBadRequest)
				(*w).Write(newJsonError(101, err.Error()))
				return
			}

			if _, ok := err.(store.NotFile); ok {
				(*w).WriteHeader(http.StatusBadRequest)
				(*w).Write(newJsonError(102, err.Error()))
				return
			}
			if err.Error() == errors[103] {
				(*w).WriteHeader(http.StatusBadRequest)
				(*w).Write(newJsonError(103, ""))
				return
			}
			(*w).WriteHeader(http.StatusInternalServerError)
			(*w).Write(newJsonError(300, err.Error()))
			return
		} else {

			if body == nil {
				(*w).WriteHeader(http.StatusNotFound)
				(*w).Write(newJsonError(300, "Empty result from raft"))
			} else {
				body, ok := body.([]byte)
				// this should not happen
				if !ok {
					panic("wrong type")
				}
				(*w).WriteHeader(http.StatusOK)
				(*w).Write(body)
			}
			return
		}
	} else {
		leader := raftServer.Leader()
		// current no leader
		if leader == "" {
			(*w).WriteHeader(http.StatusInternalServerError)
			(*w).Write(newJsonError(300, ""))
			return
		}

		// tell the client where is the leader

		path := req.URL.Path

		var url string

		if etcd {
			etcdAddr, _ := nameToEtcdURL(leader)
			if etcdAddr == "" {
				panic(leader)
			}
			url = etcdAddr + path
		} else {
			raftAddr, _ := nameToRaftURL(leader)
			url = raftAddr + path
		}

		debugf("Redirect to %s", url)

		http.Redirect(*w, req, url, http.StatusTemporaryRedirect)
		return
	}
	(*w).WriteHeader(http.StatusInternalServerError)
	(*w).Write(newJsonError(300, ""))
	return
}

//--------------------------------------
// State non-sensitive handlers
// will not dispatch to leader
// TODO: add sensitive version for these
// command?
//--------------------------------------

// Handler to return the current leader's raft address
func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) {
	leader := raftServer.Leader()

	if leader != "" {
		w.WriteHeader(http.StatusOK)
		raftURL, _ := nameToRaftURL(leader)
		w.Write([]byte(raftURL))
	} else {

		// not likely, but it may happen
		w.WriteHeader(http.StatusInternalServerError)
		w.Write(newJsonError(301, ""))
	}
}

// Handler to return all the known machines in the current cluster
func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) {
	machines := getMachines()

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(strings.Join(machines, ", ")))
}

// Handler to return the current version of etcd
func VersionHttpHandler(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf("etcd %s", releaseVersion)))
}

// Handler to return the basic stats of etcd
func StatsHttpHandler(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write(etcdStore.Stats())
}

// Get Handler
func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/keys/"):]

	debugf("[recv] GET %s/v1/keys/%s [%s]", info.EtcdURL, key, req.RemoteAddr)

	command := &GetCommand{
		Key: key,
	}

	if body, err := command.Apply(raftServer); err != nil {

		if _, ok := err.(store.NotFoundError); ok {
			(*w).WriteHeader(http.StatusNotFound)
			(*w).Write(newJsonError(100, err.Error()))
			return
		}

		(*w).WriteHeader(http.StatusInternalServerError)
		(*w).Write(newJsonError(300, ""))

	} else {
		body, ok := body.([]byte)
		if !ok {
			panic("wrong type")
		}

		(*w).WriteHeader(http.StatusOK)
		(*w).Write(body)

	}

}

// Watch handler
func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
	key := req.URL.Path[len("/v1/watch/"):]

	command := &WatchCommand{
		Key: key,
	}

	if req.Method == "GET" {
		debugf("[recv] GET %s/watch/%s [%s]", info.EtcdURL, key, req.RemoteAddr)
		command.SinceIndex = 0

	} else if req.Method == "POST" {
		// watch from a specific index

		debugf("[recv] POST %s/watch/%s [%s]", info.EtcdURL, key, req.RemoteAddr)
		content := req.FormValue("index")

		sinceIndex, err := strconv.ParseUint(string(content), 10, 64)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write(newJsonError(203, "Watch From Index"))
		}
		command.SinceIndex = sinceIndex

	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	if body, err := command.Apply(raftServer); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write(newJsonError(500, key))
	} else {
		w.WriteHeader(http.StatusOK)

		body, ok := body.([]byte)
		if !ok {
			panic("wrong type")
		}

		w.Write(body)
	}

}

// TestHandler
func TestHttpHandler(w http.ResponseWriter, req *http.Request) {
	testType := req.URL.Path[len("/test/"):]

	if testType == "speed" {
		directSet()
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("speed test success"))
		return
	}

	w.WriteHeader(http.StatusBadRequest)
}
<|endoftext|>


<commit_before>package events

import (
	"encoding/json"
	. "github.com/gamingrobot/steamgo/internal"
	. "github.com/gamingrobot/steamgo/steamid"
)

type Event string

type SendMessageEvent struct {
	SteamId       SteamId
	ChatEntryType EChatEntryType
	Message       string
}

type WebEvent struct {
	Event Event
	Body  json.RawMessage
}

type SteamEvent struct {
	Event Event
	Body  interface{}
}
<commit_msg>Moved events in file<commit_after>package events

import (
	"encoding/json"
	. "github.com/gamingrobot/steamgo/internal"
	. "github.com/gamingrobot/steamgo/steamid"
)

type Event string

type WebEvent struct {
	Event Event
	Body  json.RawMessage
}

type SteamEvent struct {
	Event Event
	Body  interface{}
}

type SendMessageEvent struct {
	SteamId       SteamId
	ChatEntryType EChatEntryType
	Message       string
}
<|endoftext|>