<commit_before>// Dataflow kit - main
//
// Copyright © 2017-2018 Slotix s.r.o. <dm@slotix.sk>
//
//
// All rights reserved. Use of this source code is governed
// by the BSD 3-Clause License license.

// Parse service of the Dataflow kit parses html content from web pages following the rules described in Payload JSON file.
// Examples for accessing Parse endpoint are here :
//
/*
		{
   "name":"collection",
   "request":{
      "url":"https://example.com"
   },
   "fields":[
      {
         "name":"Title",
         "selector":".product-container a",
         "extractor":{
            "type":"link",
            "params":{
               "includeIfEmpty":false
            }
         }
      },
      {
         "name":"Image",
         "selector":"#product-container img",
         "extractor":{
            "type":"image"
         }
      },
      {
         "name":"Buyinfo",
         "selector":".buy-info",
         "extractor":{
            "type":"text",
            "params":{
               "includeIfEmpty":false
            }
         }
      }
   ],
   "paginator":{
      "selector":".next",
      "attr":"href",
      "maxPages":3
   },
   "format":"json",
   "paginateResults":false
}
*/

package main

// EOF
<commit_msg>testing godoc.org output<commit_after>// Dataflow kit - main
//
// Copyright © 2017-2018 Slotix s.r.o. <dm@slotix.sk>
//
//
// All rights reserved. Use of this source code is governed
// by the BSD 3-Clause License license.

// Parse service of the Dataflow kit parses html content from web pages following the rules described in Payload JSON file.
// Examples for accessing Parse endpoint are here :
//

/* 		{
		"name":"collection",
		"request":{
			"url":"https://example.com"
		},
		"fields":[
			{
				"name":"Title",
				"selector":".product-container a",
				"extractor":{
					"type":"link",
					"params":{
					"includeIfEmpty":false
					}
				}
			},
			{
				"name":"Image",
				"selector":"#product-container img",
				"extractor":{
					"type":"image"
				}
			},
			{
				"name":"Buyinfo",
				"selector":".buy-info",
				"extractor":{
					"type":"text",
					"params":{
					"includeIfEmpty":false
					}
				}
			}
		],
		"paginator":{
			"selector":".next",
			"attr":"href",
			"maxPages":3
		},
		"format":"json",
		"paginateResults":false
		} */
//
package main

// EOF
<|endoftext|>


<commit_before>package openapi

import (
	"github.com/aliyun/aliyun-cli/meta"
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
	"github.com/aliyun/aliyun-cli/cli"
	"strings"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
)

func (c *Caller) InvokeRpc(ctx *cli.Context, product *meta.Product, apiName string) {
	api, ok := c.library.GetApi(product.Name, product.Version, apiName)
	if !ok {
		ctx.Command().PrintFailed(fmt.Errorf("invailed api: %s", apiName),
			fmt.Sprintf("Use\n `aliyun help %s` to view product list\n  or add --force to skip check", product.Name))
		return
	}

	//
	// call OpenApi
	// return: if check failed return error, otherwise return nil
	client, request, err := c.InitClient(ctx, product, true)
	request.ApiName = apiName
	// fmt.Printf(">>> %v\n", request)

	if err != nil {
		ctx.Command().PrintFailed(fmt.Errorf("init client failed: %v", err), "")
		return
	}

	err = c.FillRpcParameters(ctx, request, &api)
	if err != nil {
		ctx.Command().PrintFailed(fmt.Errorf("init client failed: %v", err), "")
		return
	}
	resp, err := client.ProcessCommonRequest(request)

	if err != nil {
		ctx.Command().PrintFailed(err, "")
	}
	fmt.Println(resp.GetHttpContentString())
}

func (c *Caller) InvokeRpcForce(ctx *cli.Context, product *meta.Product, apiName string) {
	//
	// call OpenApi
	// return: if check failed return error, otherwise return nil
	client, request, err := c.InitClient(ctx, product, true)
	request.ApiName = apiName

	if err != nil {
		ctx.Command().PrintFailed(fmt.Errorf("init client failed: %v", err), "")
		return
	}

	c.FillRpcParameters(ctx, request, nil)
	resp, err := client.ProcessCommonRequest(request)

	if err != nil {
		ctx.Command().PrintFailed(err, "")
	}
	fmt.Println(resp.GetHttpContentString())
}

func (c *Caller) FillRpcParameters(ctx *cli.Context, request *requests.CommonRequest, api *meta.Api) error {
	for _, f := range ctx.UnknownFlags().Flags() {
		request.QueryParams[f.Name] = f.GetValue()
		if api != nil && !api.HasParameter(f.Name){
			return fmt.Errorf("unknown parameter %s", f.Name)
		}
	}
	if api != nil {
		err := api.CheckRequiredParameters(func(s string) bool {
			switch s {
			case "RegionId":
				return request.RegionId != ""
			case "Action":
				return request.ApiName != ""
			default:
				return ctx.UnknownFlags().IsAssigned(s)
			}
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *Caller) InitClient(ctx *cli.Context, product *meta.Product, isRpc bool) (*sdk.Client, *requests.CommonRequest, error){
	//
	// call OpenApi
	// return: if check failed return error, otherwise return nil
	client, err := c.profile.GetClient()
	if err != nil {
		return nil, nil, fmt.Errorf("get client failed %b", err)
	}

	request := requests.NewCommonRequest()
	request.Headers["User-Agent"] = "Aliyun-CLI-V0.31"
	request.RegionId = c.profile.RegionId
	request.Product = product.Name
	request.Version = product.Version

	if v, ok := ctx.Flags().GetValue("region"); ok {
		request.RegionId = v
	}
	if v, ok := ctx.Flags().GetValue("endpoint"); ok {
		request.Domain = v
	}
	if v, ok := ctx.Flags().GetValue("version"); ok {
		request.Version = v
	}

	if v, ok := ctx.Flags().GetValue("content-type"); ok {
		request.SetContentType(v)
	} else if isRpc {
		request.SetContentType("application/json")
	}

	if v, ok := ctx.Flags().GetValue("accept"); ok {
		request.Headers["Accept"] = v
		if strings.Contains(v, "xml") {
			request.AcceptFormat = "XML"
		} else if strings.Contains(v, "json") {
			request.AcceptFormat = "JSON"
		}
	}
	if f := ctx.Flags().Get("header"); f != nil {
		for _, v := range f.GetValues() {
			if k2, v2, ok := cli.SplitWith(v, "="); ok {
				request.Headers[k2] = v2
			} else {
				return nil, nil, fmt.Errorf("invaild flag --header %s", v)
			}
		}
	}
	if request.Version == "" {
		return nil, nil, fmt.Errorf("unknown version! Use flag --version 2016-07-09 to assign version")
	}
	if request.Domain == "" {
		request.Domain, err = product.GetEndpoint(request.RegionId, client)
		if err != nil {
			return nil, nil, fmt.Errorf("unknown endpoint! Use flag --endpoint xxx.aliyuncs.com to assign endpoint")
		}
	}

	return client, request, nil
}
<commit_msg>improve error processing<commit_after>package openapi

import (
	"github.com/aliyun/aliyun-cli/meta"
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
	"github.com/aliyun/aliyun-cli/cli"
	"strings"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
)

func (c *Caller) InvokeRpc(ctx *cli.Context, product *meta.Product, apiName string) {
	api, ok := c.library.GetApi(product.Name, product.Version, apiName)
	if !ok {
		ctx.Command().PrintFailed(fmt.Errorf("invailed api: %s", apiName),
			fmt.Sprintf("Use\n `aliyun help %s` to view product list\n  or add --force to skip check", product.Name))
		return
	}

	//
	// call OpenApi
	// return: if check failed return error, otherwise return nil
	client, request, err := c.InitClient(ctx, product, true)
	if err != nil {
		ctx.Command().PrintFailed(fmt.Errorf("init failed: %v", err), "")
		return
	}

	request.ApiName = apiName
	err = c.FillRpcParameters(ctx, request, &api)
	if err != nil {
		ctx.Command().PrintFailed(fmt.Errorf("init failed: %v", err), "")
		return
	}
	resp, err := client.ProcessCommonRequest(request)

	if err != nil {
		ctx.Command().PrintFailed(err, "")
	}
	fmt.Println(resp.GetHttpContentString())
}

func (c *Caller) InvokeRpcForce(ctx *cli.Context, product *meta.Product, apiName string) {
	//
	// call OpenApi
	// return: if check failed return error, otherwise return nil
	client, request, err := c.InitClient(ctx, product, true)

	if err != nil {
		ctx.Command().PrintFailed(fmt.Errorf("init client failed: %v", err), "")
		return
	}

	request.ApiName = apiName

	c.FillRpcParameters(ctx, request, nil)
	resp, err := client.ProcessCommonRequest(request)

	if err != nil {
		ctx.Command().PrintFailed(err, "")
	}
	fmt.Println(resp.GetHttpContentString())
}

func (c *Caller) FillRpcParameters(ctx *cli.Context, request *requests.CommonRequest, api *meta.Api) error {
	for _, f := range ctx.UnknownFlags().Flags() {
		request.QueryParams[f.Name] = f.GetValue()
		if api != nil && !api.HasParameter(f.Name){
			return fmt.Errorf("unknown parameter %s", f.Name)
		}
	}
	if api != nil {
		err := api.CheckRequiredParameters(func(s string) bool {
			switch s {
			case "RegionId":
				return request.RegionId != ""
			case "Action":
				return request.ApiName != ""
			default:
				return ctx.UnknownFlags().IsAssigned(s)
			}
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *Caller) InitClient(ctx *cli.Context, product *meta.Product, isRpc bool) (*sdk.Client, *requests.CommonRequest, error){
	//
	// call OpenApi
	// return: if check failed return error, otherwise return nil
	client, err := c.profile.GetClient()
	if err != nil {
		return nil, nil, fmt.Errorf("bad client %v", err)
	}

	request := requests.NewCommonRequest()
	request.Headers["User-Agent"] = "Aliyun-CLI-V0.31"
	request.RegionId = c.profile.RegionId
	request.Product = product.Name
	request.Version = product.Version

	if v, ok := ctx.Flags().GetValue("region"); ok {
		request.RegionId = v
	}
	if v, ok := ctx.Flags().GetValue("endpoint"); ok {
		request.Domain = v
	}
	if v, ok := ctx.Flags().GetValue("version"); ok {
		request.Version = v
	}

	if v, ok := ctx.Flags().GetValue("content-type"); ok {
		request.SetContentType(v)
	} else if isRpc {
		request.SetContentType("application/json")
	}

	if v, ok := ctx.Flags().GetValue("accept"); ok {
		request.Headers["Accept"] = v
		if strings.Contains(v, "xml") {
			request.AcceptFormat = "XML"
		} else if strings.Contains(v, "json") {
			request.AcceptFormat = "JSON"
		}
	}
	if f := ctx.Flags().Get("header"); f != nil {
		for _, v := range f.GetValues() {
			if k2, v2, ok := cli.SplitWith(v, "="); ok {
				request.Headers[k2] = v2
			} else {
				return nil, nil, fmt.Errorf("invaild flag --header %s", v)
			}
		}
	}
	if request.Version == "" {
		return nil, nil, fmt.Errorf("unknown version! Use flag --version 2016-07-09 to assign version")
	}
	if request.Domain == "" {
		request.Domain, err = product.GetEndpoint(request.RegionId, client)
		if err != nil {
			return nil, nil, fmt.Errorf("unknown endpoint! Use flag --endpoint xxx.aliyuncs.com to assign endpoint")
		}
	}

	return client, request, nil
}
<|endoftext|>


<commit_before>package parser

import (
	"testing"

	"github.com/fatih/color"
)

var items = map[itemType]string{
	itemVersion:  "itemVersion",
	itemOperator: "itemOperator",
	itemSet:      "itemSet",
	itemRange:    "itemRange",
	itemAdvanced: "itemAdvanced",
	itemError:    "itemError",
	itemEOF:      "itemEOF",
}

type results []itemType

type lexerTestables struct {
	expected bool
	value    string
	result   results
}

var constraints = []*lexerTestables{
	{true, "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3",
		results{
			itemVersion,
			itemRange,
			itemOperator,
			itemVersion,
			itemRange,
			itemVersion,
			itemAdvanced,
			itemVersion,
		},
	},
	// Operators
	{true, "=2.3.2",
		results{itemOperator, itemVersion},
	},
	{true, "<=1.2.3",
		results{itemOperator, itemVersion},
	},
	{true, ">=1.2.3",
		results{itemOperator, itemVersion},
	},
	// Sets
	{true, "5.3.5 4.3.5",
		results{itemVersion, itemSet, itemVersion},
	},
	//Ranges
	{true, "5.3.5||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5 ||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5|| 4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{false, "5.3.5||  4.3.5",
		results{itemVersion, itemRange},
	},
	// Tilde and Caret Ranges
	{false, "~ 1.2.3",
		results{itemAdvanced},
	},
	{true, "~1.2.3",
		results{itemAdvanced, itemVersion},
	},
	{true, "^4.5.2-alpha.1",
		results{itemAdvanced, itemVersion},
	},
	{false, ">= 1.2.3",
		results{},
	},
	// Hyphen Range
	{false, "1.2.3 -3.2.5",
		results{itemVersion, itemAdvanced},
	},
	// X-Ranges
	{true, "*",
		results{itemAdvanced},
	},
	{false, "**",
		results{},
	},
	{true, "1.0",
		results{itemAdvanced},
	},
	{true, "1.x",
		results{itemAdvanced},
	},
	{true, "*.x",
		results{itemAdvanced},
	},
	{false, "1.x+98uihuhyg",
		results{},
	},
	{true, "1.*.2",
		results{itemAdvanced},
	},
	{true, "1.*.x || 1.x.4",
		results{itemAdvanced, itemRange, itemAdvanced},
	},
	{true, "1.*.2-beta",
		results{itemAdvanced},
	},
	{true, "*.1.2",
		results{itemAdvanced},
	},
	{false, "1x.2.*",
		results{},
	},
	{false, "1.x2.*",
		results{},
	},
	{false, "1...1",
		results{},
	},
	{false, "1...",
		results{},
	},
	{false, "1.x.",
		results{},
	},

	// Assorted syntax errors
	{false, "1.2.3-",
		results{},
	},
	{false, "1.2.3 >=",
		results{itemVersion, itemSet},
	},
	{false, "5.3.5 |1| 4.3.5",
		results{itemVersion},
	},
	{false, "1.2.3.1",
		results{},
	},
	{false, "5. 4.4",
		results{},
	},
	{false, "<1<1",
		results{itemOperator},
	},
	{false, "<1||",
		results{itemOperator, itemAdvanced, itemRange},
	},
	{false, "M",
		results{},
	},
}

func init() {
	// Appends appropriate end token based on expected result.
	for _, c := range constraints {
		if c.expected {
			c.result = append(c.result, itemEOF)
		} else {
			c.result = append(c.result, itemError)
		}
	}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()

func TestLexer(t *testing.T) {
	for _, c := range constraints {
		_, ch := lex(c.value)
		result := true
		x := 0
		for i := range ch {

			result = (i.typ != itemError)
			if len(c.result) > x {
				if i.typ != c.result[x] {
					t.Logf("lex(%v) => %v(%v), want %v \n", cyan(c.value), items[i.typ], yellow(i), items[c.result[x]])
				}
			} else {
				t.Logf("lex(%v) => %v(%v), want <nil>\n", cyan(c.value), items[i.typ], yellow(i))
			}
			x++
		}
		if result != c.expected {
			t.Logf("lex(%v) => %t, want %t \n", cyan(c.value), result, c.expected)
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerComplex(b *testing.B) {
	const VERSION = "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3 || ~4.3.1 ^2.1.1"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerSimple(b *testing.B) {
	const VERSION = "1.0.0"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}
<commit_msg>Added more tests.<commit_after>package parser

import (
	"testing"

	"github.com/fatih/color"
)

var items = map[itemType]string{
	itemVersion:  "itemVersion",
	itemOperator: "itemOperator",
	itemSet:      "itemSet",
	itemRange:    "itemRange",
	itemAdvanced: "itemAdvanced",
	itemError:    "itemError",
	itemEOF:      "itemEOF",
}

type results []itemType

type lexerTestables struct {
	expected bool
	value    string
	result   results
}

var constraints = []*lexerTestables{
	{true, "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3",
		results{
			itemVersion,
			itemRange,
			itemOperator,
			itemVersion,
			itemRange,
			itemVersion,
			itemAdvanced,
			itemVersion,
		},
	},
	// Operators
	{true, "=2.3.2",
		results{itemOperator, itemVersion},
	},
	{true, "<=1.2.3",
		results{itemOperator, itemVersion},
	},
	{true, ">=1.2.3",
		results{itemOperator, itemVersion},
	},
	// Sets
	{true, "5.3.5 4.3.5",
		results{itemVersion, itemSet, itemVersion},
	},
	//Ranges
	{true, "5.3.5||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5 ||4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{true, "5.3.5|| 4.3.5",
		results{itemVersion, itemRange, itemVersion},
	},
	{false, "5.3.5||  4.3.5",
		results{itemVersion, itemRange},
	},
	// Tilde and Caret Ranges
	{false, "~ 1.2.3",
		results{itemAdvanced},
	},
	{true, "~1.2.3",
		results{itemAdvanced, itemVersion},
	},
	{true, "^4.5.2-alpha.1",
		results{itemAdvanced, itemVersion},
	},
	{false, ">= 1.2.3",
		results{},
	},
	// Hyphen Range
	{false, "1.2.3 -3.2.5",
		results{itemVersion, itemAdvanced},
	},
	// X-Ranges
	{true, "*",
		results{itemAdvanced},
	},
	{false, "**",
		results{},
	},
	{true, "1.0",
		results{itemAdvanced},
	},
	{true, "1.x",
		results{itemAdvanced},
	},
	{true, "*.x",
		results{itemAdvanced},
	},
	{false, "1.x+98uihuhyg",
		results{},
	},
	{true, "1.*.2",
		results{itemAdvanced},
	},
	{true, "1.*.x || 1.x.4",
		results{itemAdvanced, itemRange, itemAdvanced},
	},
	{true, "1.*.2-beta",
		results{itemAdvanced},
	},
	{false, "1.*.2-",
		results{},
	},
	{true, "*.1.2",
		results{itemAdvanced},
	},
	{false, "1x.2.*",
		results{},
	},
	{false, "1.x2.*",
		results{},
	},
	{false, "1...1",
		results{},
	},
	{false, "1...",
		results{},
	},
	{false, "1.x.",
		results{},
	},
	{false, "1.x..",
		results{},
	},

	// Assorted syntax errors
	{false, "1.2.3-",
		results{},
	},
	{false, "1.2.3 >=",
		results{itemVersion, itemSet},
	},
	{false, "5.3.5 |1| 4.3.5",
		results{itemVersion},
	},
	{false, "1.2.3.1",
		results{},
	},
	{false, "5. 4.4",
		results{},
	},
	{false, "<1<1",
		results{itemOperator},
	},
	{false, "<1||",
		results{itemOperator, itemAdvanced, itemRange},
	},
	{false, "M",
		results{},
	},
}

func init() {
	// Appends appropriate end token based on expected result.
	for _, c := range constraints {
		if c.expected {
			c.result = append(c.result, itemEOF)
		} else {
			c.result = append(c.result, itemError)
		}
	}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()

func TestLexer(t *testing.T) {
	for _, c := range constraints {
		_, ch := lex(c.value)
		result := true
		x := 0
		for i := range ch {

			result = (i.typ != itemError)
			if len(c.result) > x {
				if i.typ != c.result[x] {
					t.Logf("lex(%v) => %v(%v), want %v \n", cyan(c.value), items[i.typ], yellow(i), items[c.result[x]])
				}
			} else {
				t.Logf("lex(%v) => %v(%v), want <nil>\n", cyan(c.value), items[i.typ], yellow(i))
			}
			x++
		}
		if result != c.expected {
			t.Logf("lex(%v) => %t, want %t \n", cyan(c.value), result, c.expected)
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerComplex(b *testing.B) {
	const VERSION = "1.0.0 || >=2.5.0 || 5.0.0 - 7.2.3 || ~4.3.1 ^2.1.1"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}

// Poor implementation, just for initial testing.
func BenchmarkLexerSimple(b *testing.B) {
	const VERSION = "1.0.0"

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, ch := lex(VERSION)
		for {
			_, ok := <-ch
			if ok == false {
				//fmt.Printf("%v: '%v' \n", items[s.typ], s)
				//} else {
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>package openx

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"path"
	"strconv"
	"strings"

	"github.com/mrjones/oauth"
	log "github.com/sirupsen/logrus"
)

// oauth global consumer (private)
var consumer *oauth.Consumer

const (
	version          = "0.0.1"
	requestTokenURL  = "https://sso.openx.com/api/index/initiate"
	accessTokenURL   = "https://sso.openx.com/api/index/token"
	authorizationURL = "https://sso.openx.com/login/process"
	apiPath          = "/ox/4.0/"
	callBack         = "oob"
)

// Client holds all the user information and all data is private
// At the moment i'm only supporting the APIPath2 and 4.0
type Client struct {
	domain          string
	realm           string
	scheme          string
	consumerKey     string
	consumerSecrect string
	email           string
	password        string
	apiPath         string
	timeOut         int
	session         *http.Client
}

// Get is simailiar to the normal Go *http.Client.Get,
// except string parameters can be passed in the url or the as a map[string]interface{}
func (c *Client) Get(url string, urlParms map[string]interface{}) (res *http.Response, err error) {
	url = c.resolveURL(url)
	if urlParms != nil {
		p := "?"
		for key, value := range urlParms {
			var v string
			switch value.(type) {
			case string:
				v = value.(string)
			case int:
				v = strconv.Itoa(value.(int))
			case float64:
				v = strconv.FormatFloat(value.(float64), 'f', -1, 64)
			case bool:
				v = strconv.FormatBool(value.(bool))
			default:
				log.Fatalln("The value entered %v must be of type string, int, float64, or bool")
			}
			p += key + "=" + v + "&"
		}
		url += p[:len(p)-1]
	}
	res, err = c.session.Get(url)
	return
}

// Delete creates a delete request
func (c *Client) Delete(url string, data io.Reader) (res *http.Response, err error) {
	request, err := http.NewRequest("DELETE", c.resolveURL(url), data)
	if err != nil {
		log.Fatalf("Could not create DELETE requests: %v", err)
	}
	res, err = c.session.Do(request)
	return
}

// Options is a wrapper for a GET request that has the /options endpoint already passed in
func (c *Client) Options(url string) (res *http.Response, err error) {
	if !strings.Contains(url, "/options") {
		url = path.Join("/options", url)
	}
	res, err = c.session.Get(c.resolveURL(url))
	return
}

// Put creates a put request
func (c *Client) Put(url string, data io.Reader) (res *http.Response, err error) {
	request, err := http.NewRequest("PUT", c.resolveURL(url), data)
	if err != nil {
		log.Fatalf("Could not create PUT requests: %v", err)
	}
	res, err = c.session.Do(request)
	return
}

// Post is a wrapper for the basic Go *http.Client.Post, however content type is automatically set to application/json
func (c *Client) Post(url string, data io.Reader) (res *http.Response, err error) {
	res, err = c.session.Post(c.resolveURL(url), "application/json", data)
	return
}

// PostForm is a wrapper for the basic Go *http.Client.PostForm
func (c *Client) PostForm(url string, data url.Values) (res *http.Response, err error) {
	res, err = c.session.PostForm(c.resolveURL(url), data)
	return
}

// LogOff sets the created session to an empty http.Client
func (c *Client) LogOff() (res *http.Response, err error) {
	// set the session to an empty struct to clear auth information
	c.session = &http.Client{}
	return
}

func (c *Client) resolveURL(endpoint string) (u string) {
	rawURL, err := url.Parse(endpoint)
	if err != nil {
		log.Fatalln("Could not parse endpoint: %v", err)
	}
	if rawURL.Scheme == "" {
		u = fmt.Sprintf("%s://", c.scheme) + path.Join(c.domain, c.apiPath, rawURL.Path)
	}
	return
}

func (c *Client) getAccessToken(debug bool) *oauth.AccessToken {
	requestToken, requestURL, err := consumer.GetRequestTokenAndUrl(callBack)
	if err != nil {
		log.Fatalf("Requests token could not be generated %v", err)
	}
	if debug {
		log.Info("Requests Token generated")
	}

	// auth into openx
	request := http.Client{}
	urlData := url.Values{}
	urlData.Set("email", c.email)
	urlData.Set("password", c.password)
	urlData.Set("oauth_token", requestToken.Token)
	resp, err := request.PostForm(requestURL, urlData)
	if err != nil {
		log.Fatalf("Could not get authorization token: %v", err)
	}
	if debug {
		log.Info("Getting auth token")
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("Could not get authorization status returned: %d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Could not read body: %v", err)
	}
	// parse the response, should contain oauth_verifier
	raw, err := url.Parse(string(body))
	if err != nil {
		log.Fatalf("Could not parse url: %v", err)
	}
	authInfo := raw.Query()
	var oauthVerifier string
	if val, ok := authInfo["oauth_verifier"]; !ok {
		log.Fatalln("oauth_verifier key not in response", err)
	} else {
		oauthVerifier = val[0]
	}
	// use oauth_verifier to get access_token
	accessToken, err := consumer.AuthorizeToken(requestToken, oauthVerifier)
	if err != nil {
		log.Fatalf("Access token could not be generated %v", err)
	}
	if debug {
		log.Info("Access Token generated")
	}
	return accessToken
}

// NewClient creates the basic Openx3 *Client via oauth1
func NewClient(domain, realm, consumerKey, consumerSecrect, email, password string, debug bool) (*Client, error) {
	if strings.TrimSpace(domain) == "" {
		return &Client{}, fmt.Errorf("Domain cannot be emtpy")
	}
	if strings.TrimSpace(realm) == "" {
		return &Client{}, fmt.Errorf("Realm cannot be emtpy")
	}
	if strings.TrimSpace(consumerKey) == "" {
		return &Client{}, fmt.Errorf("Consumer key cannot be emtpy")
	}
	if strings.TrimSpace(consumerSecrect) == "" {
		return &Client{}, fmt.Errorf("Consumer secrect cannot be emtpy")
	}
	if strings.TrimSpace(email) == "" {
		return &Client{}, fmt.Errorf("email cannot be emtpy")
	}
	if strings.TrimSpace(password) == "" {
		return &Client{}, fmt.Errorf("password cannot be emtpy")
	}

	// create base client default to http
	c := &Client{
		domain:          domain,
		realm:           realm,
		consumerKey:     consumerKey,
		consumerSecrect: consumerSecrect,
		apiPath:         apiPath,
		email:           email,
		password:        password,
		scheme:          "http",
	}

	// create oauth consumer
	consumer = oauth.NewConsumer(c.consumerKey, c.consumerSecrect, oauth.ServiceProvider{
		RequestTokenUrl:   requestTokenURL,
		AuthorizeTokenUrl: authorizationURL,
		AccessTokenUrl:    accessTokenURL,
		HttpMethod:        "POST",
	})
	consumer.Debug(debug)

	accessToken := c.getAccessToken(debug)

	// create a cookie jar to add the access token to
	cj, err := cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Cookiejar could not be created %v", err)
	}

	// clean up entered domain just incase user passes in a domain in a way I'm not ready for
	r := strings.NewReplacer(
		"www.", "",
		"http://", "",
		"https://", "",
		"//", "",
		"/", "",
	)

	c.domain = r.Replace(c.domain)
	// format the domain
	base, err := url.Parse(fmt.Sprintf("%s://www.%s", c.scheme, c.domain))
	if err != nil {
		log.Fatalf("Domain could not be parsed to type url %v", err)
	}

	// create auth cookie
	var cookies []*http.Cookie
	cookie := &http.Cookie{
		Name:   "openx3_access_token",
		Value:  accessToken.Token,
		Path:   "/",
		Domain: c.domain,
		Secure: false,
		// HttpOnly: false,
	}
	cookies = append(cookies, cookie)
	cj.SetCookies(base, cookies)

	// create authenticated session
	session, err := consumer.MakeHttpClient(accessToken)
	if err != nil {
		log.Fatalf("Could not create client %v", err)
	}
	session.Jar = cj
	c.session = session
	return c, nil
}
<commit_msg>hidding client<commit_after>package openx

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"path"
	"strconv"
	"strings"

	"github.com/mrjones/oauth"
	log "github.com/sirupsen/logrus"
)

// oauth global consumer (private)
var consumer *oauth.Consumer

const (
	version          = "0.0.1"
	requestTokenURL  = "https://sso.openx.com/api/index/initiate"
	accessTokenURL   = "https://sso.openx.com/api/index/token"
	authorizationURL = "https://sso.openx.com/login/process"
	apiPath          = "/ox/4.0/"
	callBack         = "oob"
)

type client struct {
	domain          string
	realm           string
	scheme          string
	consumerKey     string
	consumerSecrect string
	email           string
	password        string
	apiPath         string
	timeOut         int
	session         *http.Client
}

// Get is simailiar to the normal Go *http.client.Get,
// except string parameters can be passed in the url or the as a map[string]interface{}
func (c *client) Get(url string, urlParms map[string]interface{}) (res *http.Response, err error) {
	url = c.resolveURL(url)
	if urlParms != nil {
		p := "?"
		for key, value := range urlParms {
			var v string
			switch value.(type) {
			case string:
				v = value.(string)
			case int:
				v = strconv.Itoa(value.(int))
			case float64:
				v = strconv.FormatFloat(value.(float64), 'f', -1, 64)
			case bool:
				v = strconv.FormatBool(value.(bool))
			default:
				log.Fatalln("The value entered %v must be of type string, int, float64, or bool")
			}
			p += key + "=" + v + "&"
		}
		url += p[:len(p)-1]
	}
	res, err = c.session.Get(url)
	return
}

// Delete creates a delete request
func (c *client) Delete(url string, data io.Reader) (res *http.Response, err error) {
	request, err := http.NewRequest("DELETE", c.resolveURL(url), data)
	if err != nil {
		log.Fatalf("Could not create DELETE requests: %v", err)
	}
	res, err = c.session.Do(request)
	return
}

// Options is a wrapper for a GET request that has the /options endpoint already passed in
func (c *client) Options(url string) (res *http.Response, err error) {
	if !strings.Contains(url, "/options") {
		url = path.Join("/options", url)
	}
	res, err = c.session.Get(c.resolveURL(url))
	return
}

// Put creates a put request
func (c *client) Put(url string, data io.Reader) (res *http.Response, err error) {
	request, err := http.NewRequest("PUT", c.resolveURL(url), data)
	if err != nil {
		log.Fatalf("Could not create PUT requests: %v", err)
	}
	res, err = c.session.Do(request)
	return
}

// Post is a wrapper for the basic Go *http.client.Post, however content type is automatically set to application/json
func (c *client) Post(url string, data io.Reader) (res *http.Response, err error) {
	res, err = c.session.Post(c.resolveURL(url), "application/json", data)
	return
}

// PostForm is a wrapper for the basic Go *http.client.PostForm
func (c *client) PostForm(url string, data url.Values) (res *http.Response, err error) {
	res, err = c.session.PostForm(c.resolveURL(url), data)
	return
}

// LogOff sets the created session to an empty http.client
func (c *client) LogOff() (res *http.Response, err error) {
	// set the session to an empty struct to clear auth information
	c.session = &http.Client{}
	return
}

func (c *client) resolveURL(endpoint string) (u string) {
	rawURL, err := url.Parse(endpoint)
	if err != nil {
		log.Fatalln("Could not parse endpoint: %v", err)
	}
	if rawURL.Scheme == "" {
		u = fmt.Sprintf("%s://", c.scheme) + path.Join(c.domain, c.apiPath, rawURL.Path)
	}
	return
}

func (c *client) getAccessToken(debug bool) *oauth.AccessToken {
	requestToken, requestURL, err := consumer.GetRequestTokenAndUrl(callBack)
	if err != nil {
		log.Fatalf("Requests token could not be generated %v", err)
	}
	if debug {
		log.Info("Requests Token generated")
	}

	// auth into openx
	request := http.Client{}
	urlData := url.Values{}
	urlData.Set("email", c.email)
	urlData.Set("password", c.password)
	urlData.Set("oauth_token", requestToken.Token)
	resp, err := request.PostForm(requestURL, urlData)
	if err != nil {
		log.Fatalf("Could not get authorization token: %v", err)
	}
	if debug {
		log.Info("Getting auth token")
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("Could not get authorization status returned: %d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Could not read body: %v", err)
	}
	// parse the response, should contain oauth_verifier
	raw, err := url.Parse(string(body))
	if err != nil {
		log.Fatalf("Could not parse url: %v", err)
	}
	authInfo := raw.Query()
	var oauthVerifier string
	if val, ok := authInfo["oauth_verifier"]; !ok {
		log.Fatalln("oauth_verifier key not in response", err)
	} else {
		oauthVerifier = val[0]
	}
	// use oauth_verifier to get access_token
	accessToken, err := consumer.AuthorizeToken(requestToken, oauthVerifier)
	if err != nil {
		log.Fatalf("Access token could not be generated %v", err)
	}
	if debug {
		log.Info("Access Token generated")
	}
	return accessToken
}

// Newclient creates the basic Openx3 *client via oauth1
func Newclient(domain, realm, consumerKey, consumerSecrect, email, password string, debug bool) (*client, error) {
	if strings.TrimSpace(domain) == "" {
		return &client{}, fmt.Errorf("Domain cannot be emtpy")
	}
	if strings.TrimSpace(realm) == "" {
		return &client{}, fmt.Errorf("Realm cannot be emtpy")
	}
	if strings.TrimSpace(consumerKey) == "" {
		return &client{}, fmt.Errorf("Consumer key cannot be emtpy")
	}
	if strings.TrimSpace(consumerSecrect) == "" {
		return &client{}, fmt.Errorf("Consumer secrect cannot be emtpy")
	}
	if strings.TrimSpace(email) == "" {
		return &client{}, fmt.Errorf("email cannot be emtpy")
	}
	if strings.TrimSpace(password) == "" {
		return &client{}, fmt.Errorf("password cannot be emtpy")
	}

	// create base client default to http
	c := &client{
		domain:          domain,
		realm:           realm,
		consumerKey:     consumerKey,
		consumerSecrect: consumerSecrect,
		apiPath:         apiPath,
		email:           email,
		password:        password,
		scheme:          "http",
	}

	// create oauth consumer
	consumer = oauth.NewConsumer(c.consumerKey, c.consumerSecrect, oauth.ServiceProvider{
		RequestTokenUrl:   requestTokenURL,
		AuthorizeTokenUrl: authorizationURL,
		AccessTokenUrl:    accessTokenURL,
		HttpMethod:        "POST",
	})
	consumer.Debug(debug)

	accessToken := c.getAccessToken(debug)

	// create a cookie jar to add the access token to
	cj, err := cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Cookiejar could not be created %v", err)
	}

	// clean up entered domain just incase user passes in a domain in a way I'm not ready for
	r := strings.NewReplacer(
		"www.", "",
		"http://", "",
		"https://", "",
		"//", "",
		"/", "",
	)

	c.domain = r.Replace(c.domain)
	// format the domain
	base, err := url.Parse(fmt.Sprintf("%s://www.%s", c.scheme, c.domain))
	if err != nil {
		log.Fatalf("Domain could not be parsed to type url %v", err)
	}

	// create auth cookie
	var cookies []*http.Cookie
	cookie := &http.Cookie{
		Name:   "openx3_access_token",
		Value:  accessToken.Token,
		Path:   "/",
		Domain: c.domain,
		Secure: false,
		// HttpOnly: false,
	}
	cookies = append(cookies, cookie)
	cj.SetCookies(base, cookies)

	// create authenticated session
	session, err := consumer.MakeHttpClient(accessToken)
	if err != nil {
		log.Fatalf("Could not create client %v", err)
	}
	session.Jar = cj
	c.session = session
	return c, nil
}
<|endoftext|>


<commit_before>package main

import (
    "strings"
    "log"
    "regexp"
)

var(
    PATTERN_ALL = []string { PATTERN_ANY, PATTERN_CONTENT, PATTERN_LINK, PATTERN_TITLE }
    PATTERN_ALL_REGEX = regexp.MustCompile(strings.Join(PATTERN_ALL, "|"))

    HTML_WHITESPACE_REGEX = regexp.MustCompile(`>\s+`)
    HTML_WHITESPACE_REGEX2 = regexp.MustCompile(`\s+<`)
)

func MinifyHtml(htmlData []byte) []byte {
    htmlData = HTML_WHITESPACE_REGEX.ReplaceAll(htmlData, []byte(">"))
    htmlData = HTML_WHITESPACE_REGEX2.ReplaceAll(htmlData, []byte("<"))
    return htmlData
}

func FilterHtmlWithoutPattern(htmlData []byte, pattern string) bool {
    html := string(MinifyHtml(htmlData))
    for _, str := range PATTERN_ALL_REGEX.Split(pattern, -1) {
        if "" == str {
            continue
        }
        if !strings.Contains(html, str) {
            log.Printf("target html does not contain %s", str)
            return false
        }
    }

    return true
}

func ParseIndexHtml(tar Target) (entries []FeedEntry, ok bool) {
    htmlData, err := Crawl(tar.URL)
    if nil != err {
        log.Printf("failed to download index web page %s", tar.URL)
        return
    }

    htmlData = MinifyHtml(htmlData)

    if !FilterHtmlWithoutPattern(htmlData, tar.IndexPattern) {
        log.Printf("no match for target %s", tar.URL)
        return
    }

    indexRegStr := PatternToRegex(tar.IndexPattern)
    indexReg, err := regexp.Compile(indexRegStr)
    if nil != err {
        log.Printf("failed to index compile regular expression %s", indexRegStr)
        return
    }

    matches := indexReg.FindAllSubmatch(htmlData, -1)
    if nil == matches {
        log.Printf("failed to match index html %s, pattern %s did not match", tar.URL, indexRegStr)
        return
    }

    entries = make([]FeedEntry, len(matches))
    for matchInd, match := range matches {
        entry := &entries[matchInd]
        for patInd, patName := range indexReg.SubexpNames() {
            // skip whole match
            if 0 == patInd {
                continue
            }
            // no anonymous group
            if "" == patName {
                log.Printf("encountered anonymous group in pattern %s", indexRegStr)
                return
            } else if TITLE_NAME == patName {
                entry.Title = string(match[patInd])
            } else if LINK_NAME == patName {
                entry.Link = string(match[patInd])
            }
        }

        if "" == entry.Title || "" == entry.Link {
            log.Printf("empty title <%s> or empty link <%s>", entry.Title, entry.Link)
            return
        }
    }

    ok = true
    return
}

func ParseContentHtml(tar Target, entries []FeedEntry) (ok bool) {
    contentRegStr := PatternToRegex(tar.ContentPattern)

    for entryInd, _ := range entries {
        entry := &entries[entryInd]

        if "" == entry.Link {
            log.Printf("url of feed entry %s is empty", entry.Title)
            // just skip emtpy feed entry
            continue
        }

        htmlData, err := Crawl(entry.Link)
        if nil != err {
            log.Printf("failed to download web page %s", entry.Link)
            return
        }

        htmlData = MinifyHtml(htmlData)
        if !FilterHtmlWithoutPattern(htmlData, tar.ContentPattern) {
            log.Printf("no match for target %s", tar.URL)
            return
        }

        contentReg, err := regexp.Compile(contentRegStr)
        if nil != err {
            log.Printf("failed to compile content regular expression %s", contentRegStr)
            return
        }

        match := contentReg.FindSubmatch(htmlData)
        if nil == match {
            log.Printf("failed to match content html %s, pattern %s match failed", entry.Link, contentRegStr)
            return
        }

        for i, patName := range contentReg.SubexpNames() {
            // skip whole match
            if 0 == i {
                continue
            }
            // no anonymous group
            if "" == patName {
                log.Printf("encountered anonymous group in pattern %s", contentRegStr)
                return
            } else if CONTENT_NAME == patName {
            if CONTENT_NAME == patName {
                entry.Content = match[i]
            }
        }

        if 0 == len(entry.Content) {
            // just print a warning message if content is empty
            log.Printf("empty content for feed entry %s", entry.Title)
        }
    }

    ok = true
    return
}

<commit_msg>fix syntax error<commit_after>package main

import (
    "strings"
    "log"
    "regexp"
)

var(
    PATTERN_ALL = []string { PATTERN_ANY, PATTERN_CONTENT, PATTERN_LINK, PATTERN_TITLE }
    PATTERN_ALL_REGEX = regexp.MustCompile(strings.Join(PATTERN_ALL, "|"))

    HTML_WHITESPACE_REGEX = regexp.MustCompile(`>\s+`)
    HTML_WHITESPACE_REGEX2 = regexp.MustCompile(`\s+<`)
)

func MinifyHtml(htmlData []byte) []byte {
    htmlData = HTML_WHITESPACE_REGEX.ReplaceAll(htmlData, []byte(">"))
    htmlData = HTML_WHITESPACE_REGEX2.ReplaceAll(htmlData, []byte("<"))
    return htmlData
}

func FilterHtmlWithoutPattern(htmlData []byte, pattern string) bool {
    html := string(MinifyHtml(htmlData))
    for _, str := range PATTERN_ALL_REGEX.Split(pattern, -1) {
        if "" == str {
            continue
        }
        if !strings.Contains(html, str) {
            log.Printf("target html does not contain %s", str)
            return false
        }
    }

    return true
}

func ParseIndexHtml(tar Target) (entries []FeedEntry, ok bool) {
    htmlData, err := Crawl(tar.URL)
    if nil != err {
        log.Printf("failed to download index web page %s", tar.URL)
        return
    }

    htmlData = MinifyHtml(htmlData)

    if !FilterHtmlWithoutPattern(htmlData, tar.IndexPattern) {
        log.Printf("no match for target %s", tar.URL)
        return
    }

    indexRegStr := PatternToRegex(tar.IndexPattern)
    indexReg, err := regexp.Compile(indexRegStr)
    if nil != err {
        log.Printf("failed to index compile regular expression %s", indexRegStr)
        return
    }

    matches := indexReg.FindAllSubmatch(htmlData, -1)
    if nil == matches {
        log.Printf("failed to match index html %s, pattern %s did not match", tar.URL, indexRegStr)
        return
    }

    entries = make([]FeedEntry, len(matches))
    for matchInd, match := range matches {
        entry := &entries[matchInd]
        for patInd, patName := range indexReg.SubexpNames() {
            // skip whole match
            if 0 == patInd {
                continue
            }
            // no anonymous group
            if "" == patName {
                log.Printf("encountered anonymous group in pattern %s", indexRegStr)
                return
            } else if TITLE_NAME == patName {
                entry.Title = string(match[patInd])
            } else if LINK_NAME == patName {
                entry.Link = string(match[patInd])
            }
        }

        if "" == entry.Title || "" == entry.Link {
            log.Printf("empty title <%s> or empty link <%s>", entry.Title, entry.Link)
            return
        }
    }

    ok = true
    return
}

func ParseContentHtml(tar Target, entries []FeedEntry) (ok bool) {
    contentRegStr := PatternToRegex(tar.ContentPattern)

    for entryInd, _ := range entries {
        entry := &entries[entryInd]

        if "" == entry.Link {
            log.Printf("url of feed entry %s is empty", entry.Title)
            // just skip emtpy feed entry
            continue
        }

        htmlData, err := Crawl(entry.Link)
        if nil != err {
            log.Printf("failed to download web page %s", entry.Link)
            return
        }

        htmlData = MinifyHtml(htmlData)
        if !FilterHtmlWithoutPattern(htmlData, tar.ContentPattern) {
            log.Printf("no match for target %s", tar.URL)
            return
        }

        contentReg, err := regexp.Compile(contentRegStr)
        if nil != err {
            log.Printf("failed to compile content regular expression %s", contentRegStr)
            return
        }

        match := contentReg.FindSubmatch(htmlData)
        if nil == match {
            log.Printf("failed to match content html %s, pattern %s match failed", entry.Link, contentRegStr)
            return
        }

        for i, patName := range contentReg.SubexpNames() {
            // skip whole match
            if 0 == i {
                continue
            }
            // no anonymous group
            if "" == patName {
                log.Printf("encountered anonymous group in pattern %s", contentRegStr)
                return
            } else if CONTENT_NAME == patName {
                entry.Content = match[i]
            }
        }

        if 0 == len(entry.Content) {
            // just print a warning message if content is empty
            log.Printf("empty content for feed entry %s", entry.Title)
        }
    }

    ok = true
    return
}

<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"net/http"

	"github.com/geodan/gost/sensorthings/models"
	"github.com/gorilla/mux"
)

// NewRouter creates a new mux.Router and sets up all endpoints defind in the sensothings api
func NewRouter(api *models.API) *mux.Router {
	// Note: tried julienschmidt/httprouter instead of gorilla/mux but had some
	// problems with interfering endpoints cause of the wildcard used for the (id) in requests
	a := *api
	endpoints := *a.GetEndpoints()
	router := mux.NewRouter().StrictSlash(true)
	router.Path("/").Handler(http.FileServer(http.Dir("./client/")))

	for _, endpoint := range endpoints {
		ep := endpoint
		for _, op := range ep.GetOperations() {
			operation := op
			method := fmt.Sprintf("%s", operation.OperationType)
			if operation.Handler == nil {
				continue
			}

			router.Methods(method).
				Path(operation.Path).
				HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					operation.Handler(w, r, &ep, api)
				})
		}
	}

	return router
}
<commit_msg>fixed servcing client url changed to server:port/dashboard<commit_after>package http

import (
	"fmt"
	"net/http"

	"github.com/geodan/gost/sensorthings/models"
	"github.com/gorilla/mux"
)

// NewRouter creates a new mux.Router and sets up all endpoints defind in the sensothings api
func NewRouter(api *models.API) *mux.Router {
	// Note: tried julienschmidt/httprouter instead of gorilla/mux but had some
	// problems with interfering endpoints cause of the wildcard used for the (id) in requests
	a := *api
	endpoints := *a.GetEndpoints()
	router := mux.NewRouter().StrictSlash(true)
	router.PathPrefix("/dashboard").Handler(http.StripPrefix("/dashboard", http.FileServer(http.Dir("./client/"))))

	for _, endpoint := range endpoints {
		ep := endpoint
		for _, op := range ep.GetOperations() {
			operation := op
			method := fmt.Sprintf("%s", operation.OperationType)
			if operation.Handler == nil {
				continue
			}

			router.Methods(method).
				Path(operation.Path).
				HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					operation.Handler(w, r, &ep, api)
				})
		}
	}

	return router
}
<|endoftext|>


<commit_before>// The instance package contains a packer.Builder implementation that builds
// AMIs for Amazon EC2 backed by instance storage, as opposed to EBS storage.
package instance

import (
	"errors"
	"fmt"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	awscommon "github.com/mitchellh/packer/builder/amazon/common"
	"github.com/mitchellh/packer/builder/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"os"
	"strings"
)

// The unique ID for this builder
const BuilderId = "mitchellh.amazon.instance"

// Config is the configuration that is chained through the steps and
// settable from the template.
type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	awscommon.AccessConfig `mapstructure:",squash"`
	awscommon.RunConfig    `mapstructure:",squash"`

	AccountId        string `mapstructure:"account_id"`
	BundleVolCommand string `mapstructure:"bundle_vol_command"`
	X509CertPath     string `mapstructure:"x509_cert_path"`
	X509KeyPath      string `mapstructure:"x509_key_path"`
	X509UploadPath   string `mapstructure:"x509_upload_path"`
}

type Builder struct {
	config Config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) error {
	md, err := common.DecodeConfig(&b.config, raws...)
	if err != nil {
		return err
	}

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)
	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare()...)

	if b.config.AccountId == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("account_id is required"))
	} else {
		b.config.AccountId = strings.Replace(b.config.AccountId, "-", "", -1)
	}

	if b.config.BundleVolCommand == "" {
		b.config.BundleVolCommand = "ec2-bundle-vol " +
			"-k {{.KeyPath}} " +
			"-u {{.AccountId}} " +
			"-c {{.CertPath}} " +
			"-r {{.Architecture}} " +
			"-e {{.PrivatePath}}"
	}

	if b.config.X509CertPath == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("x509_cert_path is required"))
	} else if _, err := os.Stat(b.config.X509CertPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("x509_cert_path points to bad file: %s", err))
	}

	if b.config.X509KeyPath == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("x509_key_path is required"))
	} else if _, err := os.Stat(b.config.X509KeyPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("x509_key_path points to bad file: %s", err))
	}

	if b.config.X509UploadPath == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("x509_upload_path is required"))
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	log.Printf("Config: %+v", b.config)
	return nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	region, ok := aws.Regions[b.config.Region]
	if !ok {
		panic("region not found")
	}

	auth, err := b.config.AccessConfig.Auth()
	if err != nil {
		return nil, err
	}

	ec2conn := ec2.New(auth, region)

	// Setup the state bag and initial state for the steps
	state := make(map[string]interface{})
	state["config"] = &b.config
	state["ec2"] = ec2conn
	state["hook"] = hook
	state["ui"] = ui

	// Build the steps
	steps := []multistep.Step{
		&awscommon.StepKeyPair{},
		&awscommon.StepSecurityGroup{
			SecurityGroupId: b.config.SecurityGroupId,
			SSHPort:         b.config.SSHPort,
			VpcId:           b.config.VpcId,
		},
		&awscommon.StepRunSourceInstance{
			ExpectedRootDevice: "instance-store",
			InstanceType:       b.config.InstanceType,
			SourceAMI:          b.config.SourceAmi,
			SubnetId:           b.config.SubnetId,
		},
		&common.StepConnectSSH{
			SSHAddress:     awscommon.SSHAddress(b.config.SSHPort),
			SSHConfig:      awscommon.SSHConfig(b.config.SSHUsername),
			SSHWaitTimeout: b.config.SSHTimeout(),
		},
		&common.StepProvision{},
		&StepUploadX509Cert{},
		&StepBundleVolume{},
	}

	// Run!
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

	b.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state["error"]; ok {
		return nil, rawErr.(error)
	}

	// If there are no AMIs, then just return
	if _, ok := state["amis"]; !ok {
		return nil, nil
	}

	return nil, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<commit_msg>builder/amazon/instance: sudo the ec2-bundle-vol by default<commit_after>// The instance package contains a packer.Builder implementation that builds
// AMIs for Amazon EC2 backed by instance storage, as opposed to EBS storage.
package instance

import (
	"errors"
	"fmt"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	awscommon "github.com/mitchellh/packer/builder/amazon/common"
	"github.com/mitchellh/packer/builder/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"os"
	"strings"
)

// The unique ID for this builder
const BuilderId = "mitchellh.amazon.instance"

// Config is the configuration that is chained through the steps and
// settable from the template.
type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	awscommon.AccessConfig `mapstructure:",squash"`
	awscommon.RunConfig    `mapstructure:",squash"`

	AccountId        string `mapstructure:"account_id"`
	BundleVolCommand string `mapstructure:"bundle_vol_command"`
	X509CertPath     string `mapstructure:"x509_cert_path"`
	X509KeyPath      string `mapstructure:"x509_key_path"`
	X509UploadPath   string `mapstructure:"x509_upload_path"`
}

type Builder struct {
	config Config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) error {
	md, err := common.DecodeConfig(&b.config, raws...)
	if err != nil {
		return err
	}

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)
	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare()...)

	if b.config.AccountId == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("account_id is required"))
	} else {
		b.config.AccountId = strings.Replace(b.config.AccountId, "-", "", -1)
	}

	if b.config.BundleVolCommand == "" {
		b.config.BundleVolCommand = "sudo -n ec2-bundle-vol " +
			"-k {{.KeyPath}} " +
			"-u {{.AccountId}} " +
			"-c {{.CertPath}} " +
			"-r {{.Architecture}} " +
			"-e {{.PrivatePath}}"
	}

	if b.config.X509CertPath == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("x509_cert_path is required"))
	} else if _, err := os.Stat(b.config.X509CertPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("x509_cert_path points to bad file: %s", err))
	}

	if b.config.X509KeyPath == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("x509_key_path is required"))
	} else if _, err := os.Stat(b.config.X509KeyPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("x509_key_path points to bad file: %s", err))
	}

	if b.config.X509UploadPath == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("x509_upload_path is required"))
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	log.Printf("Config: %+v", b.config)
	return nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	region, ok := aws.Regions[b.config.Region]
	if !ok {
		panic("region not found")
	}

	auth, err := b.config.AccessConfig.Auth()
	if err != nil {
		return nil, err
	}

	ec2conn := ec2.New(auth, region)

	// Setup the state bag and initial state for the steps
	state := make(map[string]interface{})
	state["config"] = &b.config
	state["ec2"] = ec2conn
	state["hook"] = hook
	state["ui"] = ui

	// Build the steps
	steps := []multistep.Step{
		&awscommon.StepKeyPair{},
		&awscommon.StepSecurityGroup{
			SecurityGroupId: b.config.SecurityGroupId,
			SSHPort:         b.config.SSHPort,
			VpcId:           b.config.VpcId,
		},
		&awscommon.StepRunSourceInstance{
			ExpectedRootDevice: "instance-store",
			InstanceType:       b.config.InstanceType,
			SourceAMI:          b.config.SourceAmi,
			SubnetId:           b.config.SubnetId,
		},
		&common.StepConnectSSH{
			SSHAddress:     awscommon.SSHAddress(b.config.SSHPort),
			SSHConfig:      awscommon.SSHConfig(b.config.SSHUsername),
			SSHWaitTimeout: b.config.SSHTimeout(),
		},
		&common.StepProvision{},
		&StepUploadX509Cert{},
		&StepBundleVolume{},
	}

	// Run!
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

	b.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state["error"]; ok {
		return nil, rawErr.(error)
	}

	// If there are no AMIs, then just return
	if _, ok := state["amis"]; !ok {
		return nil, nil
	}

	return nil, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<|endoftext|>


<commit_before>package raft_test

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"github.com/docker/swarmkit/api"
	raftutils "github.com/docker/swarmkit/manager/state/raft/testutils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestRaftSnapshot(t *testing.T) {
	t.Parallel()

	// Bring up a 3 node cluster
	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 9, LogEntriesForSlowFollowers: 0})
	defer raftutils.TeardownCluster(t, nodes)

	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8", "id9", "id10", "id11", "id12"}
	values := make([]*api.Node, len(nodeIDs))
	snapshotFilenames := make(map[uint64]string, 4)

	// Propose 3 values
	var err error
	for i, nodeID := range nodeIDs[:3] {
		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
		assert.NoError(t, err, "failed to propose value")
	}

	// None of the nodes should have snapshot files yet
	for _, node := range nodes {
		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
		assert.NoError(t, err)
		assert.Len(t, dirents, 0)
	}

	// Check all nodes have all the data.
	// This also acts as a synchronization point so that the next value we
	// propose will arrive as a separate message to the raft state machine,
	// and it is guaranteed to have the right cluster settings when
	// deciding whether to create a new snapshot.
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:3], values)

	// Propose a 4th value
	values[3], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
	assert.NoError(t, err, "failed to propose value")

	// All nodes should now have a snapshot file
	for nodeID, node := range nodes {
		assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
			if err != nil {
				return err
			}
			if len(dirents) != 1 {
				return fmt.Errorf("expected 1 snapshot, found %d", len(dirents))
			}
			snapshotFilenames[nodeID] = dirents[0].Name()
			return nil
		}))
	}

	// Add a node to the cluster
	raftutils.AddRaftNode(t, clockSource, nodes, tc)

	// It should get a copy of the snapshot
	assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
		dirents, err := ioutil.ReadDir(filepath.Join(nodes[4].StateDir, "snap"))
		if err != nil {
			return err
		}
		if len(dirents) != 1 {
			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(dirents))
		}
		snapshotFilenames[4] = dirents[0].Name()
		return nil
	}))

	// It should know about the other nodes
	stripMembers := func(memberList map[uint64]*api.RaftMember) map[uint64]*api.RaftMember {
		raftNodes := make(map[uint64]*api.RaftMember)
		for k, v := range memberList {
			raftNodes[k] = &api.RaftMember{
				RaftID: v.RaftID,
				Addr:   v.Addr,
			}
		}
		return raftNodes
	}
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[4].GetMemberlist()))

	// All nodes should have all the data
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:4], values)

	// Propose more values to provoke a second snapshot
	for i := 4; i != len(nodeIDs); i++ {
		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[i])
		assert.NoError(t, err, "failed to propose value")
	}

	// All nodes should have a snapshot under a *different* name
	for nodeID, node := range nodes {
		assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
			if err != nil {
				return err
			}
			if len(dirents) != 1 {
				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeID)
			}
			if dirents[0].Name() == snapshotFilenames[nodeID] {
				return fmt.Errorf("snapshot %s did not get replaced", snapshotFilenames[nodeID])
			}
			return nil
		}))
	}

	// All nodes should have all the data
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
}

func TestRaftSnapshotRestart(t *testing.T) {
	t.Parallel()

	// Bring up a 3 node cluster
	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 10, LogEntriesForSlowFollowers: 0})
	defer raftutils.TeardownCluster(t, nodes)

	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7"}
	values := make([]*api.Node, len(nodeIDs))

	// Propose 3 values
	var err error
	for i, nodeID := range nodeIDs[:3] {
		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
		assert.NoError(t, err, "failed to propose value")
	}

	// Take down node 3
	nodes[3].Server.Stop()
	nodes[3].Shutdown()

	// Propose a 4th value before the snapshot
	values[3], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
	assert.NoError(t, err, "failed to propose value")

	// Remaining nodes shouldn't have snapshot files yet
	for _, node := range []*raftutils.TestNode{nodes[1], nodes[2]} {
		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
		assert.NoError(t, err)
		assert.Len(t, dirents, 0)
	}

	// Add a node to the cluster before the snapshot. This is the event
	// that triggers the snapshot.
	nodes[4] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2], 4: nodes[4]})

	// Remaining nodes should now have a snapshot file
	for nodeIdx, node := range []*raftutils.TestNode{nodes[1], nodes[2]} {
		assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
			if err != nil {
				return err
			}
			if len(dirents) != 1 {
				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeIdx+1)
			}
			return nil
		}))
	}
	raftutils.CheckValuesOnNodes(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2]}, nodeIDs[:4], values[:4])

	// Propose a 5th value
	values[4], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[4])
	require.NoError(t, err)

	// Add another node to the cluster
	nodes[5] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2], 4: nodes[4], 5: nodes[5]})

	// New node should get a copy of the snapshot
	assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
		dirents, err := ioutil.ReadDir(filepath.Join(nodes[5].StateDir, "snap"))
		if err != nil {
			return err
		}
		if len(dirents) != 1 {
			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(dirents))
		}
		return nil
	}))

	dirents, err := ioutil.ReadDir(filepath.Join(nodes[5].StateDir, "snap"))
	assert.NoError(t, err)
	assert.Len(t, dirents, 1)
	raftutils.CheckValuesOnNodes(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2]}, nodeIDs[:5], values[:5])

	// It should know about the other nodes, including the one that was just added
	stripMembers := func(memberList map[uint64]*api.RaftMember) map[uint64]*api.RaftMember {
		raftNodes := make(map[uint64]*api.RaftMember)
		for k, v := range memberList {
			raftNodes[k] = &api.RaftMember{
				RaftID: v.RaftID,
				Addr:   v.Addr,
			}
		}
		return raftNodes
	}
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[4].GetMemberlist()))

	// Restart node 3
	nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
	raftutils.WaitForCluster(t, clockSource, nodes)

	// Node 3 should know about other nodes, including the new one
	assert.Len(t, nodes[3].GetMemberlist(), 5)
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[3].GetMemberlist()))

	// Propose yet another value, to make sure the rejoined node is still
	// receiving new logs
	values[5], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[5])
	require.NoError(t, err)

	// All nodes should have all the data
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:6], values[:6])

	// Restart node 3 again. It should load the snapshot.
	nodes[3].Server.Stop()
	nodes[3].Shutdown()
	nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
	raftutils.WaitForCluster(t, clockSource, nodes)

	assert.Len(t, nodes[3].GetMemberlist(), 5)
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[3].GetMemberlist()))
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:6], values[:6])

	// Propose again. Just to check consensus after this latest restart.
	values[6], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[6])
	require.NoError(t, err)
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
}
<commit_msg>raft: Make TestRaftSnapshotRestart more robust<commit_after>package raft_test

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"github.com/docker/swarmkit/api"
	raftutils "github.com/docker/swarmkit/manager/state/raft/testutils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestRaftSnapshot(t *testing.T) {
	t.Parallel()

	// Bring up a 3 node cluster
	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 9, LogEntriesForSlowFollowers: 0})
	defer raftutils.TeardownCluster(t, nodes)

	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8", "id9", "id10", "id11", "id12"}
	values := make([]*api.Node, len(nodeIDs))
	snapshotFilenames := make(map[uint64]string, 4)

	// Propose 3 values
	var err error
	for i, nodeID := range nodeIDs[:3] {
		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
		assert.NoError(t, err, "failed to propose value")
	}

	// None of the nodes should have snapshot files yet
	for _, node := range nodes {
		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
		assert.NoError(t, err)
		assert.Len(t, dirents, 0)
	}

	// Check all nodes have all the data.
	// This also acts as a synchronization point so that the next value we
	// propose will arrive as a separate message to the raft state machine,
	// and it is guaranteed to have the right cluster settings when
	// deciding whether to create a new snapshot.
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:3], values)

	// Propose a 4th value
	values[3], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
	assert.NoError(t, err, "failed to propose value")

	// All nodes should now have a snapshot file
	for nodeID, node := range nodes {
		assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
			if err != nil {
				return err
			}
			if len(dirents) != 1 {
				return fmt.Errorf("expected 1 snapshot, found %d", len(dirents))
			}
			snapshotFilenames[nodeID] = dirents[0].Name()
			return nil
		}))
	}

	// Add a node to the cluster
	raftutils.AddRaftNode(t, clockSource, nodes, tc)

	// It should get a copy of the snapshot
	assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
		dirents, err := ioutil.ReadDir(filepath.Join(nodes[4].StateDir, "snap"))
		if err != nil {
			return err
		}
		if len(dirents) != 1 {
			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(dirents))
		}
		snapshotFilenames[4] = dirents[0].Name()
		return nil
	}))

	// It should know about the other nodes
	stripMembers := func(memberList map[uint64]*api.RaftMember) map[uint64]*api.RaftMember {
		raftNodes := make(map[uint64]*api.RaftMember)
		for k, v := range memberList {
			raftNodes[k] = &api.RaftMember{
				RaftID: v.RaftID,
				Addr:   v.Addr,
			}
		}
		return raftNodes
	}
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[4].GetMemberlist()))

	// All nodes should have all the data
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:4], values)

	// Propose more values to provoke a second snapshot
	for i := 4; i != len(nodeIDs); i++ {
		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[i])
		assert.NoError(t, err, "failed to propose value")
	}

	// All nodes should have a snapshot under a *different* name
	for nodeID, node := range nodes {
		assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
			if err != nil {
				return err
			}
			if len(dirents) != 1 {
				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeID)
			}
			if dirents[0].Name() == snapshotFilenames[nodeID] {
				return fmt.Errorf("snapshot %s did not get replaced", snapshotFilenames[nodeID])
			}
			return nil
		}))
	}

	// All nodes should have all the data
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
}

func TestRaftSnapshotRestart(t *testing.T) {
	t.Parallel()

	// Bring up a 3 node cluster
	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 10, LogEntriesForSlowFollowers: 0})
	defer raftutils.TeardownCluster(t, nodes)

	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7"}
	values := make([]*api.Node, len(nodeIDs))

	// Propose 3 values
	var err error
	for i, nodeID := range nodeIDs[:3] {
		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
		assert.NoError(t, err, "failed to propose value")
	}

	// Take down node 3
	nodes[3].Server.Stop()
	nodes[3].Shutdown()

	// Propose a 4th value before the snapshot
	values[3], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
	assert.NoError(t, err, "failed to propose value")

	// Remaining nodes shouldn't have snapshot files yet
	for _, node := range []*raftutils.TestNode{nodes[1], nodes[2]} {
		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
		assert.NoError(t, err)
		assert.Len(t, dirents, 0)
	}

	// Add a node to the cluster before the snapshot. This is the event
	// that triggers the snapshot.
	nodes[4] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2], 4: nodes[4]})

	// Remaining nodes should now have a snapshot file
	for nodeIdx, node := range []*raftutils.TestNode{nodes[1], nodes[2]} {
		assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap"))
			if err != nil {
				return err
			}
			if len(dirents) != 1 {
				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeIdx+1)
			}
			return nil
		}))
	}
	raftutils.CheckValuesOnNodes(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2]}, nodeIDs[:4], values[:4])

	// Propose a 5th value
	values[4], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[4])
	require.NoError(t, err)

	// Add another node to the cluster
	nodes[5] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2], 4: nodes[4], 5: nodes[5]})

	// New node should get a copy of the snapshot
	assert.NoError(t, raftutils.PollFunc(clockSource, func() error {
		dirents, err := ioutil.ReadDir(filepath.Join(nodes[5].StateDir, "snap"))
		if err != nil {
			return err
		}
		if len(dirents) != 1 {
			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(dirents))
		}
		return nil
	}))

	dirents, err := ioutil.ReadDir(filepath.Join(nodes[5].StateDir, "snap"))
	assert.NoError(t, err)
	assert.Len(t, dirents, 1)
	raftutils.CheckValuesOnNodes(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2]}, nodeIDs[:5], values[:5])

	// It should know about the other nodes, including the one that was just added
	stripMembers := func(memberList map[uint64]*api.RaftMember) map[uint64]*api.RaftMember {
		raftNodes := make(map[uint64]*api.RaftMember)
		for k, v := range memberList {
			raftNodes[k] = &api.RaftMember{
				RaftID: v.RaftID,
				Addr:   v.Addr,
			}
		}
		return raftNodes
	}
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[4].GetMemberlist()))

	// Restart node 3
	nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
	raftutils.WaitForCluster(t, clockSource, nodes)

	// Node 3 should know about other nodes, including the new one
	assert.Len(t, nodes[3].GetMemberlist(), 5)
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[3].GetMemberlist()))

	// Propose yet another value, to make sure the rejoined node is still
	// receiving new logs
	values[5], err = raftutils.ProposeValue(t, raftutils.Leader(nodes), DefaultProposalTime, nodeIDs[5])
	require.NoError(t, err)

	// All nodes should have all the data
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:6], values[:6])

	// Restart node 3 again. It should load the snapshot.
	nodes[3].Server.Stop()
	nodes[3].Shutdown()
	nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
	raftutils.WaitForCluster(t, clockSource, nodes)

	assert.Len(t, nodes[3].GetMemberlist(), 5)
	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[3].GetMemberlist()))
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:6], values[:6])

	// Propose again. Just to check consensus after this latest restart.
	values[6], err = raftutils.ProposeValue(t, raftutils.Leader(nodes), DefaultProposalTime, nodeIDs[6])
	require.NoError(t, err)
	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
}
<|endoftext|>


<commit_before>/* 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 envs

import (
	"encoding/json"
	"net/http"
	"strings"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
)

// Update : responds to PUT /projects/:project:/envs/:env/ by updating an
// existing environment
func Update(au models.User, name string, body []byte) (int, []byte) {
	var err error
	var resp []byte
	var e models.Env
	var input models.Env
	var p models.Project
	var r models.Role
	var roles []models.Role
	var pRoles []models.Role

	computedRoles := make(map[string]models.Role, 0)

	if err = json.Unmarshal(body, &input); err != nil {
		h.L.Error(err.Error())
		return http.StatusBadRequest, []byte(err.Error())
	}

	// Get existing environment
	if err = e.FindByName(name); err != nil {
		return 404, models.NewJSONError(err.Error())
	}

	if err = p.FindByID(e.ProjectID); err != nil {
		if strings.Contains(err.Error(), "not found") {
			return 404, models.NewJSONError("Specified environment name does not exist")
		}
		h.L.Error(err.Error())
		return 500, models.NewJSONError("Internal error")
	}

	if err = r.FindAllByResource(e.Project, p.GetType(), &pRoles); err == nil {
		for _, v := range pRoles {
			computedRoles[v.UserID] = v
		}
	}
	if err = r.FindAllByResource(e.GetID(), e.GetType(), &roles); err == nil {
		for _, v := range roles {
			computedRoles[v.UserID] = v
		}
	}

	for _, v := range computedRoles {
		e.Members = append(e.Members, v)
	}

	if st, res := h.IsAuthorizedToResource(&au, h.UpdateEnv, input.GetType(), name); st != 200 {
		return st, res
	}

	e.Options = input.Options
	e.Schedules = input.Schedules
	e.Credentials = input.Credentials

	if err = e.Save(); err != nil {
		return 500, models.NewJSONError(err.Error())
	}

	for _, ir := range input.Members {
		// create role
		if ir.ID == 0 {
			if !au.IsAdmin() {
				if ok := au.IsOwner(ir.ResourceType, ir.ResourceID); !ok {
					return 403, models.NewJSONError("You're not authorized to perform this action")
				}
			}

			err = ir.Save()
			if err != nil {
				h.L.Error(err.Error())
				return http.StatusBadRequest, models.NewJSONError(err.Error())
			}

			continue
		}

		for _, er := range e.Members {
			// update role
			if ir.ID == er.ID && ir.Role != er.Role {
				if !au.IsAdmin() {
					if ok := au.IsOwner(ir.ResourceType, ir.ResourceID); !ok {
						return 403, models.NewJSONError("You're not authorized to perform this action")
					}
				}

				err = ir.Save()
				if err != nil {
					h.L.Error(err.Error())
					return http.StatusBadRequest, models.NewJSONError(err.Error())
				}
			}
		}
	}

	for _, er := range e.Members {
		var exists bool

		for _, ir := range input.Members {
			if ir.ID == er.ID {
				exists = true
			}
		}

		// delete roles
		if !exists {
			if strings.Contains(er.ResourceID, "/") {
				return http.StatusBadRequest, models.NewJSONError("project memberships must be removed on the project")
			}

			if !au.IsAdmin() {
				if ok := au.IsOwner(er.ResourceType, er.ResourceID); !ok {
					return 403, models.NewJSONError("You're not authorized to perform this action")
				}
			}

			err = er.Delete()
			if err != nil {
				h.L.Error(err.Error())
				return http.StatusBadRequest, models.NewJSONError(err.Error())
			}
		}
	}

	e.Members = input.Members

	resp, err = json.Marshal(e)
	if err != nil {
		h.L.Error(err.Error())
		return http.StatusBadRequest, models.NewJSONError(err.Error())
	}

	return http.StatusOK, resp
}
<commit_msg>restricting the modification of project roles<commit_after>/* 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 envs

import (
	"encoding/json"
	"net/http"
	"strings"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
)

// Update : responds to PUT /projects/:project:/envs/:env/ by updating an
// existing environment
func Update(au models.User, name string, body []byte) (int, []byte) {
	var err error
	var resp []byte
	var e models.Env
	var input models.Env
	var p models.Project
	var r models.Role
	var roles []models.Role
	var pRoles []models.Role

	computedRoles := make(map[string]models.Role, 0)

	if err = json.Unmarshal(body, &input); err != nil {
		h.L.Error(err.Error())
		return http.StatusBadRequest, []byte(err.Error())
	}

	// Get existing environment
	if err = e.FindByName(name); err != nil {
		return 404, models.NewJSONError(err.Error())
	}

	if err = p.FindByID(e.ProjectID); err != nil {
		if strings.Contains(err.Error(), "not found") {
			return 404, models.NewJSONError("Specified environment name does not exist")
		}
		h.L.Error(err.Error())
		return 500, models.NewJSONError("Internal error")
	}

	if err = r.FindAllByResource(e.Project, p.GetType(), &pRoles); err == nil {
		for _, v := range pRoles {
			computedRoles[v.UserID] = v
		}
	}
	if err = r.FindAllByResource(e.GetID(), e.GetType(), &roles); err == nil {
		for _, v := range roles {
			computedRoles[v.UserID] = v
		}
	}

	for _, v := range computedRoles {
		e.Members = append(e.Members, v)
	}

	if st, res := h.IsAuthorizedToResource(&au, h.UpdateEnv, input.GetType(), name); st != 200 {
		return st, res
	}

	e.Options = input.Options
	e.Schedules = input.Schedules
	e.Credentials = input.Credentials

	if err = e.Save(); err != nil {
		return 500, models.NewJSONError(err.Error())
	}

	for _, ir := range input.Members {
		// create role
		if ir.ID == 0 {
			if !au.IsAdmin() {
				if ok := au.IsOwner(ir.ResourceType, ir.ResourceID); !ok {
					return 403, models.NewJSONError("You're not authorized to perform this action")
				}
			}

			err = ir.Save()
			if err != nil {
				h.L.Error(err.Error())
				return http.StatusBadRequest, models.NewJSONError(err.Error())
			}

			continue
		}

		for _, er := range e.Members {
			// update role
			if ir.ID == er.ID && ir.Role != er.Role {
				if strings.Contains(er.ResourceID, "/") {
					return http.StatusBadRequest, models.NewJSONError("project memberships must be modified on the project")
				}

				if !au.IsAdmin() {
					if ok := au.IsOwner(ir.ResourceType, ir.ResourceID); !ok {
						return 403, models.NewJSONError("You're not authorized to perform this action")
					}
				}

				err = ir.Save()
				if err != nil {
					h.L.Error(err.Error())
					return http.StatusBadRequest, models.NewJSONError(err.Error())
				}
			}
		}
	}

	for _, er := range e.Members {
		var exists bool

		for _, ir := range input.Members {
			if ir.ID == er.ID {
				exists = true
			}
		}

		// delete roles
		if !exists {
			if strings.Contains(er.ResourceID, "/") {
				return http.StatusBadRequest, models.NewJSONError("project memberships must be removed on the project")
			}

			if !au.IsAdmin() {
				if ok := au.IsOwner(er.ResourceType, er.ResourceID); !ok {
					return 403, models.NewJSONError("You're not authorized to perform this action")
				}
			}

			err = er.Delete()
			if err != nil {
				h.L.Error(err.Error())
				return http.StatusBadRequest, models.NewJSONError(err.Error())
			}
		}
	}

	e.Members = input.Members

	resp, err = json.Marshal(e)
	if err != nil {
		h.L.Error(err.Error())
		return http.StatusBadRequest, models.NewJSONError(err.Error())
	}

	return http.StatusOK, resp
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Santiago Arias | Remy Jourde | Carlos Bernal
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package users

import (
	"encoding/json"
	"errors"
	"html/template"
	"io/ioutil"
	"net/http"

	"appengine"

	"github.com/santiaago/purple-wing/helpers"
	"github.com/santiaago/purple-wing/helpers/log"
	"github.com/santiaago/purple-wing/helpers/handlers"
	templateshlp "github.com/santiaago/purple-wing/helpers/templates"
	teamrelshlp "github.com/santiaago/purple-wing/helpers/teamrels"
	tournamentrelshlp "github.com/santiaago/purple-wing/helpers/tournamentrels"

	usermdl "github.com/santiaago/purple-wing/models/user"
	teammdl "github.com/santiaago/purple-wing/models/team"
	tournamentmdl "github.com/santiaago/purple-wing/models/tournament"
	teamrequestmdl "github.com/santiaago/purple-wing/models/teamrequest"

)

type Form struct {
	Username string
	Name string
	Email string
	ErrorUsername string
	ErrorName string
	ErrorEmail string
}

type UserData struct {
	Username string
	Name string
	Email string
}

// Show handler
func Show(w http.ResponseWriter, r *http.Request){
	c := appengine.NewContext(r)

	userId, err := handlers.PermalinkID(r, c, 3)
	if err != nil{
		http.Redirect(w,r, "/m/users/", http.StatusFound)
		return
	}

	funcs := template.FuncMap{
		"Profile": func() bool {return true},
	}

	t := template.Must(template.New("tmpl_user_show").
		Funcs(funcs).
		ParseFiles("templates/user/show.html",
		"templates/user/info.html",
		"templates/user/teams.html",
		"templates/user/tournaments.html",
		"templates/user/requests.html"))

	var user *usermdl.User
	user, err = usermdl.ById(c,userId)
	if err != nil{
		helpers.Error404(w)
		return
	}

	teams := usermdl.Teams(c, userId)
	tournaments := tournamentrelshlp.Tournaments(c, userId)
	teamRequests := teamrelshlp.TeamsRequests(c, teams)

	userData := struct {
		User *usermdl.User
		Teams []*teammdl.Team
		Tournaments []*tournamentmdl.Tournament
		TeamRequests []*teamrequestmdl.TeamRequest
	}{
		user,
		teams,
		tournaments,
		teamRequests,
	}

	templateshlp.RenderWithData(w, r, c, t, userData, funcs, "renderUserShow")
}

// json index user handler
func IndexJson(w http.ResponseWriter, r *http.Request, u *usermdl.User) error{
	c := appengine.NewContext(r)

	if r.Method == "GET"{
		users := usermdl.FindAll(c)
		return templateshlp.RenderJson(w, c, users)

	} else {
		return helpers.BadRequest{errors.New("not supported.")}
	}
}

// Json show user handler
func ShowJson(w http.ResponseWriter, r *http.Request, u *usermdl.User) error{
	c := appengine.NewContext(r)
	log.Infof(c, "User Show Json Handler")

	if r.Method == "GET"{

		userId, err := handlers.PermalinkID(r, c, 4)
		if err != nil{
			return helpers.BadRequest{err}
		}

		var user *usermdl.User
		user, err = usermdl.ById(c,userId)
		if err != nil{
			return helpers.BadRequest{err}
		}

		// set teams info in user json
		teams := usermdl.Teams(c, userId)
		teamsJson := make([]helpers.TeamJsonZip, len(teams))
		counterTeams := 0
		for _, team := range teams{
			teamsJson[counterTeams].Id = team.Id
			teamsJson[counterTeams].Name = team.Name
			counterTeams++
		}
		var userJson helpers.UserJson
		userJson.Id = user.Id
		userJson.Username = user.Username
		userJson.Name = user.Name
		userJson.Email = user.Email
		userJson.IsAdmin = user.IsAdmin
		userJson.Auth = user.Auth
		userJson.Created = user.Created
		userJson.Teams = teamsJson

		// set tournament info in user json
		tournaments := tournamentrelshlp.Tournaments(c, userId)
		tournamentsJson := make([]helpers.TournamentJsonZip, len(tournaments))
		counterTournaments := 0
		for _, tournament := range tournaments{
			tournamentsJson[counterTournaments].Id = tournament.Id
			tournamentsJson[counterTournaments].Name = tournament.Name
			counterTournaments++
		}
		userJson.Tournaments = tournamentsJson

		// set request info in user json
		teamRequests := teamrelshlp.TeamsRequests(c, teams)
		teamRequestsJson := make([]helpers.TeamRequestJsonZip, len(teamRequests))
		counterRequests := 0
		for _, request := range teamRequests{
			teamRequestsJson[counterRequests].Id = request.Id
			teamRequestsJson[counterRequests].UserId = request.UserId
			teamRequestsJson[counterRequests].TeamId = request.TeamId
			counterRequests++
		}
		userJson.TeamRequests = teamRequestsJson

		return templateshlp.RenderJson(w, c, userJson)
	} else {
		return helpers.BadRequest{errors.New("not supported.")}
	}
}

// json update user handler
func UpdateJson(w http.ResponseWriter, r *http.Request, u *usermdl.User) error{
	c := appengine.NewContext(r)

	if r.Method == "POST"{
		userId, err := handlers.PermalinkID(r, c, 4)

		if err != nil{
			return helpers.BadRequest{err}
		}
		if userId != u.Id {
			return helpers.BadRequest{errors.New("User cannot be updated")}
		}

		// only work on name other values should not be editable
		defer r.Body.Close()
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return helpers.InternalServerError{ errors.New("Error when reading request body") }
		}

		var updatedData UserData
		err = json.Unmarshal(body, &updatedData)
		if err != nil {
				return helpers.InternalServerError{ errors.New("Error when decoding request body") }
		}
		if helpers.IsEmailValid(updatedData.Email) && updatedData.Email != u.Email{
			u.Email = updatedData.Email
			usermdl.Update(c, u)
		}

		// return updated user
		return templateshlp.RenderJson(w, c, u)
	} else {
		return helpers.BadRequest{errors.New("not supported.")}
	}
}
<commit_msg>* j/users/ done! seems that the struct.go is not that helpfull after all, maybe having the json structures in the controllers is the way to go after all.. #210<commit_after>/*
 * Copyright (c) 2013 Santiago Arias | Remy Jourde | Carlos Bernal
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package users

import (
	"encoding/json"
	"errors"
	"html/template"
	"io/ioutil"
	"net/http"
	"time"

	"appengine"

	"github.com/santiaago/purple-wing/helpers"
	"github.com/santiaago/purple-wing/helpers/log"
	"github.com/santiaago/purple-wing/helpers/handlers"
	templateshlp "github.com/santiaago/purple-wing/helpers/templates"
	teamrelshlp "github.com/santiaago/purple-wing/helpers/teamrels"
	tournamentrelshlp "github.com/santiaago/purple-wing/helpers/tournamentrels"

	usermdl "github.com/santiaago/purple-wing/models/user"
	teammdl "github.com/santiaago/purple-wing/models/team"
	tournamentmdl "github.com/santiaago/purple-wing/models/tournament"
	teamrequestmdl "github.com/santiaago/purple-wing/models/teamrequest"

)

type Form struct {
	Username string
	Name string
	Email string
	ErrorUsername string
	ErrorName string
	ErrorEmail string
}

type UserData struct {
	Username string
	Name string
	Email string
}

// Show handler
func Show(w http.ResponseWriter, r *http.Request){
	c := appengine.NewContext(r)

	userId, err := handlers.PermalinkID(r, c, 3)
	if err != nil{
		http.Redirect(w,r, "/m/users/", http.StatusFound)
		return
	}

	funcs := template.FuncMap{
		"Profile": func() bool {return true},
	}

	t := template.Must(template.New("tmpl_user_show").
		Funcs(funcs).
		ParseFiles("templates/user/show.html",
		"templates/user/info.html",
		"templates/user/teams.html",
		"templates/user/tournaments.html",
		"templates/user/requests.html"))

	var user *usermdl.User
	user, err = usermdl.ById(c,userId)
	if err != nil{
		helpers.Error404(w)
		return
	}

	teams := usermdl.Teams(c, userId)
	tournaments := tournamentrelshlp.Tournaments(c, userId)
	teamRequests := teamrelshlp.TeamsRequests(c, teams)

	userData := struct {
		User *usermdl.User
		Teams []*teammdl.Team
		Tournaments []*tournamentmdl.Tournament
		TeamRequests []*teamrequestmdl.TeamRequest
	}{
		user,
		teams,
		tournaments,
		teamRequests,
	}

	templateshlp.RenderWithData(w, r, c, t, userData, funcs, "renderUserShow")
}

type userJson struct{
	Id int64
	Username string
	Name string
	Email string
	Created time.Time
}

// json index user handler
func IndexJson(w http.ResponseWriter, r *http.Request, u *usermdl.User) error{
	c := appengine.NewContext(r)

	if r.Method == "GET"{
		users := usermdl.FindAll(c)
		usersJson := make([]userJson, len(users))
		counterUsers := 0
		for _, user := range users{
			usersJson[counterUsers].Id = user.Id
			usersJson[counterUsers].Username = user.Username
			usersJson[counterUsers].Name = user.Name
			usersJson[counterUsers].Email = user.Email
			usersJson[counterUsers].Created = user.Created
			counterUsers++
		}
		return templateshlp.RenderJson(w, c, usersJson)

	} else {
		return helpers.BadRequest{errors.New("not supported.")}
	}
}

// Json show user handler
func ShowJson(w http.ResponseWriter, r *http.Request, u *usermdl.User) error{
	c := appengine.NewContext(r)
	log.Infof(c, "User Show Json Handler")

	if r.Method == "GET"{

		userId, err := handlers.PermalinkID(r, c, 4)
		if err != nil{
			return helpers.BadRequest{err}
		}

		var user *usermdl.User
		user, err = usermdl.ById(c,userId)
		if err != nil{
			return helpers.BadRequest{err}
		}

		// set teams info in user json
		teams := usermdl.Teams(c, userId)
		teamsJson := make([]helpers.TeamJsonZip, len(teams))
		counterTeams := 0
		for _, team := range teams{
			teamsJson[counterTeams].Id = team.Id
			teamsJson[counterTeams].Name = team.Name
			counterTeams++
		}
		var userJson helpers.UserJson
		userJson.Id = user.Id
		userJson.Username = user.Username
		userJson.Name = user.Name
		userJson.Email = user.Email
		userJson.IsAdmin = user.IsAdmin
		userJson.Auth = user.Auth
		userJson.Created = user.Created
		userJson.Teams = teamsJson

		// set tournament info in user json
		tournaments := tournamentrelshlp.Tournaments(c, userId)
		tournamentsJson := make([]helpers.TournamentJsonZip, len(tournaments))
		counterTournaments := 0
		for _, tournament := range tournaments{
			tournamentsJson[counterTournaments].Id = tournament.Id
			tournamentsJson[counterTournaments].Name = tournament.Name
			counterTournaments++
		}
		userJson.Tournaments = tournamentsJson

		// set request info in user json
		teamRequests := teamrelshlp.TeamsRequests(c, teams)
		teamRequestsJson := make([]helpers.TeamRequestJsonZip, len(teamRequests))
		counterRequests := 0
		for _, request := range teamRequests{
			teamRequestsJson[counterRequests].Id = request.Id
			teamRequestsJson[counterRequests].UserId = request.UserId
			teamRequestsJson[counterRequests].TeamId = request.TeamId
			counterRequests++
		}
		userJson.TeamRequests = teamRequestsJson

		return templateshlp.RenderJson(w, c, userJson)
	} else {
		return helpers.BadRequest{errors.New("not supported.")}
	}
}

// json update user handler
func UpdateJson(w http.ResponseWriter, r *http.Request, u *usermdl.User) error{
	c := appengine.NewContext(r)

	if r.Method == "POST"{
		userId, err := handlers.PermalinkID(r, c, 4)

		if err != nil{
			return helpers.BadRequest{err}
		}
		if userId != u.Id {
			return helpers.BadRequest{errors.New("User cannot be updated")}
		}

		// only work on name other values should not be editable
		defer r.Body.Close()
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return helpers.InternalServerError{ errors.New("Error when reading request body") }
		}

		var updatedData UserData
		err = json.Unmarshal(body, &updatedData)
		if err != nil {
				return helpers.InternalServerError{ errors.New("Error when decoding request body") }
		}
		if helpers.IsEmailValid(updatedData.Email) && updatedData.Email != u.Email{
			u.Email = updatedData.Email
			usermdl.Update(c, u)
		}

		// return updated user
		return templateshlp.RenderJson(w, c, u)
	} else {
		return helpers.BadRequest{errors.New("not supported.")}
	}
}
<|endoftext|>