<commit_before>package fox_test

import (
	. "fox"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"net/http"
	"github.com/gorilla/mux"
	"net/http/httptest"
	"encoding/json"
	"bytes"
	"io/ioutil"
	"io"
)

var _ = Describe("Fox", func() {
	var router *mux.Router
	var recorder *httptest.ResponseRecorder
	var request *http.Request
	var aFox Fox
	var anotherFox Fox

	BeforeEach(func(){
		LoadConfigByName("test_config.gcfg")
		router = NewRouter("test")
		recorder = httptest.NewRecorder()

		aFox = Fox{
			Name: "Rein",
			Parents: []string{"2", "3"},
		}

		anotherFox = Fox{
			Name: "NewName",
			Parents: []string{"4", "5"},
		}
	})

	Describe("Adding foxes", func(){
		BeforeEach(func(){
			m, _ := json.Marshal(aFox)
			request, _ = http.NewRequest("POST", "/fox/foxes", bytes.NewReader(m))
		})

		Context("Adding, reading and updating a fox", func(){
			// Simple adding of a fox
			It("Should return 201", func(){
				var f Fox
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(201))

			// See if we can get the same fox back
			// Read the UUID from the response first
				body, err := ioutil.ReadAll(io.LimitReader(recorder.Body, 1048576))
				Expect(err).To(BeNil())

				var id *UUID
				id = new(UUID)

				err = json.Unmarshal(body, id)
				Expect(err).To(BeNil())

				f = getFox(id.Uuid, router)

				// Updating the fox we just received
				anotherFox.Uuid = id.Uuid
				m, _ := json.Marshal(anotherFox)
				request, _ = http.NewRequest("PUT", "/fox/foxes/" + id.Uuid, bytes.NewReader(m))

				recorder = httptest.NewRecorder()
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(202))

				// Read the fox again
				f = getFox(id.Uuid, router)
				Expect(Compare(f, anotherFox)).To(BeTrue())

			})

			// Send garbage instead of a Fox
			It("Should return 422", func(){
				m, err := json.Marshal("This is not a valid Fox")
				Expect(err).To(BeNil())
				request, _ = http.NewRequest("POST", "/fox/foxes", bytes.NewReader(m))

				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(422))
			})
		})
	})

	Describe ("Reading the fox list", func(){
		BeforeEach(func(){
			request, _ = http.NewRequest("GET", "/fox/foxes", nil)
		})

		Context("Foxes exist", func(){
			It("Should return http 200", func(){
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(200))
			})
		})

		Context("Random fox should return 404", func(){
			It("Should return 404", func(){
				request, _ = http.NewRequest("GET", "/fox/foxes/nosuchfoxforsure", nil)
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(404))
			})
		})
	})

	Describe ("Updating a fox", func(){
		Context("Update a fox", func(){
			It("Should return 201", func(){
				m, _ := json.Marshal(aFox)
				request, _ = http.NewRequest("PUT", "/fox/foxes/nosuchfoxforsure", bytes.NewReader(m))
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(201))
			})
		})
	})

})

func getFox(uuid string, router *mux.Router) Fox{
	var r *http.Request
	var f *Fox

	recorder := httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/fox/foxes/" + uuid, nil)
	router.ServeHTTP(recorder, r)
	Expect(recorder.Code).To(Equal(200))

	body, err := ioutil.ReadAll(io.LimitReader(recorder.Body, 1048576))
	Expect(err).To(BeNil())

	f = new(Fox)

	err = json.Unmarshal(body, f)
	Expect(err).To(BeNil())

	return *f
}<commit_msg>Update operations now covered by tests<commit_after>package fox_test

import (
	. "fox"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"net/http"
	"github.com/gorilla/mux"
	"net/http/httptest"
	"encoding/json"
	"bytes"
	"io/ioutil"
	"io"
)

var bufferLength int64 = 1048576

var _ = Describe("Fox", func() {
	var router *mux.Router
	var recorder *httptest.ResponseRecorder
	var request *http.Request
	var aFox Fox
	var anotherFox Fox

	BeforeEach(func(){
		LoadConfigByName("test_config.gcfg")
		router = NewRouter("test")
		recorder = httptest.NewRecorder()

		aFox = Fox{
			Name: "Rein",
			Parents: []string{"2", "3"},
		}

		anotherFox = Fox{
			Name: "NewName",
			Parents: []string{"4", "5"},
		}
	})

	Describe("Adding foxes", func(){
		BeforeEach(func(){
			m, _ := json.Marshal(aFox)
			request, _ = http.NewRequest("POST", "/fox/foxes", bytes.NewReader(m))
		})

		Context("Adding, reading and updating a fox", func(){
			// Simple adding of a fox
			It("Should return 201", func(){
				var f Fox
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(201))

			// See if we can get the same fox back
			// Read the UUID from the response first
				body, err := ioutil.ReadAll(io.LimitReader(recorder.Body, bufferLength))
				Expect(err).To(BeNil())

				var id *UUID
				id = new(UUID)

				err = json.Unmarshal(body, id)
				Expect(err).To(BeNil())

				f = getFox(id.Uuid, router)

				// Updating the fox we just received
				anotherFox.Uuid = id.Uuid
				m, _ := json.Marshal(anotherFox)
				request, _ = http.NewRequest("PUT", "/fox/foxes/" + id.Uuid, bytes.NewReader(m))

				recorder = httptest.NewRecorder()
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(202))

				// Read the fox again
				f = getFox(id.Uuid, router)
				Expect(Compare(f, anotherFox)).To(BeTrue())
			})
		})
	})

	Describe ("Reading the fox list", func(){
		BeforeEach(func(){
			request, _ = http.NewRequest("GET", "/fox/foxes", nil)
		})

		Context("Foxes exist", func(){
			It("Should return http 200", func(){
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(200))
			})
		})

		Context("Random fox should return 404", func(){
			It("Should return 404", func(){
				request, _ = http.NewRequest("GET", "/fox/foxes/nosuchfoxforsure", nil)
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(404))
			})
		})
	})

	Describe ("Updating a fox", func(){
		var m []byte

		BeforeEach(func(){
				m, _ = json.Marshal(aFox)
		})

		Context("Update a fox", func(){
			It("Should return 201", func(){
				request, _ = http.NewRequest("PUT", "/fox/foxes/nosuchfoxforsure", bytes.NewReader(m))
				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(201))
			})

			It("Should return 422", func(){
				request, _ = http.NewRequest("POST", "/fox/foxes", bytes.NewReader(m))
				// Read the UUID from the response first
				router.ServeHTTP(recorder, request)
				body, err := ioutil.ReadAll(io.LimitReader(recorder.Body, bufferLength))
				Expect(err).To(BeNil())

				var id *UUID
				id = new(UUID)

				err = json.Unmarshal(body, id)
				Expect(err).To(BeNil())

				recorder = httptest.NewRecorder()
				m, _ = json.Marshal("This is not a valid Fox")
				request, _ = http.NewRequest("PUT", "/fox/foxes/" + id.Uuid, bytes.NewReader(m))

				router.ServeHTTP(recorder, request)
				Expect(recorder.Code).To(Equal(422))
			})

		})
	})

})

func getFox(uuid string, router *mux.Router) Fox{
	var r *http.Request
	var f *Fox

	recorder := httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/fox/foxes/" + uuid, nil)
	router.ServeHTTP(recorder, r)
	Expect(recorder.Code).To(Equal(200))

	body, err := ioutil.ReadAll(io.LimitReader(recorder.Body, bufferLength))
	Expect(err).To(BeNil())

	f = new(Fox)

	err = json.Unmarshal(body, f)
	Expect(err).To(BeNil())

	return *f
}<|endoftext|>


<commit_before>/*                          _       _
 *__      _____  __ ___   ___  __ _| |_ ___
 *\ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
 * \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
 *  \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
 *
 * Copyright © 2016 - 2019 Weaviate. All rights reserved.
 * LICENSE: https://github.com/creativesoftwarefdn/weaviate/blob/develop/LICENSE.md
 * DESIGN & CONCEPT: Bob van Luijt (@bobvanluijt)
 * CONTACT: hello@creativesoftwarefdn.org
 */
package test

// Acceptance tests for logging. Sets up a small fake endpoint that logs are sent to.

import (
	"io/ioutil"
	"net/http"
	"net/url"
	"testing"
	"time"

	"github.com/go-openapi/strfmt"

	"github.com/creativesoftwarefdn/weaviate/client/actions"
	"github.com/creativesoftwarefdn/weaviate/models"
	"github.com/creativesoftwarefdn/weaviate/test/acceptance/helper"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/ugorji/go/codec"
)

// Ensure a request should be logged and get the most recently received log from the mock api, then assert the structure of the log
func TestCreateActionLogging(t *testing.T) {
	t.Parallel()

	// send a request
	sendCreateActionRequest(t)

	// wait for the log to be posted
	time.Sleep(3 * time.Second)

	result := retrieveLogFromMockEndpoint(t)

	if result != nil {
		interpretedResult := interpretResult(t, result)
		if interpretedResult != nil {
			_, namePresent := interpretedResult["n"]
			_, typePresent := interpretedResult["t"]
			_, identifierPresent := interpretedResult["i"]
			_, amountPresent := interpretedResult["a"]
			_, whenPresent := interpretedResult["w"]

			assert.Equal(t, true, namePresent)
			assert.Equal(t, true, typePresent)
			assert.Equal(t, true, identifierPresent)
			assert.Equal(t, true, amountPresent)
			assert.Equal(t, true, whenPresent)

		}
	}
}

// sendCreateActionRequest is copied here to ensure at least one request should be logged when we check the mock api's most recently received request
func sendCreateActionRequest(t *testing.T) {
	// Set all action values to compare
	actionTestString := "Test string"
	actionTestInt := 1
	actionTestBoolean := true
	actionTestNumber := 1.337
	actionTestDate := "2017-10-06T08:15:30+01:00"

	params := actions.NewWeaviateActionsCreateParams().WithBody(actions.WeaviateActionsCreateBody{
		Action: &models.ActionCreate{
			AtContext: "http://example.org",
			AtClass:   "TestAction",
			Schema: map[string]interface{}{
				"testString":   actionTestString,
				"testInt":      actionTestInt,
				"testBoolean":  actionTestBoolean,
				"testNumber":   actionTestNumber,
				"testDateTime": actionTestDate,
			},
		},
	})

	resp, _, err := helper.Client(t).Actions.WeaviateActionsCreate(params, nil)

	// Ensure that the response is OK
	helper.AssertRequestOk(t, resp, err, func() {
		action := resp.Payload
		assert.Regexp(t, strfmt.UUIDPattern, action.ActionID)

		schema, ok := action.Schema.(map[string]interface{})
		if !ok {
			t.Fatal("The returned schema is not an JSON object")
		}

		// Check whether the returned information is the same as the data added
		assert.Equal(t, actionTestString, schema["testString"])
		assert.Equal(t, actionTestInt, int(schema["testInt"].(float64)))
		assert.Equal(t, actionTestBoolean, schema["testBoolean"])
		assert.Equal(t, actionTestNumber, schema["testNumber"])
		assert.Equal(t, actionTestDate, schema["testDateTime"])
	})
}

// retrieveLogFromMockEndpoint retrieves the most recently received log from the mock api
func retrieveLogFromMockEndpoint(t *testing.T) []byte {
	testURL, err := url.Parse("http://localhost:8087/mock/last")
	assert.Equal(t, nil, err)

	client := &http.Client{}
	resp, err := client.Get(testURL.String())
	if err == nil {
		body, _ := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
		return body
	}
	if err != nil {
		urlError, ok := err.(*url.Error)
		if ok {
			assert.Equal(t, nil, urlError.Op)
		}
	}

	return nil
}

// interpretResult converts the received cbor-encoded log to a map[string]interface
func interpretResult(t *testing.T, resultBody []byte) map[string]interface{} {
	decoded := make(map[string]interface{}, 0)
	cborHandle := new(codec.CborHandle)
	encoder := codec.NewDecoderBytes(resultBody, cborHandle)
	err := encoder.Decode(decoded)

	require.Equal(t, nil, err)
	return decoded
}
<commit_msg>gh-699: fixed telemetry acceptance test log response interpretation<commit_after>/*                          _       _
 *__      _____  __ ___   ___  __ _| |_ ___
 *\ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
 * \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
 *  \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
 *
 * Copyright © 2016 - 2019 Weaviate. All rights reserved.
 * LICENSE: https://github.com/creativesoftwarefdn/weaviate/blob/develop/LICENSE.md
 * DESIGN & CONCEPT: Bob van Luijt (@bobvanluijt)
 * CONTACT: hello@creativesoftwarefdn.org
 */
package test

// Acceptance tests for logging. Sets up a small fake endpoint that logs are sent to.

import (
	"io/ioutil"
	"net/http"
	"net/url"
	"testing"
	"time"

	"github.com/go-openapi/strfmt"

	"github.com/creativesoftwarefdn/weaviate/client/actions"
	"github.com/creativesoftwarefdn/weaviate/models"
	"github.com/creativesoftwarefdn/weaviate/test/acceptance/helper"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/ugorji/go/codec"
)

// Ensure a request should be logged and get the most recently received log from the mock api, then assert the structure of the log
func TestCreateActionLogging(t *testing.T) {
	t.Parallel()

	// send a request
	sendCreateActionRequest(t)

	// wait for the log to be posted
	time.Sleep(3 * time.Second)

	result := retrieveLogFromMockEndpoint(t)

	if result != nil {
		interpretedResult := interpretResult(t, result)
		if interpretedResult != nil {
			_, namePresent := interpretedResult["n"]
			_, typePresent := interpretedResult["t"]
			_, identifierPresent := interpretedResult["i"]
			_, amountPresent := interpretedResult["a"]
			_, whenPresent := interpretedResult["w"]

			assert.Equal(t, true, namePresent)
			assert.Equal(t, true, typePresent)
			assert.Equal(t, true, identifierPresent)
			assert.Equal(t, true, amountPresent)
			assert.Equal(t, true, whenPresent)

		}
	}
}

// sendCreateActionRequest is copied here to ensure at least one request should be logged when we check the mock api's most recently received request
func sendCreateActionRequest(t *testing.T) {
	// Set all action values to compare
	actionTestString := "Test string"
	actionTestInt := 1
	actionTestBoolean := true
	actionTestNumber := 1.337
	actionTestDate := "2017-10-06T08:15:30+01:00"

	params := actions.NewWeaviateActionsCreateParams().WithBody(actions.WeaviateActionsCreateBody{
		Action: &models.ActionCreate{
			AtContext: "http://example.org",
			AtClass:   "TestAction",
			Schema: map[string]interface{}{
				"testString":   actionTestString,
				"testInt":      actionTestInt,
				"testBoolean":  actionTestBoolean,
				"testNumber":   actionTestNumber,
				"testDateTime": actionTestDate,
			},
		},
	})

	resp, _, err := helper.Client(t).Actions.WeaviateActionsCreate(params, nil)

	// Ensure that the response is OK
	helper.AssertRequestOk(t, resp, err, func() {
		action := resp.Payload
		assert.Regexp(t, strfmt.UUIDPattern, action.ActionID)

		schema, ok := action.Schema.(map[string]interface{})
		if !ok {
			t.Fatal("The returned schema is not an JSON object")
		}

		// Check whether the returned information is the same as the data added
		assert.Equal(t, actionTestString, schema["testString"])
		assert.Equal(t, actionTestInt, int(schema["testInt"].(float64)))
		assert.Equal(t, actionTestBoolean, schema["testBoolean"])
		assert.Equal(t, actionTestNumber, schema["testNumber"])
		assert.Equal(t, actionTestDate, schema["testDateTime"])
	})
}

// retrieveLogFromMockEndpoint retrieves the most recently received log from the mock api
func retrieveLogFromMockEndpoint(t *testing.T) []byte {
	testURL, err := url.Parse("http://localhost:8087/mock/last")
	assert.Equal(t, nil, err)

	client := &http.Client{}
	resp, err := client.Get(testURL.String())
	if err == nil {
		body, _ := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
		return body
	}
	if err != nil {
		urlError, ok := err.(*url.Error)
		if ok {
			assert.Equal(t, nil, urlError.Op)
		}
	}

	return nil
}

// interpretResult converts the received cbor-encoded log to a []map[string]interface
func interpretResult(t *testing.T, resultBody []byte) map[string]interface{} {
	decoded := make([]map[string]interface{}, 1)
	cborHandle := new(codec.CborHandle)
	encoder := codec.NewDecoderBytes(resultBody, cborHandle)
	err := encoder.Decode(decoded)

	require.Equal(t, nil, err)
	return decoded[0]
}
<|endoftext|>


<commit_before>package inj

import "testing"

const (
	HELLO_SAYER_MESSAGE   = "Hello!"
	GOODBYE_SAYER_MESSAGE = "Bye!"
	DEFAULT_STRING        = "this is a string"
)

///////////////////////////////////////////////////
// Types for the unit and feature tests
///////////////////////////////////////////////////

type InterfaceOne interface {
	SayHello() string
}

type InterfaceTwo interface {
	SayGoodbye() string
}

type FuncType func(string) string
type ChanType chan interface{}

///////////////////////////////////////////////////
// Sample concrete type which requires two interfaces,
// the func type, the channel type and a string
///////////////////////////////////////////////////

type ConcreteType struct {
	Hello    InterfaceOne `inj:""`
	Goodbye  InterfaceTwo `inj:""`
	Stringer FuncType     `inj:""`
	Channel  ChanType     `inj:""`
	String   string       `inj:""`

	// This is nested
	Nested NestedType

	// These are not included in the injection
	Something     string `in:`
	SomethingElse int
}

// A nested type that contains depdendencies
type NestedType struct {
	Hello   InterfaceOne `inj:""`
	Goodbye InterfaceTwo `inj:""`
}

// Channel instance
var ichannel = make(ChanType)

///////////////////////////////////////////////////
// Implementation of a hello-sayer
///////////////////////////////////////////////////

type helloSayer struct{}

func (g *helloSayer) SayHello() string { return HELLO_SAYER_MESSAGE }

///////////////////////////////////////////////////
// Implementation of a goodbye-sayer
///////////////////////////////////////////////////

type goodbyeSayer struct{}

func (g *goodbyeSayer) SayGoodbye() string { return GOODBYE_SAYER_MESSAGE }

///////////////////////////////////////////////////
// Implementation of a FuncType
///////////////////////////////////////////////////

func funcInstance(s string) string {
	return s
}

//////////////////////////////////////////
// Assertion for concrete type
//////////////////////////////////////////

func assertConcreteValue(c ConcreteType, t *testing.T) {

	if c.Hello == nil {
		t.Errorf("c.Hello is nil")
	}

	if c.Goodbye == nil {
		t.Errorf("c.Goodbye is nil")
	}

	if c.Stringer == nil {
		t.Errorf("c.Stringer is nil")
	}

	if c.Channel == nil {
		t.Errorf("c.Channel is nil")
	}

	if c.String == "" {
		t.Errorf("c.String is nil")
	}

	if c.Nested.Hello == nil {
		t.Errorf("c.Hello is nil")
	}

	if c.Nested.Goodbye == nil {
		t.Errorf("c.Goodbye is nil")
	}

	if g, e := c.Hello.SayHello(), HELLO_SAYER_MESSAGE; g != e {
		t.Errorf("i2.SayHello(): got %s, expected %s", g, e)
	}

	if g, e := c.Goodbye.SayGoodbye(), GOODBYE_SAYER_MESSAGE; g != e {
		t.Errorf("i2.SayHello(): got %s, expected %s", g, e)
	}

	// test the function
	if g, e := c.Stringer(DEFAULT_STRING), DEFAULT_STRING; g != e {
		t.Errorf("Test Stringer: got %s, expected %s", g, e)
	}
}
<commit_msg>Explanatory comment<commit_after>package inj

import "testing"

const (
	HELLO_SAYER_MESSAGE   = "Hello!"
	GOODBYE_SAYER_MESSAGE = "Bye!"
	DEFAULT_STRING        = "this is a string"
)

///////////////////////////////////////////////////
// Types for the unit and feature tests
///////////////////////////////////////////////////

type InterfaceOne interface {
	SayHello() string
}

type InterfaceTwo interface {
	SayGoodbye() string
}

type FuncType func(string) string
type ChanType chan interface{}

///////////////////////////////////////////////////
// Sample concrete type which requires two interfaces,
// the func type, the channel type and a string
///////////////////////////////////////////////////

type ConcreteType struct {
	Hello    InterfaceOne `inj:""`
	Goodbye  InterfaceTwo `inj:""`
	Stringer FuncType     `inj:""`
	Channel  ChanType     `inj:""`
	String   string       `inj:""`

	// This is nested
	Nested NestedType

	// These are not included in the injection
	Something     string `in:`
	SomethingElse int
}

// A nested type that contains depdendencies
type NestedType struct {
	Hello   InterfaceOne `inj:""`
	Goodbye InterfaceTwo `inj:""`
}

// Channel instance
var ichannel = make(ChanType)

///////////////////////////////////////////////////
// Implementation of a hello-sayer
///////////////////////////////////////////////////

type helloSayer struct{}

func (g *helloSayer) SayHello() string { return HELLO_SAYER_MESSAGE }

///////////////////////////////////////////////////
// Implementation of a goodbye-sayer
///////////////////////////////////////////////////

type goodbyeSayer struct{}

func (g *goodbyeSayer) SayGoodbye() string { return GOODBYE_SAYER_MESSAGE }

///////////////////////////////////////////////////
// Implementation of a FuncType
///////////////////////////////////////////////////

func funcInstance(s string) string {
	return s
}

//////////////////////////////////////////
// Assertion for concrete type
//////////////////////////////////////////

// Once the dependencies have been injected, all the depdendent
// members should be non-nil and functional.
func assertConcreteValue(c ConcreteType, t *testing.T) {

	if c.Hello == nil {
		t.Errorf("c.Hello is nil")
	}

	if c.Goodbye == nil {
		t.Errorf("c.Goodbye is nil")
	}

	if c.Stringer == nil {
		t.Errorf("c.Stringer is nil")
	}

	if c.Channel == nil {
		t.Errorf("c.Channel is nil")
	}

	if c.String == "" {
		t.Errorf("c.String is nil")
	}

	if c.Nested.Hello == nil {
		t.Errorf("c.Hello is nil")
	}

	if c.Nested.Goodbye == nil {
		t.Errorf("c.Goodbye is nil")
	}

	if g, e := c.Hello.SayHello(), HELLO_SAYER_MESSAGE; g != e {
		t.Errorf("i2.SayHello(): got %s, expected %s", g, e)
	}

	if g, e := c.Goodbye.SayGoodbye(), GOODBYE_SAYER_MESSAGE; g != e {
		t.Errorf("i2.SayHello(): got %s, expected %s", g, e)
	}

	// test the function
	if g, e := c.Stringer(DEFAULT_STRING), DEFAULT_STRING; g != e {
		t.Errorf("Test Stringer: got %s, expected %s", g, e)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package testsuites

import (
	"fmt"

	"github.com/onsi/ginkgo"

	v1 "k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
	"k8s.io/kubernetes/test/e2e/storage/testpatterns"
)

const (
	noProvisioner = "kubernetes.io/no-provisioner"
	pvNamePrefix  = "pv"
)

type volumeModeTestSuite struct {
	tsInfo TestSuiteInfo
}

var _ TestSuite = &volumeModeTestSuite{}

// InitVolumeModeTestSuite returns volumeModeTestSuite that implements TestSuite interface
func InitVolumeModeTestSuite() TestSuite {
	return &volumeModeTestSuite{
		tsInfo: TestSuiteInfo{
			name: "volumeMode",
			testPatterns: []testpatterns.TestPattern{
				testpatterns.FsVolModePreprovisionedPV,
				testpatterns.FsVolModeDynamicPV,
				testpatterns.BlockVolModePreprovisionedPV,
				testpatterns.BlockVolModeDynamicPV,
			},
		},
	}
}

func (t *volumeModeTestSuite) getTestSuiteInfo() TestSuiteInfo {
	return t.tsInfo
}

func (t *volumeModeTestSuite) defineTests(driver TestDriver, pattern testpatterns.TestPattern) {
	type local struct {
		config      *PerTestConfig
		testCleanup func()

		cs clientset.Interface
		ns *v1.Namespace
		// genericVolumeTestResource contains pv, pvc, sc, etc., owns cleaning that up
		genericVolumeTestResource

		intreeOps   opCounts
		migratedOps opCounts
	}
	var (
		dInfo = driver.GetDriverInfo()
		l     local
	)

	// No preconditions to test. Normally they would be in a BeforeEach here.

	// This intentionally comes after checking the preconditions because it
	// registers its own BeforeEach which creates the namespace. Beware that it
	// also registers an AfterEach which renders f unusable. Any code using
	// f must run inside an It or Context callback.
	f := framework.NewDefaultFramework("volumemode")

	init := func() {
		l = local{}
		l.ns = f.Namespace
		l.cs = f.ClientSet

		// Now do the more expensive test initialization.
		l.config, l.testCleanup = driver.PrepareTest(f)
		l.intreeOps, l.migratedOps = getMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName)

		fsType := pattern.FsType
		volBindMode := storagev1.VolumeBindingImmediate

		var (
			scName             string
			pvSource           *v1.PersistentVolumeSource
			volumeNodeAffinity *v1.VolumeNodeAffinity
		)

		l.genericVolumeTestResource = genericVolumeTestResource{
			driver:  driver,
			config:  l.config,
			pattern: pattern,
		}

		// Create volume for pre-provisioned volume tests
		l.volume = CreateVolume(driver, l.config, pattern.VolType)

		switch pattern.VolType {
		case testpatterns.PreprovisionedPV:
			if pattern.VolMode == v1.PersistentVolumeBlock {
				scName = fmt.Sprintf("%s-%s-sc-for-block", l.ns.Name, dInfo.Name)
			} else if pattern.VolMode == v1.PersistentVolumeFilesystem {
				scName = fmt.Sprintf("%s-%s-sc-for-file", l.ns.Name, dInfo.Name)
			}
			if pDriver, ok := driver.(PreprovisionedPVTestDriver); ok {
				pvSource, volumeNodeAffinity = pDriver.GetPersistentVolumeSource(false, fsType, l.volume)
				if pvSource == nil {
					framework.Skipf("Driver %q does not define PersistentVolumeSource - skipping", dInfo.Name)
				}

				storageClass, pvConfig, pvcConfig := generateConfigsForPreprovisionedPVTest(scName, volBindMode, pattern.VolMode, *pvSource, volumeNodeAffinity)
				l.sc = storageClass
				l.pv = framework.MakePersistentVolume(pvConfig)
				l.pvc = framework.MakePersistentVolumeClaim(pvcConfig, l.ns.Name)
			}
		case testpatterns.DynamicPV:
			if dDriver, ok := driver.(DynamicPVTestDriver); ok {
				l.sc = dDriver.GetDynamicProvisionStorageClass(l.config, fsType)
				if l.sc == nil {
					framework.Skipf("Driver %q does not define Dynamic Provision StorageClass - skipping", dInfo.Name)
				}
				l.sc.VolumeBindingMode = &volBindMode

				l.pvc = framework.MakePersistentVolumeClaim(framework.PersistentVolumeClaimConfig{
					ClaimSize:        dDriver.GetClaimSize(),
					StorageClassName: &(l.sc.Name),
					VolumeMode:       &pattern.VolMode,
				}, l.ns.Name)
			}
		default:
			e2elog.Failf("Volume mode test doesn't support: %s", pattern.VolType)
		}
	}

	cleanup := func() {
		l.cleanupResource()

		if l.testCleanup != nil {
			l.testCleanup()
			l.testCleanup = nil
		}

		validateMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName, l.intreeOps, l.migratedOps)
	}

	// We register different tests depending on the drive
	isBlockSupported := dInfo.Capabilities[CapBlock]
	switch pattern.VolType {
	case testpatterns.PreprovisionedPV:
		if pattern.VolMode == v1.PersistentVolumeBlock && !isBlockSupported {
			ginkgo.It("should fail to create pod by failing to mount volume [Slow]", func() {
				init()
				defer cleanup()

				var err error

				ginkgo.By("Creating sc")
				l.sc, err = l.cs.StorageV1().StorageClasses().Create(l.sc)
				framework.ExpectNoError(err)

				ginkgo.By("Creating pv and pvc")
				l.pv, err = l.cs.CoreV1().PersistentVolumes().Create(l.pv)
				framework.ExpectNoError(err)

				// Prebind pv
				l.pvc.Spec.VolumeName = l.pv.Name
				l.pvc, err = l.cs.CoreV1().PersistentVolumeClaims(l.ns.Name).Create(l.pvc)
				framework.ExpectNoError(err)

				framework.ExpectNoError(framework.WaitOnPVandPVC(l.cs, l.ns.Name, l.pv, l.pvc))

				ginkgo.By("Creating pod")
				pod, err := framework.CreateSecPodWithNodeSelection(l.cs, l.ns.Name, []*v1.PersistentVolumeClaim{l.pvc},
					nil, false, "", false, false, framework.SELinuxLabel,
					nil, framework.NodeSelection{Name: l.config.ClientNodeName}, framework.PodStartTimeout)
				defer func() {
					framework.ExpectNoError(framework.DeletePodWithWait(f, l.cs, pod))
				}()
				framework.ExpectError(err)
			})
			// TODO(mkimuram): Add more tests
		}

	case testpatterns.DynamicPV:
		if pattern.VolMode == v1.PersistentVolumeBlock && !isBlockSupported {
			ginkgo.It("should fail in binding dynamic provisioned PV to PVC [Slow]", func() {
				init()
				defer cleanup()

				var err error

				ginkgo.By("Creating sc")
				l.sc, err = l.cs.StorageV1().StorageClasses().Create(l.sc)
				framework.ExpectNoError(err)

				ginkgo.By("Creating pv and pvc")
				l.pvc, err = l.cs.CoreV1().PersistentVolumeClaims(l.ns.Name).Create(l.pvc)
				framework.ExpectNoError(err)

				err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, l.cs, l.pvc.Namespace, l.pvc.Name, framework.Poll, framework.ClaimProvisionTimeout)
				framework.ExpectError(err)
			})
			// TODO(mkimuram): Add more tests
		}
	default:
		e2elog.Failf("Volume mode test doesn't support volType: %v", pattern.VolType)
	}

}

func generateConfigsForPreprovisionedPVTest(scName string, volBindMode storagev1.VolumeBindingMode,
	volMode v1.PersistentVolumeMode, pvSource v1.PersistentVolumeSource, volumeNodeAffinity *v1.VolumeNodeAffinity) (*storagev1.StorageClass,
	framework.PersistentVolumeConfig, framework.PersistentVolumeClaimConfig) {
	// StorageClass
	scConfig := &storagev1.StorageClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: scName,
		},
		Provisioner:       noProvisioner,
		VolumeBindingMode: &volBindMode,
	}
	// PV
	pvConfig := framework.PersistentVolumeConfig{
		PVSource:         pvSource,
		NodeAffinity:     volumeNodeAffinity,
		NamePrefix:       pvNamePrefix,
		StorageClassName: scName,
		VolumeMode:       &volMode,
	}
	// PVC
	pvcConfig := framework.PersistentVolumeClaimConfig{
		AccessModes:      []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
		StorageClassName: &scName,
		VolumeMode:       &volMode,
	}

	return scConfig, pvConfig, pvcConfig
}
<commit_msg>Add test for mismatched usage of filesystem/block volumes<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package testsuites

import (
	"fmt"

	"github.com/onsi/ginkgo"

	v1 "k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	"k8s.io/kubernetes/test/e2e/storage/testpatterns"
)

const (
	noProvisioner = "kubernetes.io/no-provisioner"
	pvNamePrefix  = "pv"
)

type volumeModeTestSuite struct {
	tsInfo TestSuiteInfo
}

var _ TestSuite = &volumeModeTestSuite{}

// InitVolumeModeTestSuite returns volumeModeTestSuite that implements TestSuite interface
func InitVolumeModeTestSuite() TestSuite {
	return &volumeModeTestSuite{
		tsInfo: TestSuiteInfo{
			name: "volumeMode",
			testPatterns: []testpatterns.TestPattern{
				testpatterns.FsVolModePreprovisionedPV,
				testpatterns.FsVolModeDynamicPV,
				testpatterns.BlockVolModePreprovisionedPV,
				testpatterns.BlockVolModeDynamicPV,
			},
		},
	}
}

func (t *volumeModeTestSuite) getTestSuiteInfo() TestSuiteInfo {
	return t.tsInfo
}

func (t *volumeModeTestSuite) defineTests(driver TestDriver, pattern testpatterns.TestPattern) {
	type local struct {
		config      *PerTestConfig
		testCleanup func()

		cs clientset.Interface
		ns *v1.Namespace
		// genericVolumeTestResource contains pv, pvc, sc, etc., owns cleaning that up
		genericVolumeTestResource

		intreeOps   opCounts
		migratedOps opCounts
	}
	var (
		dInfo = driver.GetDriverInfo()
		l     local
	)

	// No preconditions to test. Normally they would be in a BeforeEach here.

	// This intentionally comes after checking the preconditions because it
	// registers its own BeforeEach which creates the namespace. Beware that it
	// also registers an AfterEach which renders f unusable. Any code using
	// f must run inside an It or Context callback.
	f := framework.NewDefaultFramework("volumemode")

	init := func() {
		l = local{}
		l.ns = f.Namespace
		l.cs = f.ClientSet

		// Now do the more expensive test initialization.
		l.config, l.testCleanup = driver.PrepareTest(f)
		l.intreeOps, l.migratedOps = getMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName)
	}

	// manualInit initializes l.genericVolumeTestResource without creating the PV & PVC objects.
	manualInit := func() {
		init()

		fsType := pattern.FsType
		volBindMode := storagev1.VolumeBindingImmediate

		var (
			scName             string
			pvSource           *v1.PersistentVolumeSource
			volumeNodeAffinity *v1.VolumeNodeAffinity
		)

		l.genericVolumeTestResource = genericVolumeTestResource{
			driver:  driver,
			config:  l.config,
			pattern: pattern,
		}

		// Create volume for pre-provisioned volume tests
		l.volume = CreateVolume(driver, l.config, pattern.VolType)

		switch pattern.VolType {
		case testpatterns.PreprovisionedPV:
			if pattern.VolMode == v1.PersistentVolumeBlock {
				scName = fmt.Sprintf("%s-%s-sc-for-block", l.ns.Name, dInfo.Name)
			} else if pattern.VolMode == v1.PersistentVolumeFilesystem {
				scName = fmt.Sprintf("%s-%s-sc-for-file", l.ns.Name, dInfo.Name)
			}
			if pDriver, ok := driver.(PreprovisionedPVTestDriver); ok {
				pvSource, volumeNodeAffinity = pDriver.GetPersistentVolumeSource(false, fsType, l.volume)
				if pvSource == nil {
					framework.Skipf("Driver %q does not define PersistentVolumeSource - skipping", dInfo.Name)
				}

				storageClass, pvConfig, pvcConfig := generateConfigsForPreprovisionedPVTest(scName, volBindMode, pattern.VolMode, *pvSource, volumeNodeAffinity)
				l.sc = storageClass
				l.pv = framework.MakePersistentVolume(pvConfig)
				l.pvc = framework.MakePersistentVolumeClaim(pvcConfig, l.ns.Name)
			}
		case testpatterns.DynamicPV:
			if dDriver, ok := driver.(DynamicPVTestDriver); ok {
				l.sc = dDriver.GetDynamicProvisionStorageClass(l.config, fsType)
				if l.sc == nil {
					framework.Skipf("Driver %q does not define Dynamic Provision StorageClass - skipping", dInfo.Name)
				}
				l.sc.VolumeBindingMode = &volBindMode

				l.pvc = framework.MakePersistentVolumeClaim(framework.PersistentVolumeClaimConfig{
					ClaimSize:        dDriver.GetClaimSize(),
					StorageClassName: &(l.sc.Name),
					VolumeMode:       &pattern.VolMode,
				}, l.ns.Name)
			}
		default:
			e2elog.Failf("Volume mode test doesn't support: %s", pattern.VolType)
		}
	}

	cleanup := func() {
		l.cleanupResource()

		if l.testCleanup != nil {
			l.testCleanup()
			l.testCleanup = nil
		}

		validateMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName, l.intreeOps, l.migratedOps)
	}

	// We register different tests depending on the drive
	isBlockSupported := dInfo.Capabilities[CapBlock]
	switch pattern.VolType {
	case testpatterns.PreprovisionedPV:
		if pattern.VolMode == v1.PersistentVolumeBlock && !isBlockSupported {
			ginkgo.It("should fail to create pod by failing to mount volume [Slow]", func() {
				manualInit()
				defer cleanup()

				var err error

				ginkgo.By("Creating sc")
				l.sc, err = l.cs.StorageV1().StorageClasses().Create(l.sc)
				framework.ExpectNoError(err)

				ginkgo.By("Creating pv and pvc")
				l.pv, err = l.cs.CoreV1().PersistentVolumes().Create(l.pv)
				framework.ExpectNoError(err)

				// Prebind pv
				l.pvc.Spec.VolumeName = l.pv.Name
				l.pvc, err = l.cs.CoreV1().PersistentVolumeClaims(l.ns.Name).Create(l.pvc)
				framework.ExpectNoError(err)

				framework.ExpectNoError(framework.WaitOnPVandPVC(l.cs, l.ns.Name, l.pv, l.pvc))

				ginkgo.By("Creating pod")
				pod, err := framework.CreateSecPodWithNodeSelection(l.cs, l.ns.Name, []*v1.PersistentVolumeClaim{l.pvc},
					nil, false, "", false, false, framework.SELinuxLabel,
					nil, framework.NodeSelection{Name: l.config.ClientNodeName}, framework.PodStartTimeout)
				defer func() {
					framework.ExpectNoError(framework.DeletePodWithWait(f, l.cs, pod))
				}()
				framework.ExpectError(err)
			})
		}

	case testpatterns.DynamicPV:
		if pattern.VolMode == v1.PersistentVolumeBlock && !isBlockSupported {
			ginkgo.It("should fail in binding dynamic provisioned PV to PVC [Slow]", func() {
				manualInit()
				defer cleanup()

				var err error

				ginkgo.By("Creating sc")
				l.sc, err = l.cs.StorageV1().StorageClasses().Create(l.sc)
				framework.ExpectNoError(err)

				ginkgo.By("Creating pv and pvc")
				l.pvc, err = l.cs.CoreV1().PersistentVolumeClaims(l.ns.Name).Create(l.pvc)
				framework.ExpectNoError(err)

				err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, l.cs, l.pvc.Namespace, l.pvc.Name, framework.Poll, framework.ClaimProvisionTimeout)
				framework.ExpectError(err)
			})
		}
	default:
		e2elog.Failf("Volume mode test doesn't support volType: %v", pattern.VolType)
	}

	ginkgo.It("should fail to use a volume in a pod with mismatched mode [Slow]", func() {
		skipBlockTest(driver)
		init()
		l.genericVolumeTestResource = *createGenericVolumeTestResource(driver, l.config, pattern)
		defer cleanup()

		ginkgo.By("Creating pod")
		var err error
		pod := framework.MakeSecPod(l.ns.Name, []*v1.PersistentVolumeClaim{l.pvc}, false, "", false, false, framework.SELinuxLabel, nil)
		// Change volumeMounts to volumeDevices and the other way around
		pod = swapVolumeMode(pod)

		// Run the pod
		pod, err = l.cs.CoreV1().Pods(l.ns.Name).Create(pod)
		framework.ExpectNoError(err)
		defer func() {
			framework.ExpectNoError(framework.DeletePodWithWait(f, l.cs, pod))
		}()

		// TODO: find a faster way how to check the pod can't start,
		// perhaps when https://github.com/kubernetes/kubernetes/issues/79794 is fixed.
		err = e2epod.WaitTimeoutForPodRunningInNamespace(l.cs, pod.Name, l.ns.Name, framework.PodStartTimeout)
		framework.ExpectError(err, "pod with mismatched block/filesystem volumes should not start")
	})

}

func generateConfigsForPreprovisionedPVTest(scName string, volBindMode storagev1.VolumeBindingMode,
	volMode v1.PersistentVolumeMode, pvSource v1.PersistentVolumeSource, volumeNodeAffinity *v1.VolumeNodeAffinity) (*storagev1.StorageClass,
	framework.PersistentVolumeConfig, framework.PersistentVolumeClaimConfig) {
	// StorageClass
	scConfig := &storagev1.StorageClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: scName,
		},
		Provisioner:       noProvisioner,
		VolumeBindingMode: &volBindMode,
	}
	// PV
	pvConfig := framework.PersistentVolumeConfig{
		PVSource:         pvSource,
		NodeAffinity:     volumeNodeAffinity,
		NamePrefix:       pvNamePrefix,
		StorageClassName: scName,
		VolumeMode:       &volMode,
	}
	// PVC
	pvcConfig := framework.PersistentVolumeClaimConfig{
		AccessModes:      []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
		StorageClassName: &scName,
		VolumeMode:       &volMode,
	}

	return scConfig, pvConfig, pvcConfig
}

// swapVolumeMode changes volumeMounts to volumeDevices and the other way around
func swapVolumeMode(podTemplate *v1.Pod) *v1.Pod {
	pod := podTemplate.DeepCopy()
	for c := range pod.Spec.Containers {
		container := &pod.Spec.Containers[c]
		container.VolumeDevices = []v1.VolumeDevice{}
		container.VolumeMounts = []v1.VolumeMount{}

		// Change VolumeMounts to VolumeDevices
		for _, volumeMount := range podTemplate.Spec.Containers[c].VolumeMounts {
			container.VolumeDevices = append(container.VolumeDevices, v1.VolumeDevice{
				Name:       volumeMount.Name,
				DevicePath: volumeMount.MountPath,
			})
		}
		// Change VolumeDevices to VolumeMounts
		for _, volumeDevice := range podTemplate.Spec.Containers[c].VolumeDevices {
			container.VolumeMounts = append(container.VolumeMounts, v1.VolumeMount{
				Name:      volumeDevice.Name,
				MountPath: volumeDevice.DevicePath,
			})
		}
	}
	return pod
}
<|endoftext|>


<commit_before>package transform

import (
	"image"
	"net/url"
	"strconv"
	"strings"

	"github.com/disintegration/imaging"
	"github.com/sirupsen/logrus"
)

// RotateImage implements the rotating scheme described on:
// https://docs.fastly.com/api/imageopto/orient
func RotateImage(m image.Image, orient string) image.Image {
	switch orient {
	case "r":
		return imaging.Rotate270(m)
	case "l":
		return imaging.Rotate90(m)
	case "h":
		return imaging.FlipH(m)
	case "v":
		return imaging.FlipV(m)
	case "hv":
		return imaging.FlipV(imaging.FlipH(m))
	case "vh":
		return imaging.FlipH(imaging.FlipV(m))

	// case "1":
	//  // Parse the EXIF data and perform a rotation automatically.
	// 	// Pending support from https://github.com/golang/go/issues/4341
	// 	return m

	case "2":
		return imaging.FlipH(m)
	case "3":
		return imaging.FlipV(imaging.FlipH(m))
	case "4":
		return imaging.FlipV(m)
	case "5":
		return imaging.Rotate90(imaging.FlipH(m))
	case "6":
		return imaging.Rotate270(m)
	case "7":
		return imaging.Rotate270(imaging.FlipH(m))
	case "8":
		return imaging.Rotate90(m)
	default:
		return m
	}
}

// =============================================================================

// CropImage performs cropping operations based on the api described:
// https://docs.fastly.com/api/imageopto/crop
func CropImage(m image.Image, crop string) image.Image {
	// This assumes that the crop string contains the following form:
	//   {width},{height}
	// And will anchor it to the center point.
	if wh := strings.Split(crop, ","); len(wh) == 2 {
		width, err := strconv.Atoi(wh[0])
		if err != nil {
			return m
		}

		height, err := strconv.Atoi(wh[1])
		if err != nil {
			return m
		}

		return imaging.CropCenter(m, width, height)
	}

	return m
}

// =============================================================================

// GetResizeDimension will get the resize dimension.
func GetResizeDimension(resize string) int {
	if resize == "" {
		return 0
	}

	dimension, err := strconv.Atoi(resize)
	if err != nil {
		return 0
	}

	if dimension > 8192 {
		return 8192
	}

	return dimension
}

// =============================================================================

// GetResampleFilter gets the resample filter to use for resizing.
func GetResampleFilter(filter string) imaging.ResampleFilter {
	switch filter {
	case "lanczos":
		return imaging.Lanczos
	case "nearest":
		return imaging.NearestNeighbor
	case "linear":
		return imaging.Linear
	case "netravali":
		return imaging.MitchellNetravali
	case "box":
		return imaging.Box
	case "gaussian":
		return imaging.Gaussian
	default:
		return imaging.Lanczos
	}
}

// =============================================================================

// GetFit will return the fit parameter.
func GetFit(fit string) string {
	switch fit {
	case "cover":
		return "cover"
	case "bounds":
		return "bounds"
	default:
		return ""
	}
}

// =============================================================================

// ResizeImage resizes the image with the given resample filter.
func ResizeImage(m image.Image, w, h string, originalWidth, originalHeight int, fit string, filter imaging.ResampleFilter) image.Image {
	width := GetResizeDimension(w)
	height := GetResizeDimension(h)

	// If both width and height are provided, and we have a valid fit mode, then
	// perform a resize.
	if width > 0 && height > 0 {
		switch fit {
		case "bounds":
			// Calculate the scales relative to the orignals.
			widthScale := float32(width) / float32(originalWidth)
			heightScale := float32(height) / float32(originalHeight)

			// Find the smallest scale.
			scale := widthScale
			if widthScale > heightScale {
				scale = heightScale
			}

			// Calculate the resized dimensions.
			width = int(float32(originalWidth) * scale)
			height = int(float32(originalHeight) * scale)

			// Resize the orignal dimensions to that scale.
			return imaging.Resize(m, width, height, filter)
		case "cover":
			return imaging.Resize(m, width, height, filter)
		}
	}

	// Resize the width if it was provided.
	if width > 0 {
		if width > originalWidth {
			// Don't resize if it's larger than the original!
			return m
		}

		return imaging.Resize(m, width, 0, filter)
	}

	// Resize the height if provided.
	if height > 0 {
		if height > originalHeight {
			// Don't resize if it's larger than the original!
			return m
		}

		return imaging.Resize(m, 0, height, filter)
	}

	return m
}

// =============================================================================

// Image transforms the image based on data found in the request. Following the
// available query params in the root README, this will parse the query params
// and apply image transformations.
func Image(m image.Image, v url.Values) (image.Image, error) {
	// Extract the width + height from the image bounds.
	width := m.Bounds().Max.X
	height := m.Bounds().Max.Y

	logrus.WithFields(logrus.Fields(map[string]interface{}{
		"width":  width,
		"height": height,
	})).Debug("image dimensions")

	// Crop the image if the crop parameter was provided.
	crop := v.Get("crop")
	if crop != "" {
		// Crop the image.
		m = CropImage(m, crop)
	}

	// Resize the image if the width or height are provided.
	w := v.Get("width")
	h := v.Get("height")

	if w != "" || h != "" {
		// Get the resize filter to use.
		filter := GetResampleFilter(v.Get("resize-filter"))
		fit := GetFit(v.Get("fit"))

		m = ResizeImage(m, w, h, width, height, fit, filter)
	}

	// Reorient the image if the orientation parameter was provided.
	orient := v.Get("orient")
	if orient != "" {
		// Rotate the image.
		m = RotateImage(m, orient)
	}

	// Blur the image if the parameter was provided.
	blur := v.Get("blur")
	if blur != "" {
		sigma, err := strconv.ParseFloat(blur, 64)
		if err == nil && sigma > 0 {
			m = imaging.Blur(m, sigma)
		}
	}

	return m, nil
}
<commit_msg>chore: linting<commit_after>package transform

import (
	"image"
	"net/url"
	"strconv"
	"strings"

	"github.com/disintegration/imaging"
	"github.com/sirupsen/logrus"
)

// RotateImage implements the rotating scheme described on:
// https://docs.fastly.com/api/imageopto/orient
func RotateImage(m image.Image, orient string) image.Image {
	switch orient {
	case "r":
		return imaging.Rotate270(m)
	case "l":
		return imaging.Rotate90(m)
	case "h":
		return imaging.FlipH(m)
	case "v":
		return imaging.FlipV(m)
	case "hv":
		return imaging.FlipV(imaging.FlipH(m))
	case "vh":
		return imaging.FlipH(imaging.FlipV(m))

	// case "1":
	//  // Parse the EXIF data and perform a rotation automatically.
	// 	// Pending support from https://github.com/golang/go/issues/4341
	// 	return m

	case "2":
		return imaging.FlipH(m)
	case "3":
		return imaging.FlipV(imaging.FlipH(m))
	case "4":
		return imaging.FlipV(m)
	case "5":
		return imaging.Rotate90(imaging.FlipH(m))
	case "6":
		return imaging.Rotate270(m)
	case "7":
		return imaging.Rotate270(imaging.FlipH(m))
	case "8":
		return imaging.Rotate90(m)
	default:
		return m
	}
}

// =============================================================================

// CropImage performs cropping operations based on the api described:
// https://docs.fastly.com/api/imageopto/crop
func CropImage(m image.Image, crop string) image.Image {
	// This assumes that the crop string contains the following form:
	//   {width},{height}
	// And will anchor it to the center point.
	if wh := strings.Split(crop, ","); len(wh) == 2 {
		width, err := strconv.Atoi(wh[0])
		if err != nil {
			return m
		}

		height, err := strconv.Atoi(wh[1])
		if err != nil {
			return m
		}

		return imaging.CropCenter(m, width, height)
	}

	return m
}

// =============================================================================

// GetResizeDimension will get the resize dimension.
func GetResizeDimension(resize string) int {
	if resize == "" {
		return 0
	}

	dimension, err := strconv.Atoi(resize)
	if err != nil {
		return 0
	}

	if dimension > 8192 {
		return 8192
	}

	return dimension
}

// =============================================================================

// GetResampleFilter gets the resample filter to use for resizing.
func GetResampleFilter(filter string) imaging.ResampleFilter {
	switch filter {
	case "lanczos":
		return imaging.Lanczos
	case "nearest":
		return imaging.NearestNeighbor
	case "linear":
		return imaging.Linear
	case "netravali":
		return imaging.MitchellNetravali
	case "box":
		return imaging.Box
	case "gaussian":
		return imaging.Gaussian
	default:
		return imaging.Lanczos
	}
}

// =============================================================================

// GetFit will return the fit parameter.
func GetFit(fit string) string {
	switch fit {
	case "cover":
		return "cover"
	case "bounds":
		return "bounds"
	default:
		return ""
	}
}

// =============================================================================

// ResizeImage resizes the image with the given resample filter.
func ResizeImage(m image.Image, w, h string, originalWidth, originalHeight int, fit string, filter imaging.ResampleFilter) image.Image {
	width := GetResizeDimension(w)
	height := GetResizeDimension(h)

	// If both width and height are provided, and we have a valid fit mode, then
	// perform a resize.
	if width > 0 && height > 0 {
		switch fit {
		case "bounds":
			// Calculate the scales relative to the originals.
			widthScale := float32(width) / float32(originalWidth)
			heightScale := float32(height) / float32(originalHeight)

			// Find the smallest scale.
			scale := widthScale
			if widthScale > heightScale {
				scale = heightScale
			}

			// Calculate the resized dimensions.
			width = int(float32(originalWidth) * scale)
			height = int(float32(originalHeight) * scale)

			// Resize the original dimensions to that scale.
			return imaging.Resize(m, width, height, filter)
		case "cover":
			return imaging.Resize(m, width, height, filter)
		}
	}

	// Resize the width if it was provided.
	if width > 0 {
		if width > originalWidth {
			// Don't resize if it's larger than the original!
			return m
		}

		return imaging.Resize(m, width, 0, filter)
	}

	// Resize the height if provided.
	if height > 0 {
		if height > originalHeight {
			// Don't resize if it's larger than the original!
			return m
		}

		return imaging.Resize(m, 0, height, filter)
	}

	return m
}

// =============================================================================

// Image transforms the image based on data found in the request. Following the
// available query params in the root README, this will parse the query params
// and apply image transformations.
func Image(m image.Image, v url.Values) (image.Image, error) {
	// Extract the width + height from the image bounds.
	width := m.Bounds().Max.X
	height := m.Bounds().Max.Y

	logrus.WithFields(logrus.Fields(map[string]interface{}{
		"width":  width,
		"height": height,
	})).Debug("image dimensions")

	// Crop the image if the crop parameter was provided.
	if crop := v.Get("crop"); crop != "" {
		// Crop the image.
		m = CropImage(m, crop)
	}

	// Resize the image if the width or height are provided.
	w := v.Get("width")
	h := v.Get("height")

	if w != "" || h != "" {
		// Get the resize filter to use.
		filter := GetResampleFilter(v.Get("resize-filter"))
		fit := GetFit(v.Get("fit"))

		m = ResizeImage(m, w, h, width, height, fit, filter)
	}

	// Reorient the image if the orientation parameter was provided.
	if orient := v.Get("orient"); orient != "" {
		// Rotate the image.
		m = RotateImage(m, orient)
	}

	// Blur the image if the parameter was provided.
	if blur := v.Get("blur"); blur != "" {
		sigma, err := strconv.ParseFloat(blur, 64)
		if err == nil && sigma > 0 {
			m = imaging.Blur(m, sigma)
		}
	}

	return m, nil
}
<|endoftext|>


<commit_before>package batch

import (
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/common/export"
	"github.com/qiniu/qshell/v2/iqshell/common/flow"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/storage/bucket"
)

type Info struct {
	flow.Info
	export.FileExporterConfig

	Overwrite bool // 是否覆盖

	// 工作数据源
	WorkList      []flow.Work // 工作数据源：列表
	InputFile     string      // 工作数据源：文件
	ItemSeparate  string      // 工作数据源：每行元素按分隔符分的分隔符
	MinItemsCount int         // 工作数据源：每行元素最小数量
	EnableStdin   bool        // 工作数据源：stdin, 当 InputFile 不存在时使用 stdin

	MaxOperationCountPerRequest int
}

func (info *Info) Check() *data.CodeError {
	if err := info.Info.Check(); err != nil {
		return err
	}
	info.Force = true

	if info.MinItemsCount < 1 {
		info.MinItemsCount = 1
	}

	if info.MaxOperationCountPerRequest <= 0 ||
		info.MaxOperationCountPerRequest > defaultOperationCountPerRequest {
		info.MaxOperationCountPerRequest = defaultOperationCountPerRequest
	}

	if len(info.ItemSeparate) == 0 {
		info.ItemSeparate = "\t"
	}

	return nil
}

type Handler interface {
	ItemsToOperation(func(items []string) (operation Operation, err *data.CodeError)) Handler
	OnResult(func(operationInfo string, operation Operation, result *OperationResult)) Handler
	OnError(func(err *data.CodeError)) Handler
	Start()
}

func NewHandler(info Info) Handler {
	return &handler{
		info: &info,
	}
}

type handler struct {
	info                  *Info
	operationItemsCreator func(items []string) (operation Operation, err *data.CodeError)
	onError               func(err *data.CodeError)
	onResult              func(operationInfo string, operation Operation, result *OperationResult)
}

func (h *handler) ItemsToOperation(reader func(items []string) (operation Operation, err *data.CodeError)) Handler {
	h.operationItemsCreator = reader
	return h
}

func (h *handler) OnResult(handler func(operationInfo string, operation Operation, result *OperationResult)) Handler {
	h.onResult = handler
	return h
}

func (h *handler) OnError(handler func(err *data.CodeError)) Handler {
	h.onError = handler
	return h
}

func (h *handler) Start() {
	bucketManager, err := bucket.GetBucketManager()
	if err != nil {
		h.onError(err)
		return
	}

	workBuilder := flow.New(h.info.Info)
	var workerBuilder *flow.WorkerProvideBuilder
	if h.info.WorkList != nil && len(h.info.WorkList) > 0 {
		workerBuilder = workBuilder.WorkProviderWithArray(h.info.WorkList)
	} else {
		log.DebugF("forceFlag: %v, overwriteFlag: %v, worker: %v, inputFile: %q, bsuccessFname: %q, bfailureFname: %q, sep: %q",
			h.info.Force, h.info.Overwrite, h.info.WorkerCount, h.info.InputFile, h.info.SuccessExportFilePath, h.info.FailExportFilePath, h.info.ItemSeparate)

		if h.operationItemsCreator == nil {
			log.Error(data.NewEmptyError().AppendDesc(alert.CannotEmpty("operation reader", "")))
			return
		}

		workerBuilder = workBuilder.WorkProviderWithFile(h.info.InputFile,
			h.info.EnableStdin,
			flow.NewItemsWorkCreator(h.info.ItemSeparate, h.info.MinItemsCount, func(items []string) (work flow.Work, err *data.CodeError) {
				return h.operationItemsCreator(items)
			}))
	}

	workerBuilder.
		WorkerProvider(flow.NewWorkerProvider(func() (flow.Worker, *data.CodeError) {
			return flow.NewWorker(func(workInfoList []*flow.WorkInfo) ([]*flow.WorkRecord, *data.CodeError) {

				recordList := make([]*flow.WorkRecord, 0, len(workInfoList))
				operationStrings := make([]string, 0, len(workInfoList))
				for _, workInfo := range workInfoList {
					recordList = append(recordList, &flow.WorkRecord{
						WorkInfo: workInfo,
					})

					if operation, ok := workInfo.Work.(Operation); !ok {
						return nil, alert.Error("batch WorkerProvider, operation type conv error", "")
					} else {
						if operationString, e := operation.ToOperation(); e != nil {
							return nil, alert.Error("batch WorkerProvider, ToOperation error:"+e.Error(), "")
						} else {
							operationStrings = append(operationStrings, operationString)
						}
					}
				}

				resultList, e := bucketManager.Batch(operationStrings)
				if len(resultList) != len(operationStrings) {
					return recordList, data.ConvertError(e)
				}

				for i, r := range resultList {
					recordList[i].Result = &OperationResult{
						Code:     r.Code,
						Hash:     r.Data.Hash,
						FSize:    r.Data.Fsize,
						PutTime:  r.Data.PutTime,
						MimeType: r.Data.MimeType,
						Type:     r.Data.Type,
						Error:    r.Data.Error,
					}
				}
				return recordList, nil
			}), nil
		})).
		DoWorkListMaxCount(h.info.MaxOperationCountPerRequest).
		OnWorkSkip(func(work *flow.WorkInfo, err *data.CodeError) {
			log.WarningF("Skip line:%s error:%v", work.Data, err)
		}).
		OnWorkSuccess(func(work *flow.WorkInfo, result flow.Result) {
			operation, _ := work.Work.(Operation)
			operationResult, _ := result.(*OperationResult)
			h.onResult(work.Data, operation, operationResult)
		}).
		OnWorkFail(func(work *flow.WorkInfo, err *data.CodeError) {
			operation, _ := work.Work.(Operation)
			h.onResult(work.Data, operation, &OperationResult{
				Code:  data.ErrorCodeUnknown,
				Error: err.Error(),
			})
		}).Build().Start()
}
<commit_msg>change debug log: batch info<commit_after>package batch

import (
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/common/export"
	"github.com/qiniu/qshell/v2/iqshell/common/flow"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/storage/bucket"
)

type Info struct {
	flow.Info
	export.FileExporterConfig

	Overwrite bool // 是否覆盖

	// 工作数据源
	WorkList      []flow.Work // 工作数据源：列表
	InputFile     string      // 工作数据源：文件
	ItemSeparate  string      // 工作数据源：每行元素按分隔符分的分隔符
	MinItemsCount int         // 工作数据源：每行元素最小数量
	EnableStdin   bool        // 工作数据源：stdin, 当 InputFile 不存在时使用 stdin

	MaxOperationCountPerRequest int
}

func (info *Info) Check() *data.CodeError {
	if err := info.Info.Check(); err != nil {
		return err
	}
	info.Force = true

	if info.MinItemsCount < 1 {
		info.MinItemsCount = 1
	}

	if info.MaxOperationCountPerRequest <= 0 ||
		info.MaxOperationCountPerRequest > defaultOperationCountPerRequest {
		info.MaxOperationCountPerRequest = defaultOperationCountPerRequest
	}

	if len(info.ItemSeparate) == 0 {
		info.ItemSeparate = "\t"
	}

	return nil
}

type Handler interface {
	ItemsToOperation(func(items []string) (operation Operation, err *data.CodeError)) Handler
	OnResult(func(operationInfo string, operation Operation, result *OperationResult)) Handler
	OnError(func(err *data.CodeError)) Handler
	Start()
}

func NewHandler(info Info) Handler {
	return &handler{
		info: &info,
	}
}

type handler struct {
	info                  *Info
	operationItemsCreator func(items []string) (operation Operation, err *data.CodeError)
	onError               func(err *data.CodeError)
	onResult              func(operationInfo string, operation Operation, result *OperationResult)
}

func (h *handler) ItemsToOperation(reader func(items []string) (operation Operation, err *data.CodeError)) Handler {
	h.operationItemsCreator = reader
	return h
}

func (h *handler) OnResult(handler func(operationInfo string, operation Operation, result *OperationResult)) Handler {
	h.onResult = handler
	return h
}

func (h *handler) OnError(handler func(err *data.CodeError)) Handler {
	h.onError = handler
	return h
}

func (h *handler) Start() {
	bucketManager, err := bucket.GetBucketManager()
	if err != nil {
		h.onError(err)
		return
	}

	workBuilder := flow.New(h.info.Info)
	var workerBuilder *flow.WorkerProvideBuilder
	if h.info.WorkList != nil && len(h.info.WorkList) > 0 {
		workerBuilder = workBuilder.WorkProviderWithArray(h.info.WorkList)
	} else {
		log.DebugF("forceFlag: %v, overwriteFlag: %v, worker: %v, inputFile: %q, successFilePath: %q, failureFilePath: %q, sep: %q",
			h.info.Force, h.info.Overwrite, h.info.WorkerCount, h.info.InputFile, h.info.SuccessExportFilePath, h.info.FailExportFilePath, h.info.ItemSeparate)

		if h.operationItemsCreator == nil {
			log.Error(data.NewEmptyError().AppendDesc(alert.CannotEmpty("operation reader", "")))
			return
		}

		workerBuilder = workBuilder.WorkProviderWithFile(h.info.InputFile,
			h.info.EnableStdin,
			flow.NewItemsWorkCreator(h.info.ItemSeparate, h.info.MinItemsCount, func(items []string) (work flow.Work, err *data.CodeError) {
				return h.operationItemsCreator(items)
			}))
	}

	workerBuilder.
		WorkerProvider(flow.NewWorkerProvider(func() (flow.Worker, *data.CodeError) {
			return flow.NewWorker(func(workInfoList []*flow.WorkInfo) ([]*flow.WorkRecord, *data.CodeError) {

				recordList := make([]*flow.WorkRecord, 0, len(workInfoList))
				operationStrings := make([]string, 0, len(workInfoList))
				for _, workInfo := range workInfoList {
					recordList = append(recordList, &flow.WorkRecord{
						WorkInfo: workInfo,
					})

					if operation, ok := workInfo.Work.(Operation); !ok {
						return nil, alert.Error("batch WorkerProvider, operation type conv error", "")
					} else {
						if operationString, e := operation.ToOperation(); e != nil {
							return nil, alert.Error("batch WorkerProvider, ToOperation error:"+e.Error(), "")
						} else {
							operationStrings = append(operationStrings, operationString)
						}
					}
				}

				resultList, e := bucketManager.Batch(operationStrings)
				if len(resultList) != len(operationStrings) {
					return recordList, data.ConvertError(e)
				}

				for i, r := range resultList {
					recordList[i].Result = &OperationResult{
						Code:     r.Code,
						Hash:     r.Data.Hash,
						FSize:    r.Data.Fsize,
						PutTime:  r.Data.PutTime,
						MimeType: r.Data.MimeType,
						Type:     r.Data.Type,
						Error:    r.Data.Error,
					}
				}
				return recordList, nil
			}), nil
		})).
		DoWorkListMaxCount(h.info.MaxOperationCountPerRequest).
		OnWorkSkip(func(work *flow.WorkInfo, err *data.CodeError) {
			log.WarningF("Skip line:%s error:%v", work.Data, err)
		}).
		OnWorkSuccess(func(work *flow.WorkInfo, result flow.Result) {
			operation, _ := work.Work.(Operation)
			operationResult, _ := result.(*OperationResult)
			h.onResult(work.Data, operation, operationResult)
		}).
		OnWorkFail(func(work *flow.WorkInfo, err *data.CodeError) {
			operation, _ := work.Work.(Operation)
			h.onResult(work.Data, operation, &OperationResult{
				Code:  data.ErrorCodeUnknown,
				Error: err.Error(),
			})
		}).Build().Start()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/Preetam/onecontactlink/internal-api/client"
	"github.com/Preetam/onecontactlink/schema"

	"github.com/VividCortex/siesta"
)

func serveGetRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
		return
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	templ.ExecuteTemplate(w, "request", map[string]string{
		"Name": user.Name,
	})
}

func servePostRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	recaptchaResponse := params.String("g-recaptcha-response", "", "reCAPTCHA response")
	nameStr := params.String("name", "", "name")
	emailStr := params.String("email", "", "email")
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
	}

	if *recaptchaResponse == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Bad CAPTCHA",
		})
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	if *nameStr == "" || *emailStr == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "You must provide a name and email address",
		})
		return
	}

	// verify CAPTCHA
	resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{
		"secret":   []string{RecaptchaSecret},
		"response": []string{*recaptchaResponse},
	})
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	recaptchaAPIResponse := struct {
		Success bool `json:"success"`
	}{}

	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&recaptchaAPIResponse)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	if !recaptchaAPIResponse.Success {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Couldn't verify CAPTCHA. Please try again.",
		})
		return
	}

	toUser := user.ID
	fromUser := 0

	email, err := internalAPIClient.GetEmail(*emailStr)
	if err != nil {
		if err == client.ErrNotFound {
			// Email doesn't exist. Create a new user with the email.
			user, err := internalAPIClient.CreateUser(schema.NewUser(*nameStr, *emailStr))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				templ.ExecuteTemplate(w, "request", map[string]string{
					"Name":    user.Name,
					"Warning": "Something went wrong. Please try again.",
				})
				return
			}
			fromUser = user.ID
		} else {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name":    user.Name,
				"Warning": "Something went wrong. Please try again.",
			})
			return
		}
	} else {
		fromUser = email.User
	}

	requestID, err := internalAPIClient.CreateRequest(fromUser, toUser)
	if err != nil {
		if err == client.ErrConflict {
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name": user.Name,
				"Info": "Looks like you already made this request. If " + user.Name +
					" has already approved your request, we'll send you their latest contact info.",
			})
			// Try to send another request email. This is idempotent.
			internalAPIClient.SendRequestEmail(requestID)
			internalAPIClient.SendContactInfoEmail(requestID)
			return
		}
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	err = internalAPIClient.SendRequestEmail(requestID)
	if err != nil {
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Request sent!",
	})
}

func serveManageRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	actionStr := params.String("action", "", "action")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}

	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	if *actionStr != "approve" && *actionStr != "reject" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Invalid action.",
		})
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract request ID
	requestID := int(linkToken.Data["request"].(float64))
	err = internalAPIClient.ManageRequest(requestID, *actionStr)
	if err != nil {
		if serverErr, ok := err.(client.ServerError); ok {
			if int(serverErr) == http.StatusConflict {
				templ.ExecuteTemplate(w, "invalid", map[string]string{
					"Warning": "Oops, you already responded to this request. We can't change" +
						" anything right now.",
				})
				return
			}
		}

		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		log.Println(err)
		return

	}

	if *actionStr == "approve" {
		err = internalAPIClient.SendContactInfoEmail(requestID)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "invalid", map[string]string{
				"Error": "Something went wrong. Please try again.",
			})
			log.Println(err)
			return
		}

		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Approved! We'll send them an email with your contact information.",
		})
	} else {
		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Rejected. That email won't be able to send you any more requests.",
		})
	}
}

func serveAuth(c siesta.Context, w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	err := params.Parse(r.Form)
	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}
	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract user ID
	userID := int(linkToken.Data["user"].(float64))

	// get user information
	user, err := internalAPIClient.GetUser(userID)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	linkToken.Data["name"] = user.Name

	// Update the expiration and set a cookie
	linkToken.Expires = int(time.Now().Unix() + 86400)

	token, err := tokenCodec.EncodeToken(linkToken)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "ocl",
		Value:    token,
		HttpOnly: true,
		Secure:   !DevMode,
	})

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Logged in!",
	})
}
<commit_msg>set cookie path to "/"<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/Preetam/onecontactlink/internal-api/client"
	"github.com/Preetam/onecontactlink/schema"

	"github.com/VividCortex/siesta"
)

func serveGetRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
		return
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	templ.ExecuteTemplate(w, "request", map[string]string{
		"Name": user.Name,
	})
}

func servePostRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	recaptchaResponse := params.String("g-recaptcha-response", "", "reCAPTCHA response")
	nameStr := params.String("name", "", "name")
	emailStr := params.String("email", "", "email")
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
	}

	if *recaptchaResponse == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Bad CAPTCHA",
		})
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	if *nameStr == "" || *emailStr == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "You must provide a name and email address",
		})
		return
	}

	// verify CAPTCHA
	resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{
		"secret":   []string{RecaptchaSecret},
		"response": []string{*recaptchaResponse},
	})
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	recaptchaAPIResponse := struct {
		Success bool `json:"success"`
	}{}

	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&recaptchaAPIResponse)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	if !recaptchaAPIResponse.Success {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Couldn't verify CAPTCHA. Please try again.",
		})
		return
	}

	toUser := user.ID
	fromUser := 0

	email, err := internalAPIClient.GetEmail(*emailStr)
	if err != nil {
		if err == client.ErrNotFound {
			// Email doesn't exist. Create a new user with the email.
			user, err := internalAPIClient.CreateUser(schema.NewUser(*nameStr, *emailStr))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				templ.ExecuteTemplate(w, "request", map[string]string{
					"Name":    user.Name,
					"Warning": "Something went wrong. Please try again.",
				})
				return
			}
			fromUser = user.ID
		} else {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name":    user.Name,
				"Warning": "Something went wrong. Please try again.",
			})
			return
		}
	} else {
		fromUser = email.User
	}

	requestID, err := internalAPIClient.CreateRequest(fromUser, toUser)
	if err != nil {
		if err == client.ErrConflict {
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name": user.Name,
				"Info": "Looks like you already made this request. If " + user.Name +
					" has already approved your request, we'll send you their latest contact info.",
			})
			// Try to send another request email. This is idempotent.
			internalAPIClient.SendRequestEmail(requestID)
			internalAPIClient.SendContactInfoEmail(requestID)
			return
		}
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	err = internalAPIClient.SendRequestEmail(requestID)
	if err != nil {
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Request sent!",
	})
}

func serveManageRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	actionStr := params.String("action", "", "action")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}

	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	if *actionStr != "approve" && *actionStr != "reject" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Invalid action.",
		})
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract request ID
	requestID := int(linkToken.Data["request"].(float64))
	err = internalAPIClient.ManageRequest(requestID, *actionStr)
	if err != nil {
		if serverErr, ok := err.(client.ServerError); ok {
			if int(serverErr) == http.StatusConflict {
				templ.ExecuteTemplate(w, "invalid", map[string]string{
					"Warning": "Oops, you already responded to this request. We can't change" +
						" anything right now.",
				})
				return
			}
		}

		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		log.Println(err)
		return

	}

	if *actionStr == "approve" {
		err = internalAPIClient.SendContactInfoEmail(requestID)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "invalid", map[string]string{
				"Error": "Something went wrong. Please try again.",
			})
			log.Println(err)
			return
		}

		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Approved! We'll send them an email with your contact information.",
		})
	} else {
		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Rejected. That email won't be able to send you any more requests.",
		})
	}
}

func serveAuth(c siesta.Context, w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	err := params.Parse(r.Form)
	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}
	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract user ID
	userID := int(linkToken.Data["user"].(float64))

	// get user information
	user, err := internalAPIClient.GetUser(userID)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	linkToken.Data["name"] = user.Name

	// Update the expiration and set a cookie
	linkToken.Expires = int(time.Now().Unix() + 86400)

	token, err := tokenCodec.EncodeToken(linkToken)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "ocl",
		Value:    token,
		Path:     "/",
		HttpOnly: true,
		Secure:   !DevMode,
	})

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Logged in!",
	})
}
<|endoftext|>


<commit_before>package main

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

	"github.com/elsonwu/goio"
	"github.com/go-martini/martini"
)

var flagHost = flag.String("host", "127.0.0.1:9999", "the default server host")
var flagSSLHost = flag.String("sslhost", "", "the server host for https, it will override the host setting")
var flagAllowOrigin = flag.String("alloworigin", "", "the host allow to cross site ajax")
var flagDebug = flag.Bool("debug", false, "enable debug mode or not")
var flagClientLifeCycle = flag.Int64("lifecycle", 30, "how many seconds of the client life cycle")
var flagClientMessageTimeout = flag.Int64("messagetimeout", 15, "how many seconds of the client keep waiting for new messages")
var flagEnableHttps = flag.Bool("enablehttps", false, "enable https or not")
var flagDisableHttp = flag.Bool("disablehttp", false, "disable http and use https only")
var flagCertFile = flag.String("certfile", "", "certificate file path")
var flagKeyFile = flag.String("keyfile", "", "private file path")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	goio.Debug = *flagDebug
	goio.LifeCycle = *flagClientLifeCycle

	clients := goio.GlobalClients()
	rooms := goio.GlobalRooms()
	users := goio.GlobalUsers()

	if *flagDebug {
		go func() {
			for {
				time.Sleep(3 * time.Second)
				log.Printf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())
			}
		}()
	}

	martini.Env = martini.Dev
	router := martini.NewRouter()
	mart := martini.New()
	mart.Action(router.Handle)
	m := &martini.ClassicMartini{mart, router}
	m.Use(martini.Recovery())
	m.Use(func(res http.ResponseWriter) {
		res.Header().Set("Content-Type", "text/plain; charset=utf-8")
		res.Header().Set("Access-Control-Allow-Credentials", "true")
		res.Header().Set("Access-Control-Allow-Methods", "GET,POST")
		if "" != *flagAllowOrigin {
			allowOrigins := strings.Split(*flagAllowOrigin, ",")
			for _, allowOrigin := range allowOrigins {
				res.Header().Add("Access-Control-Allow-Origin", allowOrigin)
			}
		}
	})

	if *flagDebug {
		m.Get("/test", func() string {

			st := time.Now().Unix()
			for i := 0; i < 10000; i++ {
				userId := strconv.Itoa(i)
				user := users.Get(userId)
				if user == nil {
					user = goio.NewUser(userId)
				}

				clt, done := goio.NewClient()
				user.Add(clt)
				done <- true

				room := rooms.Get(strconv.Itoa(i%1000), true)
				room.Add(user)
			}

			return strconv.Itoa(int(time.Now().Unix()-st)) + " seconds"
		})
	}

	m.Get("/count", func(req *http.Request) string {
		res := ""
		res += fmt.Sprintf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())

		if "1" == req.URL.Query().Get("detail") {
			res += fmt.Sprintf("-------------------------------\n")

			rooms.Each(func(room *goio.Room) {
				res += fmt.Sprintf("# room id: %s \n", room.Id)
				for userId, _ := range room.UserIds.Map {
					res += fmt.Sprintf(" - user id: %s \n", userId)
				}
				res += fmt.Sprintf("\n")
			})

			res += fmt.Sprintf("-------------------------------\n")

			users.Each(func(user *goio.User) {
				res += fmt.Sprintf("# user id: %s \n", user.Id)
				for clientId, _ := range user.ClientIds.Map {
					res += fmt.Sprintf(" - client id: %s \n", clientId)
				}
				res += fmt.Sprintf("\n")
			})
		}

		return res
	})

	getRoomUsersFn := func(params martini.Params, req *http.Request) (int, string) {
		roomId := params["room_id"]
		if roomId == "" {
			return 403, "room_id is missing"
		}

		room := rooms.Get(roomId, false)
		if room == nil {
			return 200, ""
		}

		return 200, strings.Join(room.UserIds.Array(), ",")
	}

	// alias of /room/users, will be removed later
	m.Get("/room_users/:room_id", getRoomUsersFn)
	m.Get("/room/users/:room_id", getRoomUsersFn)

	getUserDataFn := func(params martini.Params, req *http.Request) (int, string) {
		userId := params["user_id"]
		if userId == "" {
			return 403, "user_id is missing"
		}

		key := params["key"]
		if key == "" {
			return 403, "key is missing"
		}

		user := users.Get(userId)
		if user == nil {
			return 200, ""
		}

		return 200, user.Data().Get(key)
	}

	m.Get("/user/data/:user_id/:key", getUserDataFn)
	// alias of /user/data, will be removed later
	m.Get("/get_data/:user_id/:key", getUserDataFn)

	setUserDataFn := func(params martini.Params, req *http.Request) (int, string) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			return 500, err.Error()
		}

		clientId := params["client_id"]
		if clientId == "" {
			return 403, "client_id is missing"
		}

		key := params["key"]
		if key == "" {
			return 403, "key is missing"
		}

		clt := clients.Get(clientId)
		if clt != nil && clt.UserId != "" {
			user := users.Get(clt.UserId)
			if user != nil {
				user.Data().Set(key, string(val))
			}
		}

		return 200, ""
	}

	m.Post("/user/data/:client_id/:key", setUserDataFn)
	// alias of /user/data, will be removed later
	m.Post("/set_data/:client_id/:key", setUserDataFn)

	m.Post("/online_status", func(params martini.Params, req *http.Request) (int, string) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			return 500, err.Error()
		}

		userIds := strings.Split(string(val), ",")
		status := ""
		for _, userId := range userIds {
			if nil == users.Get(userId) {
				status += "0,"
			} else {
				status += "1,"
			}
		}

		return 200, status
	})

	m.Post("/client/:user_id", func(params martini.Params, req *http.Request) (int, string) {
		userId := params["user_id"]
		user := users.Get(userId)
		if user == nil {
			user = goio.NewUser(userId)
		}

		clt, done := goio.NewClient()
		user.Add(clt)
		done <- true

		return 200, clt.Id
	})

	m.Get("/kill_client/:client_id", func(params martini.Params, req *http.Request) (int, string) {
		id := params["client_id"]
		clt := clients.Get(id)
		if clt != nil {
			clt.Destroy()
		}

		return 204, ""
	})

	m.Get("/message/:client_id", func(params martini.Params, req *http.Request) (int, string) {
		id := params["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			return 404, fmt.Sprintf("Client %s does not exist\n", id)
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()

		timeNow := time.Now().Unix()
		for {
			if 0 < len(clt.Messages) {
				msgs, err := json.Marshal(clt.Messages)
				if err != nil {
					return 500, err.Error()
				}

				clt.CleanMessages()
				return 200, string(msgs)
			}

			if time.Now().Unix() > timeNow+*flagClientMessageTimeout {
				break
			}

			time.Sleep(100000 * time.Microsecond)
		}

		return 204, ""
	})

	m.Post("/message/:client_id", func(params martini.Params, req *http.Request) (int, string) {
		id := params["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			return 403, fmt.Sprintf("Client %s does not exist\n", id)
		}

		user := users.Get(clt.UserId)
		if user == nil {
			return 403, fmt.Sprintf("Client %s does not connect with any user\n", id)
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()
		defer req.Body.Close()

		message := &goio.Message{}
		json.NewDecoder(req.Body).Decode(message)
		message.CallerId = clt.UserId
		go func(message *goio.Message) {
			if *flagDebug {
				log.Printf("post message: %#v", *message)
			}

			if message.RoomId == "" && (message.EventName == "join" || message.EventName == "leave") {
				clt.Receive(&goio.Message{
					EventName: "error",
					Data:      "room id is missing",
				})

				return
			}

			// We change CallerId as current user
			message.CallerId = user.Id
			message.ClientId = clt.Id
			user.Emit(message.EventName, message)
		}(message)

		return 204, ""
	})

	m.Options("/.*", func(req *http.Request) {})

	host := *flagHost
	if !*flagEnableHttps && *flagDisableHttp {
		log.Fatalln("You cannot disable http but not enable https in the same time")
	}

	//Prevent exiting
	ch := make(chan bool)

	if !*flagDisableHttp {
		go func() {
			log.Println("Serve at " + host + " - http")
			if err := http.ListenAndServe(host, m); err != nil {
				log.Println(err)
			}
		}()
	}

	if *flagEnableHttps {
		go func() {
			if *flagSSLHost != "" {
				host = *flagSSLHost
			}

			log.Println("Serve at " + host + " - https")
			if err := http.ListenAndServeTLS(host, *flagCertFile, *flagKeyFile, m); err != nil {
				log.Println(err)
			}
		}()
	}

	<-ch
}
<commit_msg>remove old code<commit_after>package main

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

	"github.com/elsonwu/goio"
	"github.com/go-martini/martini"
)

var flagHost = flag.String("host", "127.0.0.1:9999", "the default server host")
var flagSSLHost = flag.String("sslhost", "", "the server host for https, it will override the host setting")
var flagAllowOrigin = flag.String("alloworigin", "", "the host allow to cross site ajax")
var flagDebug = flag.Bool("debug", false, "enable debug mode or not")
var flagClientLifeCycle = flag.Int64("lifecycle", 30, "how many seconds of the client life cycle")
var flagClientMessageTimeout = flag.Int64("messagetimeout", 15, "how many seconds of the client keep waiting for new messages")
var flagEnableHttps = flag.Bool("enablehttps", false, "enable https or not")
var flagDisableHttp = flag.Bool("disablehttp", false, "disable http and use https only")
var flagCertFile = flag.String("certfile", "", "certificate file path")
var flagKeyFile = flag.String("keyfile", "", "private file path")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	goio.Debug = *flagDebug
	goio.LifeCycle = *flagClientLifeCycle

	clients := goio.GlobalClients()
	rooms := goio.GlobalRooms()
	users := goio.GlobalUsers()

	if *flagDebug {
		go func() {
			for {
				time.Sleep(3 * time.Second)
				log.Printf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())
			}
		}()
	}

	martini.Env = martini.Dev
	router := martini.NewRouter()
	mart := martini.New()
	mart.Action(router.Handle)
	m := &martini.ClassicMartini{mart, router}
	m.Use(martini.Recovery())
	m.Use(func(res http.ResponseWriter) {
		res.Header().Set("Content-Type", "text/plain; charset=utf-8")
		res.Header().Set("Access-Control-Allow-Credentials", "true")
		res.Header().Set("Access-Control-Allow-Methods", "GET,POST")
		if "" != *flagAllowOrigin {
			allowOrigins := strings.Split(*flagAllowOrigin, ",")
			for _, allowOrigin := range allowOrigins {
				res.Header().Add("Access-Control-Allow-Origin", allowOrigin)
			}
		}
	})

	if *flagDebug {
		m.Get("/test", func() string {

			st := time.Now().Unix()
			for i := 0; i < 10000; i++ {
				userId := strconv.Itoa(i)
				user := users.Get(userId)
				if user == nil {
					user = goio.NewUser(userId)
				}

				clt, done := goio.NewClient()
				user.Add(clt)
				done <- true

				room := rooms.Get(strconv.Itoa(i%1000), true)
				room.Add(user)
			}

			return strconv.Itoa(int(time.Now().Unix()-st)) + " seconds"
		})
	}

	m.Get("/count", func(req *http.Request) string {
		res := ""
		res += fmt.Sprintf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())

		if "1" == req.URL.Query().Get("detail") {
			res += fmt.Sprintf("-------------------------------\n")

			rooms.Each(func(room *goio.Room) {
				res += fmt.Sprintf("# room id: %s \n", room.Id)
				for userId, _ := range room.UserIds.Map {
					res += fmt.Sprintf(" - user id: %s \n", userId)
				}
				res += fmt.Sprintf("\n")
			})

			res += fmt.Sprintf("-------------------------------\n")

			users.Each(func(user *goio.User) {
				res += fmt.Sprintf("# user id: %s \n", user.Id)
				for clientId, _ := range user.ClientIds.Map {
					res += fmt.Sprintf(" - client id: %s \n", clientId)
				}
				res += fmt.Sprintf("\n")
			})
		}

		return res
	})

	m.Get("/room/users/:room_id", func(params martini.Params, req *http.Request) (int, string) {
		roomId := params["room_id"]
		if roomId == "" {
			return 403, "room_id is missing"
		}

		room := rooms.Get(roomId, false)
		if room == nil {
			return 200, ""
		}

		return 200, strings.Join(room.UserIds.Array(), ",")
	})

	m.Get("/user/data/:user_id/:key", func(params martini.Params, req *http.Request) (int, string) {
		userId := params["user_id"]
		if userId == "" {
			return 403, "user_id is missing"
		}

		key := params["key"]
		if key == "" {
			return 403, "key is missing"
		}

		user := users.Get(userId)
		if user == nil {
			return 200, ""
		}

		return 200, user.Data().Get(key)
	})

	m.Post("/user/data/:client_id/:key", func(params martini.Params, req *http.Request) (int, string) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			return 500, err.Error()
		}

		clientId := params["client_id"]
		if clientId == "" {
			return 403, "client_id is missing"
		}

		key := params["key"]
		if key == "" {
			return 403, "key is missing"
		}

		clt := clients.Get(clientId)
		if clt != nil && clt.UserId != "" {
			user := users.Get(clt.UserId)
			if user != nil {
				user.Data().Set(key, string(val))
			}
		}

		return 200, ""
	})

	m.Post("/online_status", func(params martini.Params, req *http.Request) (int, string) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			return 500, err.Error()
		}

		userIds := strings.Split(string(val), ",")
		status := ""
		for _, userId := range userIds {
			if nil == users.Get(userId) {
				status += "0,"
			} else {
				status += "1,"
			}
		}

		return 200, status
	})

	m.Post("/client/:user_id", func(params martini.Params, req *http.Request) (int, string) {
		userId := params["user_id"]
		user := users.Get(userId)
		if user == nil {
			user = goio.NewUser(userId)
		}

		clt, done := goio.NewClient()
		user.Add(clt)
		done <- true

		return 200, clt.Id
	})

	m.Get("/kill_client/:client_id", func(params martini.Params, req *http.Request) (int, string) {
		id := params["client_id"]
		clt := clients.Get(id)
		if clt != nil {
			clt.Destroy()
		}

		return 204, ""
	})

	m.Get("/message/:client_id", func(params martini.Params, req *http.Request) (int, string) {
		id := params["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			return 404, fmt.Sprintf("Client %s does not exist\n", id)
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()

		timeNow := time.Now().Unix()
		for {
			if 0 < len(clt.Messages) {
				msgs, err := json.Marshal(clt.Messages)
				if err != nil {
					return 500, err.Error()
				}

				clt.CleanMessages()
				return 200, string(msgs)
			}

			if time.Now().Unix() > timeNow+*flagClientMessageTimeout {
				break
			}

			time.Sleep(100000 * time.Microsecond)
		}

		return 204, ""
	})

	m.Post("/message/:client_id", func(params martini.Params, req *http.Request) (int, string) {
		id := params["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			return 403, fmt.Sprintf("Client %s does not exist\n", id)
		}

		user := users.Get(clt.UserId)
		if user == nil {
			return 403, fmt.Sprintf("Client %s does not connect with any user\n", id)
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()
		defer req.Body.Close()

		message := &goio.Message{}
		json.NewDecoder(req.Body).Decode(message)
		message.CallerId = clt.UserId
		go func(message *goio.Message) {
			if *flagDebug {
				log.Printf("post message: %#v", *message)
			}

			if message.RoomId == "" && (message.EventName == "join" || message.EventName == "leave") {
				clt.Receive(&goio.Message{
					EventName: "error",
					Data:      "room id is missing",
				})

				return
			}

			// We change CallerId as current user
			message.CallerId = user.Id
			message.ClientId = clt.Id
			user.Emit(message.EventName, message)
		}(message)

		return 204, ""
	})

	m.Options("/.*", func(req *http.Request) {})

	host := *flagHost
	if !*flagEnableHttps && *flagDisableHttp {
		log.Fatalln("You cannot disable http but not enable https in the same time")
	}

	//Prevent exiting
	ch := make(chan bool)

	if !*flagDisableHttp {
		go func() {
			log.Println("Serve at " + host + " - http")
			if err := http.ListenAndServe(host, m); err != nil {
				log.Println(err)
			}
		}()
	}

	if *flagEnableHttps {
		go func() {
			if *flagSSLHost != "" {
				host = *flagSSLHost
			}

			log.Println("Serve at " + host + " - https")
			if err := http.ListenAndServeTLS(host, *flagCertFile, *flagKeyFile, m); err != nil {
				log.Println(err)
			}
		}()
	}

	<-ch
}
<|endoftext|>