<commit_before>// Package api contains definitions for using the premiumize.me API
package api

import (
	"fmt"
	"time"

	"github.com/pkg/errors"
)

// ListRequestSelect should be used in $select for Items/Children
const ListRequestSelect = "odata.count,FileCount,Name,FileName,CreationDate,IsHidden,FileSizeBytes,odata.type,Id,Hash,ClientModifiedDate"

// ListResponse is returned from the Items/Children call
type ListResponse struct {
	OdataCount int    `json:"odata.count"`
	Value      []Item `json:"value"`
}

// Item Types
const (
	ItemTypeFolder = "ShareFile.Api.Models.Folder"
	ItemTypeFile   = "ShareFile.Api.Models.File"
)

// Item refers to a file or folder
type Item struct {
	FileCount  int32     `json:"FileCount,omitempty"`
	Name       string    `json:"Name,omitempty"`
	FileName   string    `json:"FileName,omitempty"`
	CreatedAt  time.Time `json:"CreationDate,omitempty"`
	ModifiedAt time.Time `json:"ClientModifiedDate,omitempty"`
	IsHidden   bool      `json:"IsHidden,omitempty"`
	Size       int64     `json:"FileSizeBytes,omitempty"`
	Type       string    `json:"odata.type,omitempty"`
	ID         string    `json:"Id,omitempty"`
	Hash       string    `json:"Hash,omitempty"`
}

// Error is an odata error return
type Error struct {
	Code    string `json:"code"`
	Message struct {
		Lang  string `json:"lang"`
		Value string `json:"value"`
	} `json:"message"`
	Reason string `json:"reason"`
}

// Satisfy error interface
func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s: %s", e.Message.Value, e.Code, e.Reason)
}

// Check Error satisfies error interface
var _ error = &Error{}

// DownloadSpecification is the response to /Items/Download
type DownloadSpecification struct {
	Token    string `json:"DownloadToken"`
	URL      string `json:"DownloadUrl"`
	Metadata string `json:"odata.metadata"`
	Type     string `json:"odata.type"`
}

// UploadRequest is set to /Items/Upload2 to receive an UploadSpecification
type UploadRequest struct {
	Method         string    `json:"method"`                   // Upload method: one of: standard, streamed or threaded
	Raw            bool      `json:"raw"`                      // Raw post if true or MIME upload if false
	Filename       string    `json:"fileName"`                 // Uploaded item file name.
	Filesize       *int64    `json:"fileSize,omitempty"`       // Uploaded item file size.
	Overwrite      bool      `json:"overwrite"`                // Indicates whether items with the same name will be overwritten or not.
	CreatedDate    time.Time `json:"ClientCreatedDate"`        // Created Date of this Item.
	ModifiedDate   time.Time `json:"ClientModifiedDate"`       // Modified Date of this Item.
	BatchID        string    `json:"batchId,omitempty"`        // Indicates part of a batch. Batched uploads do not send notification until the whole batch is completed.
	BatchLast      *bool     `json:"batchLast,omitempty"`      // Indicates is the last in a batch. Upload notifications for the whole batch are sent after this upload.
	CanResume      *bool     `json:"canResume,omitempty"`      // Indicates uploader supports resume.
	StartOver      *bool     `json:"startOver,omitempty"`      // Indicates uploader wants to restart the file - i.e., ignore previous failed upload attempts.
	Tool           string    `json:"tool,omitempty"`           // Identifies the uploader tool.
	Title          string    `json:"title,omitempty"`          // Item Title
	Details        string    `json:"details,omitempty"`        // Item description
	IsSend         *bool     `json:"isSend,omitempty"`         // Indicates that this upload is part of a Send operation
	SendGUID       string    `json:"sendGuid,omitempty"`       // Used if IsSend is true. Specifies which Send operation this upload is part of.
	OpID           string    `json:"opid,omitempty"`           // Used for Asynchronous copy/move operations - called by Zones to push files to other Zones
	ThreadCount    *int      `json:"threadCount,omitempty"`    // Specifies the number of threads the threaded uploader will use. Only used is method is threaded, ignored otherwise
	Notify         *bool     `json:"notify,omitempty"`         // Indicates whether users will be notified of this upload - based on folder preferences
	ExpirationDays *int      `json:"expirationDays,omitempty"` // File expiration days
	BaseFileID     string    `json:"baseFileId,omitempty"`     // Used to check conflict in file during File Upload.
}

// UploadSpecification is returned from /Items/Upload
type UploadSpecification struct {
	Method             string `json:"Method"`             // The Upload method that must be used for this upload
	PrepareURI         string `json:"PrepareUri"`         // If provided, clients must issue a request to this Uri before uploading any data.
	ChunkURI           string `json:"ChunkUri"`           // Specifies the URI the client must send the file data to
	FinishURI          string `json:"FinishUri"`          // If provided, specifies the final call the client must perform to finish the upload process
	ProgressData       string `json:"ProgressData"`       // Allows the client to check progress of standard uploads
	IsResume           bool   `json:"IsResume"`           // Specifies a Resumable upload is supported.
	ResumeIndex        int64  `json:"ResumeIndex"`        // Specifies the initial index for resuming, if IsResume is true.
	ResumeOffset       int64  `json:"ResumeOffset"`       // Specifies the initial file offset by bytes, if IsResume is true
	ResumeFileHash     string `json:"ResumeFileHash"`     // Specifies the MD5 hash of the first ResumeOffset bytes of the partial file found at the server
	MaxNumberOfThreads int    `json:"MaxNumberOfThreads"` // Specifies the max number of chunks that can be sent simultaneously for threaded uploads
}

// UploadFinishResponse is returns from calling UploadSpecification.FinishURI
type UploadFinishResponse struct {
	Error        bool   `json:"error"`
	ErrorMessage string `json:"errorMessage"`
	ErrorCode    int    `json:"errorCode,string"`
	Value        []struct {
		UploadID    string `json:"uploadid"`
		ParentID    string `json:"parentid"`
		ID          string `json:"id"`
		StreamID    string `json:"streamid"`
		FileName    string `json:"filename"`
		DisplayName string `json:"displayname"`
		Size        int    `json:"size,string"`
		Md5         string `json:"md5"`
	} `json:"value"`
}

// ID returns the ID of the first response if available
func (finish *UploadFinishResponse) ID() (string, error) {
	if finish.Error {
		return "", errors.Errorf("upload failed: %s (%d)", finish.ErrorMessage, finish.ErrorCode)
	}
	if len(finish.Value) == 0 {
		return "", errors.New("upload failed: no results returned")
	}
	return finish.Value[0].ID, nil
}

// Parent is the ID of the parent folder
type Parent struct {
	ID string `json:"Id,omitempty"`
}

// Zone is where the data is stored
type Zone struct {
	ID string `json:"Id,omitempty"`
}

// UpdateItemRequest is sent to PATCH /v3/Items(id)
type UpdateItemRequest struct {
	Name           string     `json:"Name,omitempty"`
	FileName       string     `json:"FileName,omitempty"`
	Description    string     `json:"Description,omitempty"`
	ExpirationDate *time.Time `json:"ExpirationDate,omitempty"`
	Parent         *Parent    `json:"Parent,omitempty"`
	Zone           *Zone      `json:"Zone,omitempty"`
	ModifiedAt     *time.Time `json:"ClientModifiedDate,omitempty"`
}
<commit_msg>Revert "sharefile: fix backend due to API swapping integers for strings"<commit_after>// Package api contains definitions for using the premiumize.me API
package api

import (
	"fmt"
	"time"

	"github.com/pkg/errors"
)

// ListRequestSelect should be used in $select for Items/Children
const ListRequestSelect = "odata.count,FileCount,Name,FileName,CreationDate,IsHidden,FileSizeBytes,odata.type,Id,Hash,ClientModifiedDate"

// ListResponse is returned from the Items/Children call
type ListResponse struct {
	OdataCount int    `json:"odata.count"`
	Value      []Item `json:"value"`
}

// Item Types
const (
	ItemTypeFolder = "ShareFile.Api.Models.Folder"
	ItemTypeFile   = "ShareFile.Api.Models.File"
)

// Item refers to a file or folder
type Item struct {
	FileCount  int32     `json:"FileCount,omitempty"`
	Name       string    `json:"Name,omitempty"`
	FileName   string    `json:"FileName,omitempty"`
	CreatedAt  time.Time `json:"CreationDate,omitempty"`
	ModifiedAt time.Time `json:"ClientModifiedDate,omitempty"`
	IsHidden   bool      `json:"IsHidden,omitempty"`
	Size       int64     `json:"FileSizeBytes,omitempty"`
	Type       string    `json:"odata.type,omitempty"`
	ID         string    `json:"Id,omitempty"`
	Hash       string    `json:"Hash,omitempty"`
}

// Error is an odata error return
type Error struct {
	Code    string `json:"code"`
	Message struct {
		Lang  string `json:"lang"`
		Value string `json:"value"`
	} `json:"message"`
	Reason string `json:"reason"`
}

// Satisfy error interface
func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s: %s", e.Message.Value, e.Code, e.Reason)
}

// Check Error satisfies error interface
var _ error = &Error{}

// DownloadSpecification is the response to /Items/Download
type DownloadSpecification struct {
	Token    string `json:"DownloadToken"`
	URL      string `json:"DownloadUrl"`
	Metadata string `json:"odata.metadata"`
	Type     string `json:"odata.type"`
}

// UploadRequest is set to /Items/Upload2 to receive an UploadSpecification
type UploadRequest struct {
	Method         string    `json:"method"`                   // Upload method: one of: standard, streamed or threaded
	Raw            bool      `json:"raw"`                      // Raw post if true or MIME upload if false
	Filename       string    `json:"fileName"`                 // Uploaded item file name.
	Filesize       *int64    `json:"fileSize,omitempty"`       // Uploaded item file size.
	Overwrite      bool      `json:"overwrite"`                // Indicates whether items with the same name will be overwritten or not.
	CreatedDate    time.Time `json:"ClientCreatedDate"`        // Created Date of this Item.
	ModifiedDate   time.Time `json:"ClientModifiedDate"`       // Modified Date of this Item.
	BatchID        string    `json:"batchId,omitempty"`        // Indicates part of a batch. Batched uploads do not send notification until the whole batch is completed.
	BatchLast      *bool     `json:"batchLast,omitempty"`      // Indicates is the last in a batch. Upload notifications for the whole batch are sent after this upload.
	CanResume      *bool     `json:"canResume,omitempty"`      // Indicates uploader supports resume.
	StartOver      *bool     `json:"startOver,omitempty"`      // Indicates uploader wants to restart the file - i.e., ignore previous failed upload attempts.
	Tool           string    `json:"tool,omitempty"`           // Identifies the uploader tool.
	Title          string    `json:"title,omitempty"`          // Item Title
	Details        string    `json:"details,omitempty"`        // Item description
	IsSend         *bool     `json:"isSend,omitempty"`         // Indicates that this upload is part of a Send operation
	SendGUID       string    `json:"sendGuid,omitempty"`       // Used if IsSend is true. Specifies which Send operation this upload is part of.
	OpID           string    `json:"opid,omitempty"`           // Used for Asynchronous copy/move operations - called by Zones to push files to other Zones
	ThreadCount    *int      `json:"threadCount,omitempty"`    // Specifies the number of threads the threaded uploader will use. Only used is method is threaded, ignored otherwise
	Notify         *bool     `json:"notify,omitempty"`         // Indicates whether users will be notified of this upload - based on folder preferences
	ExpirationDays *int      `json:"expirationDays,omitempty"` // File expiration days
	BaseFileID     string    `json:"baseFileId,omitempty"`     // Used to check conflict in file during File Upload.
}

// UploadSpecification is returned from /Items/Upload
type UploadSpecification struct {
	Method             string `json:"Method"`             // The Upload method that must be used for this upload
	PrepareURI         string `json:"PrepareUri"`         // If provided, clients must issue a request to this Uri before uploading any data.
	ChunkURI           string `json:"ChunkUri"`           // Specifies the URI the client must send the file data to
	FinishURI          string `json:"FinishUri"`          // If provided, specifies the final call the client must perform to finish the upload process
	ProgressData       string `json:"ProgressData"`       // Allows the client to check progress of standard uploads
	IsResume           bool   `json:"IsResume"`           // Specifies a Resumable upload is supported.
	ResumeIndex        int64  `json:"ResumeIndex"`        // Specifies the initial index for resuming, if IsResume is true.
	ResumeOffset       int64  `json:"ResumeOffset"`       // Specifies the initial file offset by bytes, if IsResume is true
	ResumeFileHash     string `json:"ResumeFileHash"`     // Specifies the MD5 hash of the first ResumeOffset bytes of the partial file found at the server
	MaxNumberOfThreads int    `json:"MaxNumberOfThreads"` // Specifies the max number of chunks that can be sent simultaneously for threaded uploads
}

// UploadFinishResponse is returns from calling UploadSpecification.FinishURI
type UploadFinishResponse struct {
	Error        bool   `json:"error"`
	ErrorMessage string `json:"errorMessage"`
	ErrorCode    int    `json:"errorCode"`
	Value        []struct {
		UploadID    string `json:"uploadid"`
		ParentID    string `json:"parentid"`
		ID          string `json:"id"`
		StreamID    string `json:"streamid"`
		FileName    string `json:"filename"`
		DisplayName string `json:"displayname"`
		Size        int    `json:"size"`
		Md5         string `json:"md5"`
	} `json:"value"`
}

// ID returns the ID of the first response if available
func (finish *UploadFinishResponse) ID() (string, error) {
	if finish.Error {
		return "", errors.Errorf("upload failed: %s (%d)", finish.ErrorMessage, finish.ErrorCode)
	}
	if len(finish.Value) == 0 {
		return "", errors.New("upload failed: no results returned")
	}
	return finish.Value[0].ID, nil
}

// Parent is the ID of the parent folder
type Parent struct {
	ID string `json:"Id,omitempty"`
}

// Zone is where the data is stored
type Zone struct {
	ID string `json:"Id,omitempty"`
}

// UpdateItemRequest is sent to PATCH /v3/Items(id)
type UpdateItemRequest struct {
	Name           string     `json:"Name,omitempty"`
	FileName       string     `json:"FileName,omitempty"`
	Description    string     `json:"Description,omitempty"`
	ExpirationDate *time.Time `json:"ExpirationDate,omitempty"`
	Parent         *Parent    `json:"Parent,omitempty"`
	Zone           *Zone      `json:"Zone,omitempty"`
	ModifiedAt     *time.Time `json:"ClientModifiedDate,omitempty"`
}
<|endoftext|>


<commit_before>// +build go1.10

package godog

import (
	"bytes"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"text/template"
	"time"
	"unicode"
)

var tooldir = findToolDir()
var compiler = filepath.Join(tooldir, "compile")
var linker = filepath.Join(tooldir, "link")
var gopaths = filepath.SplitList(build.Default.GOPATH)
var goarch = build.Default.GOARCH
var goroot = build.Default.GOROOT
var goos = build.Default.GOOS

var godogImportPath = "github.com/DATA-DOG/godog"
var runnerTemplate = template.Must(template.New("testmain").Parse(`package main

import (
	"github.com/DATA-DOG/godog"
	{{if .Contexts}}_test "{{.ImportPath}}"{{end}}
	"os"
)

func main() {
	status := godog.Run("{{ .Name }}", func (suite *godog.Suite) {
		os.Setenv("GODOG_TESTED_PACKAGE", "{{.ImportPath}}")
		{{range .Contexts}}
			_test.{{ . }}(suite)
		{{end}}
	})
	os.Exit(status)
}`))

// Build creates a test package like go test command at given target path.
// If there are no go files in tested directory, then
// it simply builds a godog executable to scan features.
//
// If there are go test files, it first builds a test
// package with standard go test command.
//
// Finally it generates godog suite executable which
// registers exported godog contexts from the test files
// of tested package.
//
// Returns the path to generated executable
func Build(bin string) error {
	abs, err := filepath.Abs(".")
	if err != nil {
		return err
	}

	// we allow package to be nil, if godog is run only when
	// there is a feature file in empty directory
	pkg := importPackage(abs)
	src, anyContexts, err := buildTestMain(pkg)
	if err != nil {
		return err
	}

	workdir := fmt.Sprintf(filepath.Join("%s", "godog-%d"), os.TempDir(), time.Now().UnixNano())
	testdir := workdir

	// if none of test files exist, or there are no contexts found
	// we will skip test package compilation, since it is useless
	if anyContexts {
		// first of all compile test package dependencies
		// that will save us many compilations for dependencies
		// go does it better
		out, err := exec.Command("go", "test", "-i").CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// builds and compile the tested package.
		// generated test executable will be removed
		// since we do not need it for godog suite.
		// we also print back the temp WORK directory
		// go has built. We will reuse it for our suite workdir.
		out, err = exec.Command("go", "test", "-c", "-work", "-o", os.DevNull).CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile tested package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// extract go-build temporary directory as our workdir
		workdir = strings.TrimSpace(string(out))
		if !strings.HasPrefix(workdir, "WORK=") {
			return fmt.Errorf("expected WORK dir path, but got: %s", workdir)
		}
		workdir = strings.Replace(workdir, "WORK=", "", 1)
		testdir = filepath.Join(workdir, "b001")
	} else {
		// still need to create temporary workdir
		if err = os.MkdirAll(testdir, 0755); err != nil {
			return err
		}
	}
	defer os.RemoveAll(workdir)

	// replace _testmain.go file with our own
	testmain := filepath.Join(testdir, "_testmain.go")
	err = ioutil.WriteFile(testmain, src, 0644)
	if err != nil {
		return err
	}

	// godog library may not be imported in tested package
	// but we need it for our testmain package.
	// So we look it up in available source paths
	// including vendor directory, supported since 1.5.
	godogPkg, err := locatePackage(godogImportPath)
	if err != nil {
		return err
	}

	// make sure godog package archive is installed, gherkin
	// will be installed as dependency of godog
	cmd := exec.Command("go", "install", "-i", godogPkg.ImportPath)
	cmd.Env = os.Environ()
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to install godog package: %s, reason: %v", string(out), err)
	}

	// compile godog testmain package archive
	// we do not depend on CGO so a lot of checks are not necessary
	testMainPkgOut := filepath.Join(testdir, "main.a")
	args := []string{
		"-o", testMainPkgOut,
		"-p", "main",
		"-complete",
	}

	cfg := filepath.Join(testdir, "importcfg.link")
	args = append(args, "-importcfg", cfg)
	if _, err := os.Stat(cfg); err != nil {
		// there were no go sources in the directory
		// so we need to build all dependency tree ourselves
		in, err := os.Create(cfg)
		if err != nil {
			return err
		}
		fmt.Fprintln(in, "# import config")

		deps := make(map[string]string)
		if err := dependencies(godogPkg, deps, false); err != nil {
			in.Close()
			return err
		}

		for pkgName, pkgObj := range deps {
			if i := strings.LastIndex(pkgName, "vendor/"); i != -1 {
				name := pkgName[i+7:]
				fmt.Fprintf(in, "importmap %s=%s\n", name, pkgName)
			}
			fmt.Fprintf(in, "packagefile %s=%s\n", pkgName, pkgObj)
		}
		in.Close()
	} else {
		// need to make sure that vendor dependencies are mapped
		in, err := os.OpenFile(cfg, os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			return err
		}
		deps := make(map[string]string)
		if err := dependencies(pkg, deps, true); err != nil {
			in.Close()
			return err
		}
		if err := dependencies(godogPkg, deps, false); err != nil {
			in.Close()
			return err
		}
		for pkgName := range deps {
			if i := strings.LastIndex(pkgName, "vendor/"); i != -1 {
				name := pkgName[i+7:]
				fmt.Fprintf(in, "importmap %s=%s\n", name, pkgName)
			}
		}
		in.Close()
	}

	args = append(args, "-pack", testmain)
	cmd = exec.Command(compiler, args...)
	cmd.Env = os.Environ()
	out, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to compile testmain package: %v - output: %s", err, string(out))
	}

	// link test suite executable
	args = []string{
		"-o", bin,
		"-importcfg", cfg,
		"-buildmode=exe",
	}
	args = append(args, testMainPkgOut)
	cmd = exec.Command(linker, args...)
	cmd.Env = os.Environ()

	// in case if build is without contexts, need to remove import maps
	data, err := ioutil.ReadFile(cfg)
	if err != nil {
		return err
	}

	lines := strings.Split(string(data), "\n")
	var fixed []string
	for _, line := range lines {
		if strings.Index(line, "importmap") == 0 {
			continue
		}
		fixed = append(fixed, line)
	}
	if err := ioutil.WriteFile(cfg, []byte(strings.Join(fixed, "\n")), 0600); err != nil {
		return err
	}

	out, err = cmd.CombinedOutput()
	if err != nil {
		msg := `failed to link test executable:
	reason: %s
	command: %s`
		return fmt.Errorf(msg, string(out), linker+" '"+strings.Join(args, "' '")+"'")
	}

	return nil
}

func locatePackage(name string) (*build.Package, error) {
	// search vendor paths first since that takes priority
	dir, err := filepath.Abs(".")
	if err != nil {
		return nil, err
	}

	for _, gopath := range gopaths {
		gopath = filepath.Join(gopath, "src")
		for strings.HasPrefix(dir, gopath) && dir != gopath {
			pkg, err := build.ImportDir(filepath.Join(dir, "vendor", name), 0)
			if err != nil {
				dir = filepath.Dir(dir)
				continue
			}
			return pkg, nil
		}
	}

	// search source paths otherwise
	for _, p := range build.Default.SrcDirs() {
		abs, err := filepath.Abs(filepath.Join(p, name))
		if err != nil {
			continue
		}
		pkg, err := build.ImportDir(abs, 0)
		if err != nil {
			continue
		}
		return pkg, nil
	}

	return nil, fmt.Errorf("failed to find %s package in any of:\n%s", name, strings.Join(build.Default.SrcDirs(), "\n"))
}

func importPackage(dir string) *build.Package {
	pkg, _ := build.ImportDir(dir, 0)

	// normalize import path for local import packages
	// taken from go source code
	// see: https://github.com/golang/go/blob/go1.7rc5/src/cmd/go/pkg.go#L279
	if pkg != nil && pkg.ImportPath == "." {
		pkg.ImportPath = path.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
	}

	return pkg
}

// from go src
func makeImportValid(r rune) rune {
	// Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
		return '_'
	}
	return r
}

func uniqStringList(strs []string) (unique []string) {
	uniq := make(map[string]void, len(strs))
	for _, s := range strs {
		if _, ok := uniq[s]; !ok {
			uniq[s] = void{}
			unique = append(unique, s)
		}
	}
	return
}

// buildTestMain if given package is valid
// it scans test files for contexts
// and produces a testmain source code.
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
	var contexts []string
	var importPath string
	name := "main"
	if nil != pkg {
		ctxs, err := processPackageTestFiles(
			pkg.TestGoFiles,
			pkg.XTestGoFiles,
		)
		if err != nil {
			return nil, false, err
		}
		contexts = ctxs
		// @TODO: is it a good indicator for packages outside GOPATH
		if strings.Index(pkg.ImportPath, "_/") == 0 {
			importPath = pkg.Name
		} else {
			importPath = pkg.ImportPath
		}
		name = pkg.Name
	}

	data := struct {
		Name       string
		Contexts   []string
		ImportPath string
	}{name, contexts, importPath}

	var buf bytes.Buffer
	if err := runnerTemplate.Execute(&buf, data); err != nil {
		return nil, len(contexts) > 0, err
	}
	return buf.Bytes(), len(contexts) > 0, nil
}

// processPackageTestFiles runs through ast of each test
// file pack and looks for godog suite contexts to register
// on run
func processPackageTestFiles(packs ...[]string) ([]string, error) {
	var ctxs []string
	fset := token.NewFileSet()
	for _, pack := range packs {
		for _, testFile := range pack {
			node, err := parser.ParseFile(fset, testFile, nil, 0)
			if err != nil {
				return ctxs, err
			}

			ctxs = append(ctxs, astContexts(node)...)
		}
	}
	var failed []string
	for _, ctx := range ctxs {
		runes := []rune(ctx)
		if unicode.IsLower(runes[0]) {
			expected := append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)
			failed = append(failed, fmt.Sprintf("%s - should be: %s", ctx, string(expected)))
		}
	}
	if len(failed) > 0 {
		return ctxs, fmt.Errorf("godog contexts must be exported:\n\t%s", strings.Join(failed, "\n\t"))
	}
	return ctxs, nil
}

func findToolDir() string {
	if out, err := exec.Command("go", "env", "GOTOOLDIR").Output(); err != nil {
		return filepath.Clean(strings.TrimSpace(string(out)))
	}
	return filepath.Clean(build.ToolDir)
}

func dependencies(pkg *build.Package, visited map[string]string, vendor bool) error {
	visited[pkg.ImportPath] = pkg.PkgObj
	imports := pkg.Imports
	if vendor {
		imports = append(imports, pkg.TestImports...)
	}
	for _, name := range imports {
		if i := strings.LastIndex(name, "vendor/"); vendor && i == -1 {
			continue // only interested in vendor packages
		}

		if _, ok := visited[name]; ok {
			continue
		}

		next, err := locatePackage(name)
		if err != nil {
			return err
		}

		visited[name] = pkg.PkgObj
		if err := dependencies(next, visited, vendor); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>should close #136<commit_after>// +build go1.10

package godog

import (
	"bytes"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"text/template"
	"time"
	"unicode"
)

var tooldir = findToolDir()
var compiler = filepath.Join(tooldir, "compile")
var linker = filepath.Join(tooldir, "link")
var gopaths = filepath.SplitList(build.Default.GOPATH)
var goarch = build.Default.GOARCH
var goroot = build.Default.GOROOT
var goos = build.Default.GOOS

var godogImportPath = "github.com/DATA-DOG/godog"
var runnerTemplate = template.Must(template.New("testmain").Parse(`package main

import (
	"github.com/DATA-DOG/godog"
	{{if .Contexts}}_test "{{.ImportPath}}"{{end}}
	"os"
)

func main() {
	status := godog.Run("{{ .Name }}", func (suite *godog.Suite) {
		os.Setenv("GODOG_TESTED_PACKAGE", "{{.ImportPath}}")
		{{range .Contexts}}
			_test.{{ . }}(suite)
		{{end}}
	})
	os.Exit(status)
}`))

// Build creates a test package like go test command at given target path.
// If there are no go files in tested directory, then
// it simply builds a godog executable to scan features.
//
// If there are go test files, it first builds a test
// package with standard go test command.
//
// Finally it generates godog suite executable which
// registers exported godog contexts from the test files
// of tested package.
//
// Returns the path to generated executable
func Build(bin string) error {
	abs, err := filepath.Abs(".")
	if err != nil {
		return err
	}

	// we allow package to be nil, if godog is run only when
	// there is a feature file in empty directory
	pkg := importPackage(abs)
	src, anyContexts, err := buildTestMain(pkg)
	if err != nil {
		return err
	}

	workdir := fmt.Sprintf(filepath.Join("%s", "godog-%d"), os.TempDir(), time.Now().UnixNano())
	testdir := workdir

	// if none of test files exist, or there are no contexts found
	// we will skip test package compilation, since it is useless
	if anyContexts {
		// first of all compile test package dependencies
		// that will save us many compilations for dependencies
		// go does it better
		out, err := exec.Command("go", "test", "-i").CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// builds and compile the tested package.
		// generated test executable will be removed
		// since we do not need it for godog suite.
		// we also print back the temp WORK directory
		// go has built. We will reuse it for our suite workdir.
		out, err = exec.Command("go", "test", "-c", "-work", "-o", os.DevNull).CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile tested package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// extract go-build temporary directory as our workdir
		workdir = strings.TrimSpace(string(out))
		if !strings.HasPrefix(workdir, "WORK=") {
			return fmt.Errorf("expected WORK dir path, but got: %s", workdir)
		}
		workdir = strings.Replace(workdir, "WORK=", "", 1)
		testdir = filepath.Join(workdir, "b001")
	} else {
		// still need to create temporary workdir
		if err = os.MkdirAll(testdir, 0755); err != nil {
			return err
		}
	}
	defer os.RemoveAll(workdir)

	// replace _testmain.go file with our own
	testmain := filepath.Join(testdir, "_testmain.go")
	err = ioutil.WriteFile(testmain, src, 0644)
	if err != nil {
		return err
	}

	// godog library may not be imported in tested package
	// but we need it for our testmain package.
	// So we look it up in available source paths
	// including vendor directory, supported since 1.5.
	godogPkg, err := locatePackage(godogImportPath)
	if err != nil {
		return err
	}

	// make sure godog package archive is installed, gherkin
	// will be installed as dependency of godog
	cmd := exec.Command("go", "install", "-i", godogPkg.ImportPath)
	cmd.Env = os.Environ()
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to install godog package: %s, reason: %v", string(out), err)
	}

	// compile godog testmain package archive
	// we do not depend on CGO so a lot of checks are not necessary
	testMainPkgOut := filepath.Join(testdir, "main.a")
	args := []string{
		"-o", testMainPkgOut,
		"-p", "main",
		"-complete",
	}

	cfg := filepath.Join(testdir, "importcfg.link")
	args = append(args, "-importcfg", cfg)
	if _, err := os.Stat(cfg); err != nil {
		// there were no go sources in the directory
		// so we need to build all dependency tree ourselves
		in, err := os.Create(cfg)
		if err != nil {
			return err
		}
		fmt.Fprintln(in, "# import config")

		deps := make(map[string]string)
		if err := dependencies(godogPkg, deps, false); err != nil {
			in.Close()
			return err
		}

		for pkgName, pkgObj := range deps {
			if i := strings.LastIndex(pkgName, "vendor/"); i != -1 {
				name := pkgName[i+7:]
				fmt.Fprintf(in, "importmap %s=%s\n", name, pkgName)
			}
			fmt.Fprintf(in, "packagefile %s=%s\n", pkgName, pkgObj)
		}
		in.Close()
	} else {
		// need to make sure that vendor dependencies are mapped
		in, err := os.OpenFile(cfg, os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			return err
		}
		deps := make(map[string]string)
		if err := dependencies(pkg, deps, true); err != nil {
			in.Close()
			return err
		}
		if err := dependencies(godogPkg, deps, false); err != nil {
			in.Close()
			return err
		}
		for pkgName := range deps {
			if i := strings.LastIndex(pkgName, "vendor/"); i != -1 {
				name := pkgName[i+7:]
				fmt.Fprintf(in, "importmap %s=%s\n", name, pkgName)
			}
		}
		in.Close()
	}

	args = append(args, "-pack", testmain)
	cmd = exec.Command(compiler, args...)
	cmd.Env = os.Environ()
	out, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to compile testmain package: %v - output: %s", err, string(out))
	}

	// link test suite executable
	args = []string{
		"-o", bin,
		"-importcfg", cfg,
		"-buildmode=exe",
	}
	args = append(args, testMainPkgOut)
	cmd = exec.Command(linker, args...)
	cmd.Env = os.Environ()

	// in case if build is without contexts, need to remove import maps
	data, err := ioutil.ReadFile(cfg)
	if err != nil {
		return err
	}

	lines := strings.Split(string(data), "\n")
	var fixed []string
	for _, line := range lines {
		if strings.Index(line, "importmap") == 0 {
			continue
		}
		fixed = append(fixed, line)
	}
	if err := ioutil.WriteFile(cfg, []byte(strings.Join(fixed, "\n")), 0600); err != nil {
		return err
	}

	out, err = cmd.CombinedOutput()
	if err != nil {
		msg := `failed to link test executable:
	reason: %s
	command: %s`
		return fmt.Errorf(msg, string(out), linker+" '"+strings.Join(args, "' '")+"'")
	}

	return nil
}

func locatePackage(name string) (*build.Package, error) {
	// search vendor paths first since that takes priority
	dir, err := filepath.Abs(".")
	if err != nil {
		return nil, err
	}

	for _, gopath := range gopaths {
		gopath = filepath.Join(gopath, "src")
		for strings.HasPrefix(dir, gopath) && dir != gopath {
			pkg, err := build.ImportDir(filepath.Join(dir, "vendor", name), 0)
			if err != nil {
				dir = filepath.Dir(dir)
				continue
			}
			return pkg, nil
		}
	}

	// search source paths otherwise
	for _, p := range build.Default.SrcDirs() {
		abs, err := filepath.Abs(filepath.Join(p, name))
		if err != nil {
			continue
		}
		pkg, err := build.ImportDir(abs, 0)
		if err != nil {
			continue
		}
		return pkg, nil
	}

	return nil, fmt.Errorf("failed to find %s package in any of:\n%s", name, strings.Join(build.Default.SrcDirs(), "\n"))
}

func importPackage(dir string) *build.Package {
	pkg, _ := build.ImportDir(dir, 0)

	// normalize import path for local import packages
	// taken from go source code
	// see: https://github.com/golang/go/blob/go1.7rc5/src/cmd/go/pkg.go#L279
	if pkg != nil && pkg.ImportPath == "." {
		pkg.ImportPath = path.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
	}

	return pkg
}

// from go src
func makeImportValid(r rune) rune {
	// Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
		return '_'
	}
	return r
}

func uniqStringList(strs []string) (unique []string) {
	uniq := make(map[string]void, len(strs))
	for _, s := range strs {
		if _, ok := uniq[s]; !ok {
			uniq[s] = void{}
			unique = append(unique, s)
		}
	}
	return
}

// buildTestMain if given package is valid
// it scans test files for contexts
// and produces a testmain source code.
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
	var contexts []string
	var importPath string
	name := "main"
	if nil != pkg {
		ctxs, err := processPackageTestFiles(
			pkg.TestGoFiles,
			pkg.XTestGoFiles,
		)
		if err != nil {
			return nil, false, err
		}
		contexts = ctxs

		// for module support, query the module import path
		// @TODO: maybe there is a better way to read it
		out, err := exec.Command("go", "list", "-m").CombinedOutput()
		if err != nil {
			// is not using modules or older go version
			importPath = pkg.ImportPath
		} else {
			// otherwise read the module name from command output
			importPath = strings.TrimSpace(string(out))
		}
		name = pkg.Name
	}

	data := struct {
		Name       string
		Contexts   []string
		ImportPath string
	}{name, contexts, importPath}

	var buf bytes.Buffer
	if err := runnerTemplate.Execute(&buf, data); err != nil {
		return nil, len(contexts) > 0, err
	}
	return buf.Bytes(), len(contexts) > 0, nil
}

// processPackageTestFiles runs through ast of each test
// file pack and looks for godog suite contexts to register
// on run
func processPackageTestFiles(packs ...[]string) ([]string, error) {
	var ctxs []string
	fset := token.NewFileSet()
	for _, pack := range packs {
		for _, testFile := range pack {
			node, err := parser.ParseFile(fset, testFile, nil, 0)
			if err != nil {
				return ctxs, err
			}

			ctxs = append(ctxs, astContexts(node)...)
		}
	}
	var failed []string
	for _, ctx := range ctxs {
		runes := []rune(ctx)
		if unicode.IsLower(runes[0]) {
			expected := append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)
			failed = append(failed, fmt.Sprintf("%s - should be: %s", ctx, string(expected)))
		}
	}
	if len(failed) > 0 {
		return ctxs, fmt.Errorf("godog contexts must be exported:\n\t%s", strings.Join(failed, "\n\t"))
	}
	return ctxs, nil
}

func findToolDir() string {
	if out, err := exec.Command("go", "env", "GOTOOLDIR").Output(); err != nil {
		return filepath.Clean(strings.TrimSpace(string(out)))
	}
	return filepath.Clean(build.ToolDir)
}

func dependencies(pkg *build.Package, visited map[string]string, vendor bool) error {
	visited[pkg.ImportPath] = pkg.PkgObj
	imports := pkg.Imports
	if vendor {
		imports = append(imports, pkg.TestImports...)
	}
	for _, name := range imports {
		if i := strings.LastIndex(name, "vendor/"); vendor && i == -1 {
			continue // only interested in vendor packages
		}

		if _, ok := visited[name]; ok {
			continue
		}

		next, err := locatePackage(name)
		if err != nil {
			return err
		}

		visited[name] = pkg.PkgObj
		if err := dependencies(next, visited, vendor); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// +build windows

package bot

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/kardianos/osext"
	"golang.org/x/sys/windows/svc"
)

var started bool
var isIntSess bool
var hostName string
var conn Connector
var botLogger *log.Logger
var finish = make(chan struct{})

func init() {
	hostName = os.Getenv("COMPUTERNAME")
}

func dirExists(path string) bool {
	if len(path) == 0 {
		return false
	}
	ds, err := os.Stat(path)
	if err != nil {
		return false
	}
	if ds.Mode().IsDir() {
		return true
	}
	return false
}

// Start gets the robot going
func Start() {
	botLock.Lock()
	if started {
		botLock.Unlock()
		return
	}
	started = true
	botLock.Unlock()

	const svcName = "gopherbot"
	var err error
	isIntSess, err = svc.IsAnInteractiveSession()
	if err != nil {
		log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
	}

	var execpath, execdir, installdir, localdir string

	// Process command-line flags
	var configDir string
	cusage := "path to the local configuration directory"
	flag.StringVar(&configDir, "config", "", cusage)
	flag.StringVar(&configDir, "c", "", cusage+" (shorthand)")
	var installDir string
	iusage := "path to the local install directory containing default/stock configuration"
	flag.StringVar(&installDir, "install", "", iusage)
	flag.StringVar(&installDir, "i", "", iusage+" (shorthand)")
	var logFile string
	lusage := "path to robot's log file"
	flag.StringVar(&logFile, "log", "", lusage)
	flag.StringVar(&logFile, "l", "", lusage+" (shorthand)")
	var winCommand string
	if isIntSess {
		wusage := "manage Windows service, one of: install, remove, start, stop"
		flag.StringVar(&winCommand, "winsvc", "", wusage)
		flag.StringVar(&winCommand, "w", "", wusage+" (shorthand)")
	}
	flag.Parse()

	if winCommand != "" {
		switch winCommand {
		case "install":
			var args []string
			if configDir != "" {
				args = append(args, "-c", configDir)
			}
			if installDir != "" {
				args = append(args, "-i", installDir)
			}
			err = installService(svcName, "Gopherbot ChatOps chat bot", args)
		case "remove":
			err = removeService(svcName)
		case "start":
			err = startService(svcName)
		case "stop":
			err = controlService(svcName, svc.Stop, svc.Stopped)
		case "pause":
			err = controlService(svcName, svc.Pause, svc.Paused)
		case "continue":
			err = controlService(svcName, svc.Continue, svc.Running)
		default:
			log.Fatalf("invalid command %s", winCommand)
		}
		if err != nil {
			log.Fatalf("failed to %s %s: %v", winCommand, svcName, err)
		}
		return
	}

	if isIntSess {
		botLogger = log.New(os.Stdout, "", log.LstdFlags)
	} else {
		if logFile == "" {
			logFile = "C:/Windows/Temp/gopherbot-startup.log"
		}
		var f os.File
		f, err = os.Create(logFile)
		if err != nil {
			log.Fatal("Unable to open log file")
		}
		botLogger = log.New(f, "", log.LstdFlags)
	}
	botLogger.Println("Starting up ...")

	// Installdir is where the default config and stock external
	// plugins are. Search some likely locations in case installDir
	// wasn't passed on the command line, or Gopherbot isn't installed
	// in one of the usual system locations.
	execpath, err = osext.ExecutableFolder()
	if err == nil {
		execdir = execpath
	}
	instSearchPath := []string{
		installDir,
		`C:/Program Files`,
		`C:/Program Files (x86)`,
	}
	gosearchpath := os.Getenv("GOPATH")
	if len(gosearchpath) > 0 {
		for _, gopath := range strings.Split(gosearchpath, ":") {
			instSearchPath = append(instSearchPath, gopath+"/src/github.com/uva-its/gopherbot")
		}
	}
	home := os.Getenv("USERPROFILE")
	if len(home) > 0 {
		instSearchPath = append(instSearchPath, home+"/go/src/github.com/uva-its/gopherbot")
	}
	instSearchPath = append(instSearchPath, execdir)
	for _, spath := range instSearchPath {
		if len(spath) > 0 && dirExists(spath+"/lib") {
			installdir = spath
			break
		}
	}
	if len(installdir) == 0 {
		botLogger.Println("Install directory not found, exiting")
		os.Exit(0)
	}

	// Localdir is where all user-supplied configuration and
	// external plugins are.
	confSearchPath := []string{
		configDir,
		`C:/Windows/gopherbot`,
	}
	if len(home) > 0 {
		confSearchPath = append(confSearchPath, home+"/.gopherbot")
	}
	for _, spath := range confSearchPath {
		if len(spath) > 0 && dirExists(spath) {
			localdir = spath
			break
		}
	}
	if len(localdir) == 0 {
		botLogger.Println("Couldn't locate local configuration directory, exiting")
		os.Exit(0)
	}

	// Create the 'bot and load configuration, supplying configdir and installdir.
	// When loading configuration, gopherbot first loads default configuration
	// from internal config, then loads from localdir/conf/..., which
	// overrides defaults.
	os.Setenv("GOPHER_INSTALLDIR", installdir)
	os.Setenv("GOPHER_CONFIGDIR", localdir)
	err = newBot(localdir, installdir, botLogger)
	if err != nil {
		botLogger.Fatal(fmt.Errorf("Error loading initial configuration: %v", err))
	}
	botLogger.Printf("Starting up with localdir: %s, and installdir: %s\n", localdir, installdir)

	connectionStarter, ok := connectors[b.protocol]
	if !ok {
		botLogger.Fatal("No connector registered with name:", b.protocol)
	}

	// handler{} is just a placeholder struct for implementing the Handler interface
	h := handler{}
	conn = connectionStarter(h, log.New(ioutil.Discard, "", 0))

	// Initialize the robot with a valid connector
	botInit(conn)

	// Start the brain loop
	go runBrain()
	if isIntSess {
		// Start the connector's main loop for interactive sessions
		conn.Run(finish)
	} else {
		// Stop logging to startup log when running as a service
		b.logger.SetOutput(ioutil.Discard)
		// Started as a Windows Service
		runService(svcName)
	}
}
<commit_msg>Fix Windows log file (corrected the type)<commit_after>// +build windows

package bot

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/kardianos/osext"
	"golang.org/x/sys/windows/svc"
)

var started bool
var isIntSess bool
var hostName string
var conn Connector
var botLogger *log.Logger
var finish = make(chan struct{})

func init() {
	hostName = os.Getenv("COMPUTERNAME")
}

func dirExists(path string) bool {
	if len(path) == 0 {
		return false
	}
	ds, err := os.Stat(path)
	if err != nil {
		return false
	}
	if ds.Mode().IsDir() {
		return true
	}
	return false
}

// Start gets the robot going
func Start() {
	botLock.Lock()
	if started {
		botLock.Unlock()
		return
	}
	started = true
	botLock.Unlock()

	const svcName = "gopherbot"
	var err error
	isIntSess, err = svc.IsAnInteractiveSession()
	if err != nil {
		log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
	}

	var execpath, execdir, installdir, localdir string

	// Process command-line flags
	var configDir string
	cusage := "path to the local configuration directory"
	flag.StringVar(&configDir, "config", "", cusage)
	flag.StringVar(&configDir, "c", "", cusage+" (shorthand)")
	var installDir string
	iusage := "path to the local install directory containing default/stock configuration"
	flag.StringVar(&installDir, "install", "", iusage)
	flag.StringVar(&installDir, "i", "", iusage+" (shorthand)")
	var logFile string
	lusage := "path to robot's log file"
	flag.StringVar(&logFile, "log", "", lusage)
	flag.StringVar(&logFile, "l", "", lusage+" (shorthand)")
	var winCommand string
	if isIntSess {
		wusage := "manage Windows service, one of: install, remove, start, stop"
		flag.StringVar(&winCommand, "winsvc", "", wusage)
		flag.StringVar(&winCommand, "w", "", wusage+" (shorthand)")
	}
	flag.Parse()

	if winCommand != "" {
		switch winCommand {
		case "install":
			var args []string
			if configDir != "" {
				args = append(args, "-c", configDir)
			}
			if installDir != "" {
				args = append(args, "-i", installDir)
			}
			err = installService(svcName, "Gopherbot ChatOps chat bot", args)
		case "remove":
			err = removeService(svcName)
		case "start":
			err = startService(svcName)
		case "stop":
			err = controlService(svcName, svc.Stop, svc.Stopped)
		case "pause":
			err = controlService(svcName, svc.Pause, svc.Paused)
		case "continue":
			err = controlService(svcName, svc.Continue, svc.Running)
		default:
			log.Fatalf("invalid command %s", winCommand)
		}
		if err != nil {
			log.Fatalf("failed to %s %s: %v", winCommand, svcName, err)
		}
		return
	}

	if isIntSess {
		botLogger = log.New(os.Stdout, "", log.LstdFlags)
	} else {
		if logFile == "" {
			logFile = "C:/Windows/Temp/gopherbot-startup.log"
		}
		var f *os.File
		f, err = os.Create(logFile)
		if err != nil {
			log.Fatal("Unable to open log file")
		}
		botLogger = log.New(f, "", log.LstdFlags)
	}
	botLogger.Println("Starting up ...")

	// Installdir is where the default config and stock external
	// plugins are. Search some likely locations in case installDir
	// wasn't passed on the command line, or Gopherbot isn't installed
	// in one of the usual system locations.
	execpath, err = osext.ExecutableFolder()
	if err == nil {
		execdir = execpath
	}
	instSearchPath := []string{
		installDir,
		`C:/Program Files`,
		`C:/Program Files (x86)`,
	}
	gosearchpath := os.Getenv("GOPATH")
	if len(gosearchpath) > 0 {
		for _, gopath := range strings.Split(gosearchpath, ":") {
			instSearchPath = append(instSearchPath, gopath+"/src/github.com/uva-its/gopherbot")
		}
	}
	home := os.Getenv("USERPROFILE")
	if len(home) > 0 {
		instSearchPath = append(instSearchPath, home+"/go/src/github.com/uva-its/gopherbot")
	}
	instSearchPath = append(instSearchPath, execdir)
	for _, spath := range instSearchPath {
		if len(spath) > 0 && dirExists(spath+"/lib") {
			installdir = spath
			break
		}
	}
	if len(installdir) == 0 {
		botLogger.Println("Install directory not found, exiting")
		os.Exit(0)
	}

	// Localdir is where all user-supplied configuration and
	// external plugins are.
	confSearchPath := []string{
		configDir,
		`C:/Windows/gopherbot`,
	}
	if len(home) > 0 {
		confSearchPath = append(confSearchPath, home+"/.gopherbot")
	}
	for _, spath := range confSearchPath {
		if len(spath) > 0 && dirExists(spath) {
			localdir = spath
			break
		}
	}
	if len(localdir) == 0 {
		botLogger.Println("Couldn't locate local configuration directory, exiting")
		os.Exit(0)
	}

	// Create the 'bot and load configuration, supplying configdir and installdir.
	// When loading configuration, gopherbot first loads default configuration
	// from internal config, then loads from localdir/conf/..., which
	// overrides defaults.
	os.Setenv("GOPHER_INSTALLDIR", installdir)
	os.Setenv("GOPHER_CONFIGDIR", localdir)
	err = newBot(localdir, installdir, botLogger)
	if err != nil {
		botLogger.Fatal(fmt.Errorf("Error loading initial configuration: %v", err))
	}
	botLogger.Printf("Starting up with localdir: %s, and installdir: %s\n", localdir, installdir)

	connectionStarter, ok := connectors[b.protocol]
	if !ok {
		botLogger.Fatal("No connector registered with name:", b.protocol)
	}

	// handler{} is just a placeholder struct for implementing the Handler interface
	h := handler{}
	conn = connectionStarter(h, log.New(ioutil.Discard, "", 0))

	// Initialize the robot with a valid connector
	botInit(conn)

	// Start the brain loop
	go runBrain()
	if isIntSess {
		// Start the connector's main loop for interactive sessions
		conn.Run(finish)
	} else {
		// Stop logging to startup log when running as a service
		b.logger.SetOutput(ioutil.Discard)
		// Started as a Windows Service
		runService(svcName)
	}
}
<|endoftext|>


<commit_before>package types

import (
	"fmt"
	"strings"
)

// Expression is S-expression
type Expression interface{}

// Number is number of scheme. (based on Go float64)
type Number float64

// Symbol is index of S-expression in some environment.
type Symbol string

// Boolean is boolean of scheme.
type Boolean bool

func (b Boolean) String() string {
	if b {
		return "#t"
	}
	return "#f"
}

// Pair is cons
type Pair struct {
	Car Expression
	Cdr Expression
}

func (p *Pair) String() string {
	if p.IsNull() {
		return "()"
	}
	if p.IsList() {
		var tokens []string
		pp := p
		for {
			if pp.IsNull() {
				break
			}
			tokens = append(tokens, fmt.Sprintf("%v", pp.Car))
			switch cdr := pp.Cdr.(type) {
			case *Pair:
				pp = cdr
			default:
				break
			}
		}
		return fmt.Sprintf("(%s)", strings.Join(tokens, " "))
	}
	return fmt.Sprintf("(%v . %v)", p.Car, p.Cdr)
}

// IsNull checking if pair is null or not.
func (p *Pair) IsNull() bool {
	return p.Car == nil && p.Cdr == nil
}

// IsList returns if pair is list or not.
//
// * empty pair is list
// * end of list should be empty pair (empty list)
func (p *Pair) IsList() bool {
	pp := p
	for {
		if pp.IsNull() {
			return true
		}
		switch cdr := pp.Cdr.(type) {
		case *Pair:
			pp = cdr
		default:
			return false
		}
	}
}

// Append add cons pair to given pair
// exp, first arguments of callee, should be car of this pair.
func (p *Pair) Append(exp Expression) *Pair {
	// append exp to tail
	pp := p
	for {
		if pp.IsNull() {
			break
		}
		pp = pp.Cdr.(*Pair)
	}
	pp.Car = exp
	pp.Cdr = &Pair{}
	return pp
}

// NewList makes concatenated pair's list.
// Internally, using appned for this operation.
func NewList(args ...Expression) *Pair {
	p := &Pair{Car: nil, Cdr: nil}
	for _, arg := range args {
		p.Append(arg)
	}
	return p
}
<commit_msg>for NewList, use cons internally, not append.<commit_after>package types

import (
	"fmt"
	"strings"
)

// Expression is S-expression
type Expression interface{}

// Number is number of scheme. (based on Go float64)
type Number float64

// Symbol is index of S-expression in some environment.
type Symbol string

// Boolean is boolean of scheme.
type Boolean bool

func (b Boolean) String() string {
	if b {
		return "#t"
	}
	return "#f"
}

// Pair is cons
type Pair struct {
	Car Expression
	Cdr Expression
}

func (p *Pair) String() string {
	if p.IsNull() {
		return "()"
	}
	if p.IsList() {
		var tokens []string
		pp := p
		for {
			if pp.IsNull() {
				break
			}
			tokens = append(tokens, fmt.Sprintf("%v", pp.Car))
			switch cdr := pp.Cdr.(type) {
			case *Pair:
				pp = cdr
			default:
				break
			}
		}
		return fmt.Sprintf("(%s)", strings.Join(tokens, " "))
	}
	return fmt.Sprintf("(%v . %v)", p.Car, p.Cdr)
}

// IsNull checking if pair is null or not.
func (p *Pair) IsNull() bool {
	return p.Car == nil && p.Cdr == nil
}

// IsList returns if pair is list or not.
//
// * empty pair is list
// * end of list should be empty pair (empty list)
func (p *Pair) IsList() bool {
	pp := p
	for {
		if pp.IsNull() {
			return true
		}
		switch cdr := pp.Cdr.(type) {
		case *Pair:
			pp = cdr
		default:
			return false
		}
	}
}

// Append add cons pair to given pair
// exp, first arguments of callee, should be car of this pair.
func (p *Pair) Append(exp Expression) *Pair {
	// append exp to tail
	pp := p
	for {
		if pp.IsNull() {
			break
		}
		pp = pp.Cdr.(*Pair)
	}
	pp.Car = exp
	pp.Cdr = &Pair{}
	return pp
}

// NewList makes concatenated pair's list.
// Internally, creating last pair and concatenate it with previous pair.
func NewList(args ...Expression) *Pair {
	// In normal, p is prefer to be defined by var statement because of no allocation.
	// But in this case, for empty list should be return empty pair.
	p, prev := &Pair{}, &Pair{}
	for i := len(args) - 1; i >= 0; i-- {
		p = &Pair{args[i], prev}
		prev = p
	}
	return p
}
<|endoftext|>


<commit_before>package gmusic

//AddPlaylist is used to add a new playlist
type AddPlaylist struct {
	id      string
	title   string
	success bool
}

//DeletePlaylist is used to delete a playlist
type DeletePlaylist struct {
	deleteId string
}

//Tune is an abstract type
type Tune struct {
	genre          string
	beatsPerMinute int16
	album          string
	id             string
	composer       string
	title          string
	albumArtist    string
	year           int16
	artist         string
	durationMillis int64
	deleted        bool
	playCount      int16
	rating         string
	comment        string
}

//Song is a single song
type Song struct {
	Tune
	totalTracks       int16
	subjectToCuration bool
	name              string
	totalDiscs        int16
	titleNorm         string
	albumNorm         string
	track             int16
	albumArtUrl       string
	url               string
	creationDate      float32
	albumArtistNorm   string
	artistNorm        string
	lastPlayed        float64
	metajamId         string
	songType          int16 //type
	disc              int16
}

//Playlist is a set of songs
type Playlist struct {
	playlistId         string
	title              string
	requestTime        float32
	continuationToken  string
	differentialUpdate bool
	playlist           []Song
}

type Playlists struct {
	playlists      []Playlist
	magicPlaylists []Playlist
}

type SongUrl struct {
	url string
}

type QueryResults struct {
	artists []Song
	albums  []Song
	songs   []Song
}
<commit_msg>adding structs<commit_after>package gmusic

//AddPlaylist is used to add a new playlist
type AddPlaylist struct {
	id      string
	title   string
	success bool
}

//DeletePlaylist is used to delete a playlist
type DeletePlaylist struct {
	deleteId string
}

//Tune is an abstract type
type Tune struct {
	genre          string
	beatsPerMinute int16
	album          string
	id             string
	composer       string
	title          string
	albumArtist    string
	year           int16
	artist         string
	durationMillis int64
	deleted        bool
	playCount      int16
	rating         string
	comment        string
}

//Song is a single song
type Song struct {
	Tune
	totalTracks       int16
	subjectToCuration bool
	name              string
	totalDiscs        int16
	titleNorm         string
	albumNorm         string
	track             int16
	albumArtUrl       string
	url               string
	creationDate      float32
	albumArtistNorm   string
	artistNorm        string
	lastPlayed        float64
	metajamId         string
	songType          int16 //type
	disc              int16
}

//Playlist is a set of songs
type Playlist struct {
	playlistId         string
	title              string
	requestTime        float32
	continuationToken  string
	differentialUpdate bool
	playlist           []Song
}

type Playlists struct {
	playlists      []Playlist
	magicPlaylists []Playlist
}

type SongUrl struct {
	url string
}

type QueryResults struct {
	artists []Song
	albums  []Song
	songs   []Song
}

type QueryResponse struct {
	results QueryResults
}
<|endoftext|>


<commit_before>package brands

import (
	"errors"
	"fmt"
	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

// Driver interface
type Driver interface {
	Read(id string) (brand Brand, canonicalId string, found bool, err error)
	CheckConnectivity() error
}

// CypherDriver struct
type CypherDriver struct {
	conn neoutils.NeoConnection
	env  string
}

//NewCypherDriver instantiate driver
func NewCypherDriver(conn neoutils.NeoConnection, env string) CypherDriver {
	return CypherDriver{conn, env}
}

// CheckConnectivity tests neo4j by running a simple cypher query
func (driver CypherDriver) CheckConnectivity() error {
	return neoutils.Check(driver.conn)
}

func (driver CypherDriver) Read(uuid string) (Brand, string, bool, error) {
	isCanonicalqueryResults := []struct {
		Brand
	}{}

	isCanonicalquery := &neoism.CypherQuery{
		Statement: `
                        MATCH (t:Thing{prefUUID:{uuid}})
			OPTIONAL MATCH (t)<-[:EQUIVALENT_TO]-(x:Thing)
			OPTIONAL MATCH (x)-[:HAS_PARENT]->(p:Thing)
			OPTIONAL MATCH (x)<-[:HAS_PARENT]-(c:Thing)
			RETURN t.prefUUID as id, t.prefLabel as prefLabel, labels(t)as types, t.descriptionXML as descriptionXML, t.strapline as strapline, t.imageUrl as imageUrl,
			collect ( { id: p.uuid, prefId: p.prefUuid, types: labels(p), prefLabel: p.prefLabel } ) AS parentBrands,
			collect ( { id: c.uuid, prefId: c.prefUuid, types: labels(c), prefLabel: c.prefLabel } ) AS childBrands
                `,
		Parameters: neoism.Props{"uuid": uuid},
		Result:     &isCanonicalqueryResults,
	}

	log.Debugf("CypherResult Read Brand for uuid: %s was: %+v", uuid, isCanonicalqueryResults)

	if err := driver.conn.CypherBatch([]*neoism.CypherQuery{isCanonicalquery}); err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v\n", uuid, isCanonicalquery.Statement, err)
		return Brand{}, "", false, fmt.Errorf("Error accessing Brands datastore for uuid: %s", uuid)
	} else if (len(isCanonicalqueryResults)) == 0 {
		canonicalUUid, err := driver.isSourceBrand(uuid)
		return Brand{}, canonicalUUid, false, err
	} else if len(isCanonicalqueryResults) != 1 {
		errMsg := fmt.Sprintf("Multiple brands found with the same uuid:%s !", uuid)
		log.Error(errMsg)
		return Brand{}, "", true, errors.New(errMsg)
	}

	publicAPITransformation(&isCanonicalqueryResults[0].Brand, driver.env)
	return isCanonicalqueryResults[0].Brand, "", true, nil
}

func (driver CypherDriver) isSourceBrand(uuid string) (string, error) {
	isSourceQueryResults := []struct {
		Brand
	}{}

	isSourceQuery := &neoism.CypherQuery{
		Statement: `
                        MATCH (upp:UPPIdentifier{value:{uuid}})-[:IDENTIFIES]->(b:Thing)-[:EQUIVALENT_TO]->(c:Thing)
			RETURN c.prefUUID as id
                `,
		Parameters: neoism.Props{"uuid": uuid},
		Result:     &isSourceQueryResults,
	}

	if err := driver.conn.CypherBatch([]*neoism.CypherQuery{isSourceQuery}); err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v\n", uuid, isSourceQuery.Statement, err)
		return "", fmt.Errorf("Error accessing Brands datastore for uuid: %s", uuid)
	} else if (len(isSourceQueryResults)) == 0 {
		return "", nil
	}

	return isSourceQueryResults[0].ID, nil
}

func publicAPITransformation(brand *Brand, env string) {
	if len(brand.Parents) > 0 {
		parents := []*Thing{}
		for _, idx := range brand.Parents {
			if idx.ID != "" {
				newParent := &Thing{ID: mapper.IDURL(idx.ID), Types: filterToMostSpecificType(idx.Types), APIURL: mapper.APIURL(idx.ID, idx.Types, env), PrefLabel: idx.PrefLabel}
				parents = append(parents, newParent)
			}
		}
		brand.Parents = parents
	} else {
		brand.Parents = []*Thing{}
	}
	if len(brand.Children) > 0 {
		children := []*Thing{}
		for _, idx := range brand.Children {
			if idx.ID != "" {
				newChild := &Thing{ID: mapper.IDURL(idx.ID), Types: filterToMostSpecificType(idx.Types), APIURL: mapper.APIURL(idx.ID, idx.Types, env), PrefLabel: idx.PrefLabel}
				children = append(children, newChild)
			}
		}
		brand.Children = children
	} else {
		brand.Children = []*Thing{}
	}
	brand.DescriptionXML = brand.DescriptionXML
	brand.ImageURL = brand.ImageURL
	brand.Strapline = brand.Strapline
	brand.APIURL = mapper.APIURL(brand.ID, brand.Types, env)
	brand.Types = filterToMostSpecificType(brand.Types)
	brand.ID = mapper.IDURL(brand.ID)
}

func filterToMostSpecificType(unfilteredTypes []string) []string {
	mostSpecificType, _ := mapper.MostSpecificType(unfilteredTypes)
	return mapper.TypeURIs([]string{mostSpecificType})
}
<commit_msg>Removed matching on UppIdentifier<commit_after>package brands

import (
	"errors"
	"fmt"
	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

// Driver interface
type Driver interface {
	Read(id string) (brand Brand, canonicalId string, found bool, err error)
	CheckConnectivity() error
}

// CypherDriver struct
type CypherDriver struct {
	conn neoutils.NeoConnection
	env  string
}

//NewCypherDriver instantiate driver
func NewCypherDriver(conn neoutils.NeoConnection, env string) CypherDriver {
	return CypherDriver{conn, env}
}

// CheckConnectivity tests neo4j by running a simple cypher query
func (driver CypherDriver) CheckConnectivity() error {
	return neoutils.Check(driver.conn)
}

func (driver CypherDriver) Read(uuid string) (Brand, string, bool, error) {
	isCanonicalqueryResults := []struct {
		Brand
	}{}

	isCanonicalquery := &neoism.CypherQuery{
		Statement: `
                        MATCH (t:Thing{prefUUID:{uuid}})
			OPTIONAL MATCH (t)<-[:EQUIVALENT_TO]-(x:Thing)
			OPTIONAL MATCH (x)-[:HAS_PARENT]->(p:Thing)
			OPTIONAL MATCH (x)<-[:HAS_PARENT]-(c:Thing)
			RETURN t.prefUUID as id, t.prefLabel as prefLabel, labels(t)as types, t.descriptionXML as descriptionXML, t.strapline as strapline, t.imageUrl as imageUrl,
			collect ( { id: p.uuid, prefId: p.prefUuid, types: labels(p), prefLabel: p.prefLabel } ) AS parentBrands,
			collect ( { id: c.uuid, prefId: c.prefUuid, types: labels(c), prefLabel: c.prefLabel } ) AS childBrands
                `,
		Parameters: neoism.Props{"uuid": uuid},
		Result:     &isCanonicalqueryResults,
	}

	log.Debugf("CypherResult Read Brand for uuid: %s was: %+v", uuid, isCanonicalqueryResults)

	if err := driver.conn.CypherBatch([]*neoism.CypherQuery{isCanonicalquery}); err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v\n", uuid, isCanonicalquery.Statement, err)
		return Brand{}, "", false, fmt.Errorf("Error accessing Brands datastore for uuid: %s", uuid)
	} else if (len(isCanonicalqueryResults)) == 0 {
		canonicalUUid, err := driver.isSourceBrand(uuid)
		return Brand{}, canonicalUUid, false, err
	} else if len(isCanonicalqueryResults) != 1 {
		errMsg := fmt.Sprintf("Multiple brands found with the same uuid:%s !", uuid)
		log.Error(errMsg)
		return Brand{}, "", true, errors.New(errMsg)
	}

	publicAPITransformation(&isCanonicalqueryResults[0].Brand, driver.env)
	return isCanonicalqueryResults[0].Brand, "", true, nil
}

func (driver CypherDriver) isSourceBrand(uuid string) (string, error) {
	isSourceQueryResults := []struct {
		Brand
	}{}

	isSourceQuery := &neoism.CypherQuery{
		Statement: `
                        MATCH (b:Thing{uuid:{uuid}})-[:EQUIVALENT_TO]->(c:Thing)
			RETURN c.prefUUID as id
                `,
		Parameters: neoism.Props{"uuid": uuid},
		Result:     &isSourceQueryResults,
	}

	if err := driver.conn.CypherBatch([]*neoism.CypherQuery{isSourceQuery}); err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v\n", uuid, isSourceQuery.Statement, err)
		return "", fmt.Errorf("Error accessing Brands datastore for uuid: %s", uuid)
	} else if (len(isSourceQueryResults)) == 0 {
		return "", nil
	}

	return isSourceQueryResults[0].ID, nil
}

func publicAPITransformation(brand *Brand, env string) {
	if len(brand.Parents) > 0 {
		parents := []*Thing{}
		for _, idx := range brand.Parents {
			if idx.ID != "" {
				newParent := &Thing{ID: mapper.IDURL(idx.ID), Types: filterToMostSpecificType(idx.Types), APIURL: mapper.APIURL(idx.ID, idx.Types, env), PrefLabel: idx.PrefLabel}
				parents = append(parents, newParent)
			}
		}
		brand.Parents = parents
	} else {
		brand.Parents = []*Thing{}
	}
	if len(brand.Children) > 0 {
		children := []*Thing{}
		for _, idx := range brand.Children {
			if idx.ID != "" {
				newChild := &Thing{ID: mapper.IDURL(idx.ID), Types: filterToMostSpecificType(idx.Types), APIURL: mapper.APIURL(idx.ID, idx.Types, env), PrefLabel: idx.PrefLabel}
				children = append(children, newChild)
			}
		}
		brand.Children = children
	} else {
		brand.Children = []*Thing{}
	}
	brand.DescriptionXML = brand.DescriptionXML
	brand.ImageURL = brand.ImageURL
	brand.Strapline = brand.Strapline
	brand.APIURL = mapper.APIURL(brand.ID, brand.Types, env)
	brand.Types = filterToMostSpecificType(brand.Types)
	brand.ID = mapper.IDURL(brand.ID)
}

func filterToMostSpecificType(unfilteredTypes []string) []string {
	mostSpecificType, _ := mapper.MostSpecificType(unfilteredTypes)
	return mapper.TypeURIs([]string{mostSpecificType})
}
<|endoftext|>


<commit_before>package pool

type Job struct {
	JobId int
}
<commit_msg>add job result<commit_after>package pool

type Job struct {
	JobId int
}

type JobResult struct {
	JobId      int
	WorkerId   int
	Error      error
	ResultType interface{}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package stylish

import (
	"fmt"
	"strings"

	"github.com/mitchellh/go-wordwrap"
)

// ProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessStart "i am a process"
//
// Output:
// + I am a process ------------------------------------------------------------ >
func ProcessStart(msg string, v ...interface{}) string {

	maxLen := 80
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("+ %s %s >", process, strings.Repeat("-", (maxLen-subLen))))
}

// NestedProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
// with a nested prefix according to the level specified
func NestedProcessStart(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), ProcessStart(msg))

	maxLen := 80 - level
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("%s+ %s %s >", GenerateNestedPrefix(level) process, strings.Repeat("-", (maxLen-subLen))))
}

// ProcessEnd styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessEnd
//
// Output:
// <new line>
func ProcessEnd() string {
	return fmt.Sprintf("\n")
}

// Marker is the root for Bullet/SubBullet; used alone, it allows for a custom
// mark to be specified
//
// Usage:
// Maker "*",  "i am a marker"
//
// Output:
// * i am a marker
func Marker(mark, msg string, v ...interface{}) string {
	return fmt.Sprintf("%s %s\n", mark, fmt.Sprintf(msg, v...))
}

// Bullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// Bullet "i am a bullet"
//
// Output:
// + i am a bullet
func Bullet(msg string, v ...interface{}) string {
	return Marker("+", fmt.Sprintf(msg, v...))
}

// NestedBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
// with a nested prefix according to the level specified
func NestedBullet(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), Bullet(msg))
}

// SubBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// SubBullet "i am a sub bullet"
//
// Output:
//   i am a sub bullet
func SubBullet(msg string, v ...interface{}) string {
	return Marker(" ", fmt.Sprintf(msg, v...))
}

// Warning styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#warning
//
// Usage:
// Warning "You just bought Hot Pockets!"
//
// Output:
// -----------------------------  WARNING  -----------------------------
// You just bought Hot Pockets!
func Warning(body string, v ...interface{}) string {
	return fmt.Sprintf(`
----------------------------------  WARNING  ----------------------------------
%s
`, wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// ErrorHead styles and prints an error heading as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorHead "nuclear launch detected"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
func ErrorHead(heading string, v ...interface{}) string {
	return fmt.Sprintf("\n! %s !\n", strings.ToUpper(fmt.Sprintf(heading, v...)))
}

// ErrorBody styles and prints an error body as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorBody "All your base are belong to us"
//
// Output:
// All your base are belong to us
func ErrorBody(body string, v ...interface{}) string {
	return fmt.Sprintf("%s\n", wordwrap.WrapString(fmt.Sprintf(body, v...), 80))
}

// Error styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// Error "nuclear launch detected", "All your base are belong to us"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
//
// All your base are belong to us
func Error(heading, body string) string {
	return fmt.Sprintf("%s%s", ErrorHead(heading), ErrorBody(body))
}

// GenerateNestedPrefix will generate a prefix string of spaces to match the
// specified depth level
func GenerateNestedPrefix(level int) string {
	prefix := ""

	for i := 0; i < level; i++ {
		prefix += "  "
	}

	return prefix
}
<commit_msg>Add comma to fix compiler error<commit_after>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package stylish

import (
	"fmt"
	"strings"

	"github.com/mitchellh/go-wordwrap"
)

// ProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessStart "i am a process"
//
// Output:
// + I am a process ------------------------------------------------------------ >
func ProcessStart(msg string, v ...interface{}) string {

	maxLen := 80
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("+ %s %s >", process, strings.Repeat("-", (maxLen-subLen))))
}

// NestedProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
// with a nested prefix according to the level specified
func NestedProcessStart(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), ProcessStart(msg))

	maxLen := 80 - level
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("%s+ %s %s >", GenerateNestedPrefix(level), process, strings.Repeat("-", (maxLen-subLen))))
}

// ProcessEnd styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessEnd
//
// Output:
// <new line>
func ProcessEnd() string {
	return fmt.Sprintf("\n")
}

// Marker is the root for Bullet/SubBullet; used alone, it allows for a custom
// mark to be specified
//
// Usage:
// Maker "*",  "i am a marker"
//
// Output:
// * i am a marker
func Marker(mark, msg string, v ...interface{}) string {
	return fmt.Sprintf("%s %s\n", mark, fmt.Sprintf(msg, v...))
}

// Bullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// Bullet "i am a bullet"
//
// Output:
// + i am a bullet
func Bullet(msg string, v ...interface{}) string {
	return Marker("+", fmt.Sprintf(msg, v...))
}

// NestedBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
// with a nested prefix according to the level specified
func NestedBullet(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), Bullet(msg))
}

// SubBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// SubBullet "i am a sub bullet"
//
// Output:
//   i am a sub bullet
func SubBullet(msg string, v ...interface{}) string {
	return Marker(" ", fmt.Sprintf(msg, v...))
}

// Warning styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#warning
//
// Usage:
// Warning "You just bought Hot Pockets!"
//
// Output:
// -----------------------------  WARNING  -----------------------------
// You just bought Hot Pockets!
func Warning(body string, v ...interface{}) string {
	return fmt.Sprintf(`
----------------------------------  WARNING  ----------------------------------
%s
`, wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// ErrorHead styles and prints an error heading as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorHead "nuclear launch detected"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
func ErrorHead(heading string, v ...interface{}) string {
	return fmt.Sprintf("\n! %s !\n", strings.ToUpper(fmt.Sprintf(heading, v...)))
}

// ErrorBody styles and prints an error body as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorBody "All your base are belong to us"
//
// Output:
// All your base are belong to us
func ErrorBody(body string, v ...interface{}) string {
	return fmt.Sprintf("%s\n", wordwrap.WrapString(fmt.Sprintf(body, v...), 80))
}

// Error styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// Error "nuclear launch detected", "All your base are belong to us"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
//
// All your base are belong to us
func Error(heading, body string) string {
	return fmt.Sprintf("%s%s", ErrorHead(heading), ErrorBody(body))
}

// GenerateNestedPrefix will generate a prefix string of spaces to match the
// specified depth level
func GenerateNestedPrefix(level int) string {
	prefix := ""

	for i := 0; i < level; i++ {
		prefix += "  "
	}

	return prefix
}
<|endoftext|>


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

// Package cdap provides a Terraform provider to manage CDAP APIs.
package cdap

import (
	"context"
	"net/http"
	"time"

	"cloud.google.com/go/storage"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"golang.org/x/oauth2"
	"google.golang.org/api/option"
)

const defaultNamespace = "default"

// Provider returns a terraform.ResourceProvider.
func Provider() *schema.Provider {
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"host": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "The address of the CDAP instance.",
			},
			"token": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "The OAuth token to use for all http calls to the instance.",
			},
		},
		ConfigureFunc: configureProvider,
		ResourcesMap: map[string]*schema.Resource{
			"cdap_application":           resourceApplication(),
			"cdap_gcs_artifact":          resourceGCSArtifact(),
			"cdap_local_artifact":        resourceLocalArtifact(),
			"cdap_namespace":             resourceNamespace(),
			"cdap_namespace_preferences": resourceNamespacePreferences(),
			"cdap_profile":               resourceProfile(),
		},
	}
}

// Config provides service configuration for service clients.
type Config struct {
	host          string
	httpClient    *http.Client
	storageClient *storage.Client
}

func configureProvider(d *schema.ResourceData) (interface{}, error) {
	ctx := context.Background()
	httpClient := oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
		AccessToken: d.Get("token").(string),
		TokenType:   "Bearer",
	}))
	httpClient.Timeout = 30 * time.Minute

	storageClient, err := storage.NewClient(ctx, option.WithScopes(storage.ScopeReadOnly))
	if err != nil {
		return nil, err
	}

	return &Config{
		host:          d.Get("host").(string),
		httpClient:    httpClient,
		storageClient: storageClient,
	}, nil
}
<commit_msg>make token optional (#46)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cdap provides a Terraform provider to manage CDAP APIs.
package cdap

import (
	"context"
	"net/http"
	"time"

	"cloud.google.com/go/storage"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"golang.org/x/oauth2"
	"google.golang.org/api/option"
)

const defaultNamespace = "default"

// Provider returns a terraform.ResourceProvider.
func Provider() *schema.Provider {
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"host": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "The address of the CDAP instance.",
			},
			"token": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Description: "The OAuth token to use for all http calls to the instance.",
			},
		},
		ConfigureFunc: configureProvider,
		ResourcesMap: map[string]*schema.Resource{
			"cdap_application":           resourceApplication(),
			"cdap_gcs_artifact":          resourceGCSArtifact(),
			"cdap_local_artifact":        resourceLocalArtifact(),
			"cdap_namespace":             resourceNamespace(),
			"cdap_namespace_preferences": resourceNamespacePreferences(),
			"cdap_profile":               resourceProfile(),
		},
	}
}

// Config provides service configuration for service clients.
type Config struct {
	host          string
	httpClient    *http.Client
	storageClient *storage.Client
}

func configureProvider(d *schema.ResourceData) (interface{}, error) {
	ctx := context.Background()

	httpClient := &http.Client{}
	if token, ok := d.GetOk("token"); ok {
		httpClient = oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
			AccessToken: token.(string),
			TokenType:   "Bearer",
		}))
	}
	httpClient.Timeout = 30 * time.Minute

	storageClient, err := storage.NewClient(ctx, option.WithScopes(storage.ScopeReadOnly))
	if err != nil {
		return nil, err
	}

	return &Config{
		host:          d.Get("host").(string),
		httpClient:    httpClient,
		storageClient: storageClient,
	}, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"context"
	"net/http"
	"os"

	baz "github.com/uber/zanzibar/examples/example-gateway/build/clients/baz"
	clientsBazBaz "github.com/uber/zanzibar/examples/example-gateway/build/gen-code/clients/baz/baz"
	testBackend "github.com/uber/zanzibar/test/lib/test_backend"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

const (
	httpPort     int32 = 8092
	tchannelPort int32 = 8094
)

func main() {
	var logger = zap.New(
		zapcore.NewCore(
			zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
			os.Stderr,
			zap.InfoLevel,
		),
	)
	serverTChannel(logger)
	serveHTTP(logger)
}

func serveHTTP(logger *zap.Logger) {
	httpBackend := testBackend.CreateHTTPBackend(httpPort)
	err := httpBackend.Bootstrap()
	if err != nil {
		panic(err)
	}
	handleContacts := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(202)
		_, _ = w.Write([]byte("{}"))
	}
	handleGoogleNow := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(202)
	}
	httpBackend.HandleFunc("POST", "/foo/contacts", handleContacts)
	httpBackend.HandleFunc("POST", "/add-credentials", handleGoogleNow)

	logger.Info("HTTP server listening on port & serving")

	httpBackend.Wait()
}

func serverTChannel(logger *zap.Logger) {
	logger.Info("Setting up TChannel server")
	tchannelBackend, err := testBackend.CreateTChannelBackend(tchannelPort, "Qux")
	if err != nil {
		panic(err)
	}

	handleSimpleServiceCall := func(
		ctx context.Context,
		reqHeaders map[string]string,
		args *clientsBazBaz.SimpleService_Call_Args,
	) (map[string]string, error) {
		return nil, nil
	}
	simpleServiceCallHandler := baz.NewSimpleServiceCallHandler(handleSimpleServiceCall)

	// must register handler first before bootstrap
	tchannelBackend.Register("SimpleService", "Call", simpleServiceCallHandler)

	err = tchannelBackend.Bootstrap()
	if err != nil {
		panic(err)
	}

	logger.Info("TChannel server listening on port & serving")
}
<commit_msg>Move server wait to main function<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"context"
	"net/http"
	"os"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	baz "github.com/uber/zanzibar/examples/example-gateway/build/clients/baz"
	clientsBazBaz "github.com/uber/zanzibar/examples/example-gateway/build/gen-code/clients/baz/baz"
	testBackend "github.com/uber/zanzibar/test/lib/test_backend"
)

const (
	httpPort     int32 = 8092
	tchannelPort int32 = 8094
)

func main() {
	var logger = zap.New(
		zapcore.NewCore(
			zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
			os.Stderr,
			zap.InfoLevel,
		),
	)

	httpBackend := serveHTTP(logger)
	_ = serverTChannel(logger)

	httpBackend.Wait()
}

func serveHTTP(logger *zap.Logger) *testBackend.TestHTTPBackend {
	httpBackend := testBackend.CreateHTTPBackend(httpPort)
	err := httpBackend.Bootstrap()
	if err != nil {
		panic(err)
	}
	handleContacts := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(202)
		_, _ = w.Write([]byte("{}"))
	}
	handleGoogleNow := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(202)
	}
	httpBackend.HandleFunc("POST", "/foo/contacts", handleContacts)
	httpBackend.HandleFunc("POST", "/add-credentials", handleGoogleNow)

	logger.Info("HTTP server listening on port & serving")

	return httpBackend
}

func serverTChannel(logger *zap.Logger) *testBackend.TestTChannelBackend {
	tchannelBackend, err := testBackend.CreateTChannelBackend(tchannelPort, "Qux")
	if err != nil {
		panic(err)
	}

	handleSimpleServiceCall := func(
		ctx context.Context,
		reqHeaders map[string]string,
		args *clientsBazBaz.SimpleService_Call_Args,
	) (map[string]string, error) {
		return nil, nil
	}
	simpleServiceCallHandler := baz.NewSimpleServiceCallHandler(handleSimpleServiceCall)

	// must register handler first before bootstrap
	tchannelBackend.Register("SimpleService", "Call", simpleServiceCallHandler)

	err = tchannelBackend.Bootstrap()
	if err != nil {
		panic(err)
	}

	logger.Info("TChannel server listening on port & serving")
	return tchannelBackend
}
<|endoftext|>