<commit_before>package rest

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// Resource represents a domain model.
type Resource interface{}

// Payload is the unmarshalled request body.
type Payload map[string]interface{}

// ResourceHandler specifies the endpoint handlers for working with a resource. This
// consists of the business logic for performing CRUD operations.
type ResourceHandler interface {
	ResourceName() string
	CreateResource(RequestContext, Payload, string) (Resource, error)
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)
	ReadResource(RequestContext, string, string) (Resource, error)
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)
	DeleteResource(RequestContext, string, string) (Resource, error)
	Authenticate(http.Request) error
}

// BaseResourceHandler is a base implementation of ResourceHandler with stubs for the
// CRUD operations. This allows ResourceHandler implementations to only implement
// what they need.
type BaseResourceHandler struct{}

// ResourceName is a stub. It must be implemented.
func (b BaseResourceHandler) ResourceName() string {
	panic("ResourceName not implemented")
}

// CreateResource is a stub. Implement if necessary.
func (b BaseResourceHandler) CreateResource(ctx RequestContext, data Payload,
	version string) (Resource, error) {
	panic("CreateResource not implemented")
}

// ReadResourceList is a stub. Implement if necessary.
func (b BaseResourceHandler) ReadResourceList(ctx RequestContext, limit int,
	cursor string, version string) ([]Resource, string, error) {
	panic("ReadResourceList not implemented")
}

// ReadResource is a stub. Implement if necessary.
func (b BaseResourceHandler) ReadResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	panic("ReadResource not implemented")
}

// UpdateResource is a stub. Implement if necessary.
func (b BaseResourceHandler) UpdateResource(ctx RequestContext, id string,
	data Payload, version string) (Resource, error) {
	panic("UpdateResource not implemented")
}

// DeleteResource is a stub. Implement if necessary.
func (b BaseResourceHandler) DeleteResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	panic("DeleteResource not implemented")
}

// Authenticate is the default authentication logic. All requests are authorized.
// Implement custom authentication logic if necessary.
func (b BaseResourceHandler) Authenticate(r http.Request) error {
	return nil
}

// requestHandler constructs http.HandlerFuncs responsible for handling HTTP requests.
type requestHandler struct {
	API
}

// handleCreate returns a HandlerFunc which will deserialize the request payload, pass
// it to the provided create function, and then serialize and dispatch the response.
// The serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleCreate(createFunc func(RequestContext, Payload,
	string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		decoder := json.NewDecoder(r.Body)
		var data map[string]interface{}
		if err := decoder.Decode(&data); err != nil {
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			resource, err := createFunc(ctx, data, ctx.Version())
			ctx = ctx.setResult(resource)
			ctx = ctx.setStatus(http.StatusCreated)
			if err != nil {
				ctx = ctx.setError(err)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleReadList returns a HandlerFunc which will pass the request context to the
// provided read function and then serialize and dispatch the response. The
// serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleReadList(readFunc func(RequestContext, int,
	string, string) ([]Resource, string, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		resources, cursor, err := readFunc(ctx, ctx.Limit(), ctx.Cursor(), ctx.Version())
		ctx = ctx.setResult(resources)
		ctx = ctx.setCursor(cursor)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleRead returns a HandlerFunc which will pass the resource id to the provided
// read function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleRead(readFunc func(RequestContext, string,
	string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		resource, err := readFunc(ctx, ctx.ResourceID(), ctx.Version())
		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleUpdate returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdate(updateFunc func(RequestContext,
	string, Payload, string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		decoder := json.NewDecoder(r.Body)
		var data map[string]interface{}
		if err := decoder.Decode(&data); err != nil {
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			resource, err := updateFunc(ctx, ctx.ResourceID(), data, ctx.Version())
			ctx = ctx.setResult(resource)
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusOK)
		}

		h.sendResponse(w, ctx)
	}
}

// handleDelete returns a HandlerFunc which will pass the resource id to the provided
// delete function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleDelete(deleteFunc func(RequestContext, string,
	string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		resource, err := deleteFunc(ctx, ctx.ResourceID(), ctx.Version())
		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// sendResponse writes a success or error response to the provided http.ResponseWriter
// based on the contents of the RequestContext.
func (h requestHandler) sendResponse(w http.ResponseWriter, ctx RequestContext) {
	status := ctx.Status()
	requestError := ctx.Error()
	result := ctx.Result()
	format := ctx.ResponseFormat()

	serializer, err := h.responseSerializer(format)
	if err != nil {
		// Fall back to json serialization.
		serializer = jsonSerializer{}
		requestError = NotImplemented(fmt.Sprintf("Format not implemented: %s", format))
	}

	if requestError != nil {
		if status < 400 {
			if restError, ok := requestError.(Error); ok {
				status = restError.Status()
			} else {
				status = http.StatusInternalServerError
			}
		}
		serializer.SendErrorResponse(w, requestError, status)
		return
	}

	nextURL, _ := ctx.NextURL()
	serializer.SendSuccessResponse(w, NewSuccessResponse(result, nextURL), status)
}
<commit_msg>Add docstrs to ResourceHandler interface declarations<commit_after>package rest

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// Resource represents a domain model.
type Resource interface{}

// Payload is the unmarshalled request body.
type Payload map[string]interface{}

// ResourceHandler specifies the endpoint handlers for working with a resource. This
// consists of the business logic for performing CRUD operations.
type ResourceHandler interface {
	// ResourceName is used to identify what resource a handler corresponds to and is
	// used in the endpoint URLs, i.e. /api/:version/resourceName.
	ResourceName() string

	// CreateResource is the logic that corresponds to creating a new resource at
	// POST /api/:version/resourceName. Typically, this would insert a record into a
	// database. It returns the newly created resource or an error if the create failed.
	CreateResource(RequestContext, Payload, string) (Resource, error)

	// ReadResourceList is the logic that corresponds to reading multiple resources,
	// perhaps with specified query parameters accessed through the RequestContext. This
	// is mapped to GET /api/:version/resourceName. Typically, this would make some sort
	// of database query to fetch the resources. It returns the slice of results, a
	// cursor (or empty) string, and error (or nil).
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)

	// ReadResource is the logic that corresponds to reading a single resource by its ID
	// at GET /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database query to load the resource. If the resource doesn't exist, nil should be
	// returned along with an appropriate error.
	ReadResource(RequestContext, string, string) (Resource, error)

	// UpdateResource is the logic that corresponds to updating an existing resource at
	// PUT /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database update call. It returns the updated resource or an error if the update
	// failed.
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)

	// DeleteResource is the logic that corresponds to deleting an existing resource at
	// DELETE /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database delete call. It returns the deleted resource or an error if the delete
	// failed.
	DeleteResource(RequestContext, string, string) (Resource, error)

	// Authenticate is logic that is used to authenticate requests. The default behavior
	// of Authenticate, seen in BaseResourceHandler, always returns nil, meaning all
	// requests are authenticated. Returning an error means that the request is
	// unauthorized and any error message will be sent back with the response.
	Authenticate(http.Request) error
}

// BaseResourceHandler is a base implementation of ResourceHandler with stubs for the
// CRUD operations. This allows ResourceHandler implementations to only implement
// what they need.
type BaseResourceHandler struct{}

// ResourceName is a stub. It must be implemented.
func (b BaseResourceHandler) ResourceName() string {
	panic("ResourceName not implemented")
}

// CreateResource is a stub. Implement if necessary.
func (b BaseResourceHandler) CreateResource(ctx RequestContext, data Payload,
	version string) (Resource, error) {
	panic("CreateResource not implemented")
}

// ReadResourceList is a stub. Implement if necessary.
func (b BaseResourceHandler) ReadResourceList(ctx RequestContext, limit int,
	cursor string, version string) ([]Resource, string, error) {
	panic("ReadResourceList not implemented")
}

// ReadResource is a stub. Implement if necessary.
func (b BaseResourceHandler) ReadResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	panic("ReadResource not implemented")
}

// UpdateResource is a stub. Implement if necessary.
func (b BaseResourceHandler) UpdateResource(ctx RequestContext, id string,
	data Payload, version string) (Resource, error) {
	panic("UpdateResource not implemented")
}

// DeleteResource is a stub. Implement if necessary.
func (b BaseResourceHandler) DeleteResource(ctx RequestContext, id string,
	version string) (Resource, error) {
	panic("DeleteResource not implemented")
}

// Authenticate is the default authentication logic. All requests are authorized.
// Implement custom authentication logic if necessary.
func (b BaseResourceHandler) Authenticate(r http.Request) error {
	return nil
}

// requestHandler constructs http.HandlerFuncs responsible for handling HTTP requests.
type requestHandler struct {
	API
}

// handleCreate returns a HandlerFunc which will deserialize the request payload, pass
// it to the provided create function, and then serialize and dispatch the response.
// The serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleCreate(createFunc func(RequestContext, Payload,
	string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		decoder := json.NewDecoder(r.Body)
		var data map[string]interface{}
		if err := decoder.Decode(&data); err != nil {
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			resource, err := createFunc(ctx, data, ctx.Version())
			ctx = ctx.setResult(resource)
			ctx = ctx.setStatus(http.StatusCreated)
			if err != nil {
				ctx = ctx.setError(err)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleReadList returns a HandlerFunc which will pass the request context to the
// provided read function and then serialize and dispatch the response. The
// serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleReadList(readFunc func(RequestContext, int,
	string, string) ([]Resource, string, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		resources, cursor, err := readFunc(ctx, ctx.Limit(), ctx.Cursor(), ctx.Version())
		ctx = ctx.setResult(resources)
		ctx = ctx.setCursor(cursor)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleRead returns a HandlerFunc which will pass the resource id to the provided
// read function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleRead(readFunc func(RequestContext, string,
	string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		resource, err := readFunc(ctx, ctx.ResourceID(), ctx.Version())
		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleUpdate returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdate(updateFunc func(RequestContext,
	string, Payload, string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		decoder := json.NewDecoder(r.Body)
		var data map[string]interface{}
		if err := decoder.Decode(&data); err != nil {
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			resource, err := updateFunc(ctx, ctx.ResourceID(), data, ctx.Version())
			ctx = ctx.setResult(resource)
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusOK)
		}

		h.sendResponse(w, ctx)
	}
}

// handleDelete returns a HandlerFunc which will pass the resource id to the provided
// delete function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleDelete(deleteFunc func(RequestContext, string,
	string) (Resource, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)

		resource, err := deleteFunc(ctx, ctx.ResourceID(), ctx.Version())
		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// sendResponse writes a success or error response to the provided http.ResponseWriter
// based on the contents of the RequestContext.
func (h requestHandler) sendResponse(w http.ResponseWriter, ctx RequestContext) {
	status := ctx.Status()
	requestError := ctx.Error()
	result := ctx.Result()
	format := ctx.ResponseFormat()

	serializer, err := h.responseSerializer(format)
	if err != nil {
		// Fall back to json serialization.
		serializer = jsonSerializer{}
		requestError = NotImplemented(fmt.Sprintf("Format not implemented: %s", format))
	}

	if requestError != nil {
		if status < 400 {
			if restError, ok := requestError.(Error); ok {
				status = restError.Status()
			} else {
				status = http.StatusInternalServerError
			}
		}
		serializer.SendErrorResponse(w, requestError, status)
		return
	}

	nextURL, _ := ctx.NextURL()
	serializer.SendSuccessResponse(w, NewSuccessResponse(result, nextURL), status)
}
<|endoftext|>


<commit_before>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

package service

import (
	"fmt"
	"log"
)

// SessionPool holds sessions to be used to access services.
type SessionPool struct {
	// Size is the maximum number of sessions to cache.
	Size int
	// InfoPath is the path to the Info service to be used.
	InfoPath string
	data     chan *DataClient
	info     chan *InfoClient
	mail     chan *MailClient
}

// NewSessionPool returns a new session pool.
func NewSessionPool(size int, infoPath string) *SessionPool {
	pool := &SessionPool{Size: size, InfoPath: infoPath}
	pool.data = make(chan *DataClient, size)
	pool.info = make(chan *InfoClient, size)
	pool.mail = make(chan *MailClient, size)
	return pool
}

// New returns a session from the pool.
func (s *SessionPool) New() (*Session, error) {
	session := &Session{pool: s}
	select {
	case session.info = <-s.info:
	default:
		info, err := NewInfoConnection(s.InfoPath)
		if err != nil {
			return nil, fmt.Errorf("service: Could not create Info client: %v", err)
		}
		session.info = info
	}
	return session, nil
}

// Free puts a session back to the pool.
func (s *SessionPool) Free(session *Session) {
	if session.data != nil {
		select {
		case s.data <- session.data:
		default:
		}
	}
	if session.info != nil {
		select {
		case s.info <- session.info:
		default:
		}
	}
	if session.mail != nil {
		select {
		case s.mail <- session.mail:
		default:
		}
	}
}

// Session holds connections to the services.
type Session struct {
	info *InfoClient
	data *DataClient
	mail *MailClient
	pool *SessionPool
}

// Info returns an InfoClient.
func (s *Session) Info() *InfoClient {
	return s.info
}

// Data returns a DataClient.
func (s *Session) Data() *DataClient {
	if s.data != nil {
		return s.data
	}
	select {
	case s.data = <-s.pool.data:
	default:
		data, err := s.info.FindDataService()
		s.data = data
		if err != nil {
			s.data = NewDataClient()
			s.data.Error = err
		}
	}
	return s.data
}

// Mail returns a MailClient.
func (s *Session) Mail() *MailClient {
	log.Printf("Get Mail Service")
	if s.mail != nil {
		return s.mail
	}
	select {
	case s.mail = <-s.pool.mail:
		log.Printf("Get OLD Mail Service")
	default:
		log.Printf("Get NEW Mail Service")
		mail, err := s.info.FindMailService()
		log.Printf("Got mail Service: %v %v", mail, err)
		s.mail = mail
		if err != nil {
			s.mail = NewMailClient()
			s.mail.Error = err
		}
	}
	log.Printf("Return mail Service: %v", s.mail)
	return s.mail
}
<commit_msg>Remove debug messages.<commit_after>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

package service

import "fmt"

// SessionPool holds sessions to be used to access services.
type SessionPool struct {
	// Size is the maximum number of sessions to cache.
	Size int
	// InfoPath is the path to the Info service to be used.
	InfoPath string
	data     chan *DataClient
	info     chan *InfoClient
	mail     chan *MailClient
}

// NewSessionPool returns a new session pool.
func NewSessionPool(size int, infoPath string) *SessionPool {
	pool := &SessionPool{Size: size, InfoPath: infoPath}
	pool.data = make(chan *DataClient, size)
	pool.info = make(chan *InfoClient, size)
	pool.mail = make(chan *MailClient, size)
	return pool
}

// New returns a session from the pool.
func (s *SessionPool) New() (*Session, error) {
	session := &Session{pool: s}
	select {
	case session.info = <-s.info:
	default:
		info, err := NewInfoConnection(s.InfoPath)
		if err != nil {
			return nil, fmt.Errorf("service: Could not create Info client: %v", err)
		}
		session.info = info
	}
	return session, nil
}

// Free puts a session back to the pool.
func (s *SessionPool) Free(session *Session) {
	if session.data != nil {
		select {
		case s.data <- session.data:
		default:
		}
	}
	if session.info != nil {
		select {
		case s.info <- session.info:
		default:
		}
	}
	if session.mail != nil {
		select {
		case s.mail <- session.mail:
		default:
		}
	}
}

// Session holds connections to the services.
type Session struct {
	info *InfoClient
	data *DataClient
	mail *MailClient
	pool *SessionPool
}

// Info returns an InfoClient.
func (s *Session) Info() *InfoClient {
	return s.info
}

// Data returns a DataClient.
func (s *Session) Data() *DataClient {
	if s.data != nil {
		return s.data
	}
	select {
	case s.data = <-s.pool.data:
	default:
		data, err := s.info.FindDataService()
		s.data = data
		if err != nil {
			s.data = NewDataClient()
			s.data.Error = err
		}
	}
	return s.data
}

// Mail returns a MailClient.
func (s *Session) Mail() *MailClient {
	if s.mail != nil {
		return s.mail
	}
	select {
	case s.mail = <-s.pool.mail:
	default:
		mail, err := s.info.FindMailService()
		s.mail = mail
		if err != nil {
			s.mail = NewMailClient()
			s.mail.Error = err
		}
	}
	return s.mail
}
<|endoftext|>


<commit_before>// Copyright 2015 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package wares

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/ursiform/bear"
	"github.com/ursiform/forest"
	"net/http"
)

func SessionDel(app *forest.App, manager SessionManager) bear.HandlerFunc {
	sessionDel := func(
		_ http.ResponseWriter, _ *http.Request, ctx *bear.Context) {
		sessionID, ok := ctx.Get(forest.SessionID).(string)
		if !ok {
			err := fmt.Errorf("SessionDel %s: %v",
				forest.SessionID, ctx.Get(forest.SessionID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		userID, ok := ctx.Get(forest.SessionUserID).(string)
		if !ok {
			err := fmt.Errorf("SessionDel %s: %v",
				forest.SessionUserID, ctx.Get(forest.SessionUserID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		if err := manager.Delete(sessionID, userID); err != nil {
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		ctx.Next()
	}
	return bear.HandlerFunc(sessionDel)
}

func SessionGet(app *forest.App, manager SessionManager) bear.HandlerFunc {
	sessionGet := func(
		_ http.ResponseWriter, _ *http.Request, ctx *bear.Context) {
		cookieName := forest.SessionID
		createEmptySession := func(sessionID string) {
			path := app.CookiePath
			if path == "" {
				path = "/"
			}
			cookieValue := sessionID
			duration := app.Duration("Cookie")
			// Reset the cookie.
			app.SetCookie(ctx, path, cookieName, cookieValue, duration)
			manager.CreateEmpty(sessionID, ctx)
			ctx.Next()
		}
		cookie, err := ctx.Request.Cookie(cookieName)
		if err != nil || cookie.Value == "" {
			createEmptySession(uuid.New())
			return
		}
		sessionID := cookie.Value
		userID, userJSON, err := manager.Read(sessionID)
		if err != nil || userID == "" || userJSON == "" {
			createEmptySession(uuid.New())
			return
		}
		if err := manager.Create(sessionID, userID, userJSON, ctx); err != nil {
			println(fmt.Sprintf("error creating session: %s", err))
			defer func(sessionID string, userID string) {
				if err := manager.Delete(sessionID, userID); err != nil {
					println(fmt.Sprintf("error deleting session: %s", err))
				}
			}(sessionID, userID)
			createEmptySession(uuid.New())
			return
		}
		// If SessionRefresh is set to false, the session will not refresh;
		// if it's not set or if it's set to true, the session is refreshed.
		refresh, ok := ctx.Get(forest.SessionRefresh).(bool)
		if !ok || refresh {
			path := app.CookiePath
			if path == "" {
				path = "/"
			}
			cookieName := forest.SessionID
			cookieValue := sessionID
			duration := app.Duration("Cookie")
			// Refresh the cookie.
			app.SetCookie(ctx, path, cookieName, cookieValue, duration)
			err := manager.Update(sessionID, userID,
				userJSON, app.Duration("Session"))
			if err != nil {
				println(fmt.Sprintf("error updating session: %s", err))
			}
		}
		ctx.Next()
	}
	return bear.HandlerFunc(sessionGet)
}

func SessionSet(app *forest.App, manager SessionManager) bear.HandlerFunc {
	sessionSet := func(
		_ http.ResponseWriter, _ *http.Request, ctx *bear.Context) {
		userJSON, err := manager.Marshal(ctx)
		if err != nil {
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		sessionID, ok := ctx.Get(forest.SessionID).(string)
		if !ok {
			err := fmt.Errorf("%s: %v",
				forest.SessionID, ctx.Get(forest.SessionID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		userID, ok := ctx.Get(forest.SessionUserID).(string)
		if !ok {
			err := fmt.Errorf("%s: %v",
				forest.SessionUserID, ctx.Get(forest.SessionUserID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		if err := manager.Update(sessionID, userID,
			string(userJSON), app.Duration("Session")); err != nil {
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		ctx.Next()
	}
	return bear.HandlerFunc(sessionSet)
}
<commit_msg>switching to new uuid location<commit_after>// Copyright 2015 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package wares

import (
	"fmt"
	"github.com/pborman/uuid"
	"github.com/ursiform/bear"
	"github.com/ursiform/forest"
	"net/http"
)

func SessionDel(app *forest.App, manager SessionManager) bear.HandlerFunc {
	sessionDel := func(
		_ http.ResponseWriter, _ *http.Request, ctx *bear.Context) {
		sessionID, ok := ctx.Get(forest.SessionID).(string)
		if !ok {
			err := fmt.Errorf("SessionDel %s: %v",
				forest.SessionID, ctx.Get(forest.SessionID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		userID, ok := ctx.Get(forest.SessionUserID).(string)
		if !ok {
			err := fmt.Errorf("SessionDel %s: %v",
				forest.SessionUserID, ctx.Get(forest.SessionUserID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		if err := manager.Delete(sessionID, userID); err != nil {
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		ctx.Next()
	}
	return bear.HandlerFunc(sessionDel)
}

func SessionGet(app *forest.App, manager SessionManager) bear.HandlerFunc {
	sessionGet := func(
		_ http.ResponseWriter, _ *http.Request, ctx *bear.Context) {
		cookieName := forest.SessionID
		createEmptySession := func(sessionID string) {
			path := app.CookiePath
			if path == "" {
				path = "/"
			}
			cookieValue := sessionID
			duration := app.Duration("Cookie")
			// Reset the cookie.
			app.SetCookie(ctx, path, cookieName, cookieValue, duration)
			manager.CreateEmpty(sessionID, ctx)
			ctx.Next()
		}
		cookie, err := ctx.Request.Cookie(cookieName)
		if err != nil || cookie.Value == "" {
			createEmptySession(uuid.New())
			return
		}
		sessionID := cookie.Value
		userID, userJSON, err := manager.Read(sessionID)
		if err != nil || userID == "" || userJSON == "" {
			createEmptySession(uuid.New())
			return
		}
		if err := manager.Create(sessionID, userID, userJSON, ctx); err != nil {
			println(fmt.Sprintf("error creating session: %s", err))
			defer func(sessionID string, userID string) {
				if err := manager.Delete(sessionID, userID); err != nil {
					println(fmt.Sprintf("error deleting session: %s", err))
				}
			}(sessionID, userID)
			createEmptySession(uuid.New())
			return
		}
		// If SessionRefresh is set to false, the session will not refresh;
		// if it's not set or if it's set to true, the session is refreshed.
		refresh, ok := ctx.Get(forest.SessionRefresh).(bool)
		if !ok || refresh {
			path := app.CookiePath
			if path == "" {
				path = "/"
			}
			cookieName := forest.SessionID
			cookieValue := sessionID
			duration := app.Duration("Cookie")
			// Refresh the cookie.
			app.SetCookie(ctx, path, cookieName, cookieValue, duration)
			err := manager.Update(sessionID, userID,
				userJSON, app.Duration("Session"))
			if err != nil {
				println(fmt.Sprintf("error updating session: %s", err))
			}
		}
		ctx.Next()
	}
	return bear.HandlerFunc(sessionGet)
}

func SessionSet(app *forest.App, manager SessionManager) bear.HandlerFunc {
	sessionSet := func(
		_ http.ResponseWriter, _ *http.Request, ctx *bear.Context) {
		userJSON, err := manager.Marshal(ctx)
		if err != nil {
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		sessionID, ok := ctx.Get(forest.SessionID).(string)
		if !ok {
			err := fmt.Errorf("%s: %v",
				forest.SessionID, ctx.Get(forest.SessionID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		userID, ok := ctx.Get(forest.SessionUserID).(string)
		if !ok {
			err := fmt.Errorf("%s: %v",
				forest.SessionUserID, ctx.Get(forest.SessionUserID))
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		if err := manager.Update(sessionID, userID,
			string(userJSON), app.Duration("Session")); err != nil {
			ctx.Set(forest.Error, err)
			message := safeErrorMessage(app, ctx, app.Error("Generic"))
			app.Response(ctx, http.StatusInternalServerError,
				forest.Failure, message).Write(nil)
			return
		}
		ctx.Next()
	}
	return bear.HandlerFunc(sessionSet)
}
<|endoftext|>


<commit_before>package redux

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/gyepisam/fileutils"
)

/*
FindDoFile searches for the most specific .do file for the target and, if found, stores its path in f.DoFile
and returns an array of paths to more specific .do files, if any, that were not found.

target => target.do
target => default.do

target.txt => target.txt.do
target.txt => default.txt.do
target.txt => default.do

target.a.b.c.d.e => target.a.b.c.d.e.do
target.a.b.c.d.e => default.a.b.c.d.e.do
target.a.b.c.d.e => default.a.b.c.d.do
target.a.b.c.d.e => default.a.b.c.do
target.a.b.c.d.e => default.a.b.do
target.a.b.c.d.e => default.a.do
target.a.b.c.d.e => default.do

The presence of multiple extensions does not change the $2 argument to the .do script;
it will still only have one level of extension removed. So, in the last example, the
$2 argument is "target.a.b.c.d" no matter which do script is executed.
*/
func (f *File) findDoFile() (missing []string, err error) {

	candidates := []string{f.Name + ".do"}
	ext := strings.Split(f.Name, ".")
	for i := len(ext); i > 0; i-- {
		candidates = append(candidates, strings.Join(append(append([]string{"default"}, ext[1:i]...), "do"), "."))
	}

TOP:
	for dir := f.Dir; ; /* no test */ dir = filepath.Dir(dir) {
		for _, candidate := range candidates {
			path := filepath.Join(dir, candidate)
			var exists bool // avoid rescoping err
			exists, err = fileutils.FileExists(path)
			f.Debug("@Dofile:%s exists:%t, err: %s\n", path, exists, err)
			if err != nil {
				break TOP
			} else if exists {
				f.DoFile = path
				break TOP
			} else {
				missing = append(missing, path)
			}
		}
		if dir == f.RootDir {
			break TOP
		}
	}

	return
}

const shell = "/bin/sh"

// RunDoFile executes the do file script, records the metadata for the resulting output, then
// saves the resulting output to the target file, if applicable.
func (target *File) RunDoFile() (err error) {
	/*

			  The execution is equivalent to:

			  sh target.ext.do target.ext target tmp0 > tmp1

			  A well behaved .do file writes to stdout (tmp0) or to the $3 file (tmp1), but not both.

			  We use two temp files so as to detect when the .do script misbehaves,
		      in order to avoid producing incorrect output.
	*/

	var outputs [2]*Output

	// If the do file is a task, the first output goes to stdout
	// and the second to a file that will be subsequently deleted.
	for i := 0; i < len(outputs); i++ {
		if i == 0 && target.IsTask() {
			outputs[i] = &Output{os.Stdout, false}
		} else {
			outputs[i], err = target.NewOutput(i == 1)
			if err != nil {
				return err
			}
			defer func(f *Output) {
				f.Close()
				os.Remove(f.Name())
			}(outputs[i])
		}
	}

	err = target.runCmd(outputs)
	if err != nil {
		return err
	}

	if target.IsTask() {
		// Task files should not write to the temp file.
		size, err := outputs[1].Size()
		if err != nil {
			return err
		}

		if size > 0 {
			return target.Errorf("Task do file %s unexpectedly wrote to $3", target.DoFile)
		}

		return nil
	}

	//  Pick an output file...
	//  In the correct case where one file has content and the other is empty,
	//  the former is chosen and the latter is deleted.
	//  If both are empty, the first one is chosen and the second deleted.
	//  If both are non-empty, an error is reported and both are deleted.

	// Default to the first one in case both are empty.
	out := outputs[0]

	// number of files written to
	outCount := 0

	for _, f := range outputs {
		size, err := f.Size()
		if err != nil {
			return err
		}

		if size > 0 {
			outCount++
			out = f
		}

		f.Close()
	}

	// It is an error to write to both files.
	if outCount == len(outputs) {
		return target.Errorf(".do file %s wrote to stdout and to file $3", target.DoFile)
	}

	err = os.Rename(out.Name(), target.Fullpath())

	if err != nil && strings.Index(err.Error(), "cross-device") > -1 {

		// The rename failed due to a cross-device error because the output file
		// tmp dir is on a different device from the target file.
		// Copy the tmp file across the device to the target directory and try again.
		var path string
		path, err = out.Copy(target.Dir)
		if err != nil {
			return err
		}

		err = os.Rename(path, target.Fullpath())
		if err != nil {
			os.Remove(path)
		}
	}

	return err
}

func (target *File) runCmd(outputs [2]*Output) error {

	args := []string{"-e"}

	if ShellArgs != "" {
		if ShellArgs[0] != '-' {
			ShellArgs = "-" + ShellArgs
		}
		args = append(args, ShellArgs)
	}

	args = append(args, target.DoFile, target.Path, target.Basename, outputs[1].Name())

	cmd := exec.Command(shell, args...)
	cmd.Dir = filepath.Dir(target.DoFile)
	cmd.Stdout = outputs[0]
	cmd.Stderr = os.Stderr

	depth := os.Getenv("REDO_DEPTH")
	parent := os.Getenv(REDO_PARENT_ENV_NAME)

	// Add environment variables, replacing existing entries if necessary.
	cmdEnv := os.Environ()
	env := map[string]string{
		REDO_PARENT_ENV_NAME: target.Fullpath(),
		"REDO_DEPTH":         depth + " ",
	}

	// Update environment values if they exist and append when they dont.
TOP:
	for key, value := range env {
		prefix := key + "="
		for i, entry := range cmdEnv {
			if strings.HasPrefix(entry, prefix) {
				cmdEnv[i] = prefix + value
				continue TOP
			}
		}
		cmdEnv = append(cmdEnv, prefix+value)
	}

	cmd.Env = cmdEnv

	if Verbose() {
		prefix := depth
		if parent != "" {
			prefix += target.Rel(parent) + " => "
		}
		target.Log("%s%s (%s)\n", prefix, target.Rel(target.Fullpath()), target.Rel(target.DoFile))
	}

	err := cmd.Run()
	if err == nil {
		return nil
	}

	if Verbose() {
		return target.Errorf("%s %s: %s", shell, strings.Join(args, " "), err)
	}

	return target.Errorf("%s", err)
}
<commit_msg>Invoke do script with filename and not full path<commit_after>package redux

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/gyepisam/fileutils"
)

/*
FindDoFile searches for the most specific .do file for the target and, if found, stores its path in f.DoFile
and returns an array of paths to more specific .do files, if any, that were not found.

target => target.do
target => default.do

target.txt => target.txt.do
target.txt => default.txt.do
target.txt => default.do

target.a.b.c.d.e => target.a.b.c.d.e.do
target.a.b.c.d.e => default.a.b.c.d.e.do
target.a.b.c.d.e => default.a.b.c.d.do
target.a.b.c.d.e => default.a.b.c.do
target.a.b.c.d.e => default.a.b.do
target.a.b.c.d.e => default.a.do
target.a.b.c.d.e => default.do

The presence of multiple extensions does not change the $2 argument to the .do script;
it will still only have one level of extension removed. So, in the last example, the
$2 argument is "target.a.b.c.d" no matter which do script is executed.
*/
func (f *File) findDoFile() (missing []string, err error) {

	candidates := []string{f.Name + ".do"}
	ext := strings.Split(f.Name, ".")
	for i := len(ext); i > 0; i-- {
		candidates = append(candidates, strings.Join(append(append([]string{"default"}, ext[1:i]...), "do"), "."))
	}

TOP:
	for dir := f.Dir; ; /* no test */ dir = filepath.Dir(dir) {
		for _, candidate := range candidates {
			path := filepath.Join(dir, candidate)
			var exists bool // avoid rescoping err
			exists, err = fileutils.FileExists(path)
			f.Debug("@Dofile:%s exists:%t, err: %s\n", path, exists, err)
			if err != nil {
				break TOP
			} else if exists {
				f.DoFile = path
				break TOP
			} else {
				missing = append(missing, path)
			}
		}
		if dir == f.RootDir {
			break TOP
		}
	}

	return
}

const shell = "/bin/sh"

// RunDoFile executes the do file script, records the metadata for the resulting output, then
// saves the resulting output to the target file, if applicable.
func (target *File) RunDoFile() (err error) {
	/*

			  The execution is equivalent to:

			  sh target.ext.do target.ext target tmp0 > tmp1

			  A well behaved .do file writes to stdout (tmp0) or to the $3 file (tmp1), but not both.

			  We use two temp files so as to detect when the .do script misbehaves,
		      in order to avoid producing incorrect output.
	*/

	var outputs [2]*Output

	// If the do file is a task, the first output goes to stdout
	// and the second to a file that will be subsequently deleted.
	for i := 0; i < len(outputs); i++ {
		if i == 0 && target.IsTask() {
			outputs[i] = &Output{os.Stdout, false}
		} else {
			outputs[i], err = target.NewOutput(i == 1)
			if err != nil {
				return err
			}
			defer func(f *Output) {
				f.Close()
				os.Remove(f.Name())
			}(outputs[i])
		}
	}

	err = target.runCmd(outputs)
	if err != nil {
		return err
	}

	if target.IsTask() {
		// Task files should not write to the temp file.
		size, err := outputs[1].Size()
		if err != nil {
			return err
		}

		if size > 0 {
			return target.Errorf("Task do file %s unexpectedly wrote to $3", target.DoFile)
		}

		return nil
	}

	//  Pick an output file...
	//  In the correct case where one file has content and the other is empty,
	//  the former is chosen and the latter is deleted.
	//  If both are empty, the first one is chosen and the second deleted.
	//  If both are non-empty, an error is reported and both are deleted.

	// Default to the first one in case both are empty.
	out := outputs[0]

	// number of files written to
	outCount := 0

	for _, f := range outputs {
		size, err := f.Size()
		if err != nil {
			return err
		}

		if size > 0 {
			outCount++
			out = f
		}

		f.Close()
	}

	// It is an error to write to both files.
	if outCount == len(outputs) {
		return target.Errorf(".do file %s wrote to stdout and to file $3", target.DoFile)
	}

	err = os.Rename(out.Name(), target.Fullpath())

	if err != nil && strings.Index(err.Error(), "cross-device") > -1 {

		// The rename failed due to a cross-device error because the output file
		// tmp dir is on a different device from the target file.
		// Copy the tmp file across the device to the target directory and try again.
		var path string
		path, err = out.Copy(target.Dir)
		if err != nil {
			return err
		}

		err = os.Rename(path, target.Fullpath())
		if err != nil {
			os.Remove(path)
		}
	}

	return err
}

func (target *File) runCmd(outputs [2]*Output) error {

	args := []string{"-e"}

	if ShellArgs != "" {
		if ShellArgs[0] != '-' {
			ShellArgs = "-" + ShellArgs
		}
		args = append(args, ShellArgs)
	}

	doPath, doFile := filepath.Split(target.DoFile)

	args = append(args, doFile, target.Path, target.Basename, outputs[1].Name())

	cmd := exec.Command(shell, args...)
	cmd.Dir = doPath
	cmd.Stdout = outputs[0]
	cmd.Stderr = os.Stderr

	depth := os.Getenv("REDO_DEPTH")
	parent := os.Getenv(REDO_PARENT_ENV_NAME)

	// Add environment variables, replacing existing entries if necessary.
	cmdEnv := os.Environ()
	env := map[string]string{
		REDO_PARENT_ENV_NAME: target.Fullpath(),
		"REDO_DEPTH":         depth + " ",
	}

	// Update environment values if they exist and append when they dont.
TOP:
	for key, value := range env {
		prefix := key + "="
		for i, entry := range cmdEnv {
			if strings.HasPrefix(entry, prefix) {
				cmdEnv[i] = prefix + value
				continue TOP
			}
		}
		cmdEnv = append(cmdEnv, prefix+value)
	}

	cmd.Env = cmdEnv

	if Verbose() {
		prefix := depth
		if parent != "" {
			prefix += target.Rel(parent) + " => "
		}
		target.Log("%s%s (%s)\n", prefix, target.Rel(target.Fullpath()), target.Rel(target.DoFile))
	}

	err := cmd.Run()
	if err == nil {
		return nil
	}

	if Verbose() {
		return target.Errorf("%s %s: %s", shell, strings.Join(args, " "), err)
	}

	return target.Errorf("%s", err)
}
<|endoftext|>


<commit_before>package testutil

import (
	"path/filepath"
	"time"

	"v.io/tools/lib/collect"
	"v.io/tools/lib/runutil"
	"v.io/tools/lib/util"
)

const (
	defaultBrowserTestTimeout = 5 * time.Minute
)

// vanadiumBrowserTest runs an integration test for the vanadium
// namespace browser.
//
// TODO(aghassemi): Port the namespace browser test logic from shell to Go.
func vanadiumNamespaceBrowserTest(ctx *util.Context, testName string) (_ *TestResult, e error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	xUnitFile := XUnitReportPath(testName)

	// Initialize the test.
	cleanup, result, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	} else if result != nil {
		return result, nil
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Invoke "make clean" for the vanadium namespace browser and remove
	// the test output file if it exists.
	browserDir := filepath.Join(root, "release", "projects", "namespace_browser")
	if err := ctx.Run().Chdir(browserDir); err != nil {
		return nil, err
	}
	if err := ctx.Run().Command("make", "clean"); err != nil {
		return nil, err
	}
	if err := ctx.Run().RemoveAll(xUnitFile); err != nil {
		return nil, err
	}

	// Invoke "make test" for the vanadium namepsace browser.
	opts := ctx.Run().Opts()
	opts.Env["XUNIT_OUTPUT_FILE"] = xUnitFile
	if err := ctx.Run().TimedCommandWithOpts(defaultBrowserTestTimeout, opts, "make", "test"); err != nil {
		if err == runutil.CommandTimedOutErr {
			return &TestResult{
				Status:       TestTimedOut,
				TimeoutValue: defaultBrowserTestTimeout,
			}, nil
		}
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}
<commit_msg>lib/testutil: report errors for "make test" in namespace browser test.<commit_after>package testutil

import (
	"path/filepath"
	"time"

	"v.io/tools/lib/collect"
	"v.io/tools/lib/runutil"
	"v.io/tools/lib/util"
)

const (
	defaultBrowserTestTimeout = 5 * time.Minute
)

// vanadiumBrowserTest runs an integration test for the vanadium
// namespace browser.
//
// TODO(aghassemi): Port the namespace browser test logic from shell to Go.
func vanadiumNamespaceBrowserTest(ctx *util.Context, testName string) (_ *TestResult, e error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	xUnitFile := XUnitReportPath(testName)

	// Initialize the test.
	cleanup, result, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	} else if result != nil {
		return result, nil
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Invoke "make clean" for the vanadium namespace browser and remove
	// the test output file if it exists.
	browserDir := filepath.Join(root, "release", "projects", "namespace_browser")
	if err := ctx.Run().Chdir(browserDir); err != nil {
		return nil, err
	}
	if err := ctx.Run().Command("make", "clean"); err != nil {
		return nil, err
	}
	if err := ctx.Run().RemoveAll(xUnitFile); err != nil {
		return nil, err
	}

	// Invoke "make test" for the vanadium namepsace browser.
	makeTargetFunc := func(opts runutil.Opts) error {
		opts.Env["XUNIT_OUTPUT_FILE"] = xUnitFile
		return ctx.Run().TimedCommandWithOpts(defaultBrowserTestTimeout, opts, "make", "test")
	}
	if testResult, err := genXUnitReportOnCmdError(ctx, testName, "Make test", "failure", makeTargetFunc); err != nil {
		return nil, err
	} else if testResult != nil {
		if testResult.Status == TestTimedOut {
			testResult.TimeoutValue = defaultBrowserTestTimeout
		}
		return testResult, nil
	}

	return &TestResult{Status: TestPassed}, nil
}
<|endoftext|>


<commit_before>package douban

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

const AUTH_HOST = "https://www.douban.com"
const API_HOST = "https://api.douban.com"

type Client struct {
	APT_KEY      string
	API_SECRET   string
	redirct_url  string
	scope        []string
	access_token map[string]interface{}
}

func NewClient(API_KEY, API_SECRET, redrict_url string, scope []string) *Client {
	return &Client{API_KEY, API_SECRET, redrict_url, scope, nil}
}

func (c *Client) get(url string) []byte {
	client := &http.Client{}

	req, err := http.NewRequest("GET", API_HOST+url, nil)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Authorization", "Bearer "+c.access_token["access_token"].(string))
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	return content
}

func (c *Client) AuthorizeUrl() string {
	return Urlencode(AUTH_HOST+"/service/auth2/auth", map[string]string{
		"client_id":     c.APT_KEY,
		"redirect_uri":  c.redirct_url,
		"response_type": "code",
		"scope":         strings.Join(c.scope, ","),
	})
}

func (c *Client) AuthWithToken(token map[string]interface{}) {
	c.access_token = token
}

func (c *Client) AuthWithCode(code string) {
	resp, err := http.PostForm(AUTH_HOST+"/service/auth2/token", url.Values{
		"client_id":     {c.APT_KEY},
		"client_secret": {c.API_SECRET},
		"redirect_uri":  {c.redirct_url},
		"grant_type":    {"authorization_code"},
		"code":          {code},
	})
	if err != nil {
		log.Fatal(err)
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	fmt.Println(string(content))
	json.Unmarshal(content, &c.access_token)
}

func (c *Client) RefreshToken() {
	resp, err := http.PostForm(AUTH_HOST+"/service/auth2/token", url.Values{
		"client_id":     {c.APT_KEY},
		"client_secret": {c.API_SECRET},
		"redirect_uri":  {c.redirct_url},
		"grant_type":    {"refresh_token"},
		"refresh_token": {c.access_token["refresh_token"].(string)},
	})
	if err != nil {
		log.Fatal(err)
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	json.Unmarshal(content, &c.access_token)
}

// User

func (c *Client) Me() []byte {
	return c.get("/v2/user/~me")
}

func (c *Client) GetUserbyId(id int32) []byte {
	return c.get("/v2/user/" + string(id))
}

func (c *Client) GetUserbyName(username string) []byte {
	return c.get("/v2/user/" + username)
}

func (c *Client) SearchUserByKeywords(keywords []string, start, count int32) []byte {
	return c.get(Urlencode("/v2/user", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": string(start),
		"count": string(count),
	}))
}

// Book
func (c *Client) GetBookById(id int32) []byte {
	return c.get("/v2/book/" + string(id))
}

func (c *Client) GetBookByISBN(isbn string) []byte {
	return c.get("/v2/book/isbn/" + isbn)
}

func (c *Client) SearchBookByKeywords(keywords []string, start, count int32) []byte {
	return c.get(Urlencode("/v2/book/search", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": string(start),
		"count": string(count),
	}))
}

func (c *Client) SearchBookByTag(tag string, start, count int32) []byte {
	return c.get(Urlencode("/v2/book/search", map[string]string{
		"tag":   tag,
		"start": string(start),
		"count": string(count),
	}))
}

func (c *Client) GetTagsOfBookById(id int32) []byte {
	return c.get("/v2/book/" + string(id) + "/tags")
}

/*
http://developers.douban.com/wiki/?title=book_v2#get_user_book_tags
func (c *Client) GetTagsOfBooksOfUser() {

}
*/

// http://developers.douban.com/wiki/?title=book_v2#get_user_collections
func (c *Client) GetBookCollectionsOfUser(username string) []byte {
	return c.get("/v2/book/user/" + username + "/collections")
}

// http://developers.douban.com/wiki/?title=book_v2#get_book_collection
func (c *Client) GetBookCollectionOfUser(book_id, username string) []byte {
	return c.get(Urlencode("/v2/book/"+book_id+"/collection", map[string]string{
		"user_id": username,
	}))
}

// http://developers.douban.com/wiki/?title=book_v2#get_user_annotations
func (c *Client) GetUserAnnotations(username string) []byte {
	return c.get("/v2/book/user/" + username + "/annotations")
}

// http://developers.douban.com/wiki/?title=book_v2#get_book_annotations
func (c *Client) GetBookAnnotations(book_id int32) []byte {
	return c.get("/v2/book/" + string(book_id) + "/annotations")
}

// http://developers.douban.com/wiki/?title=book_v2#get_annotation
func (c *Client) GetAnnotationById(id int32) []byte {
	return c.get("/v2/book/annotation/" + string(id))
}

// Movie

func (c *Client) GetMovieById(id int32) []byte {
	return c.get("/v2/movie/subject/" + string(id))
}

func (c *Client) GetMoviePhotosById(id int32) []byte {
	return c.get("/v2/movie/subject/" + string(id) + "/photos")
}

//http://developers.douban.com/wiki/?title=movie_v2#reviews
func (c *Client) GetMovieReviewsById(id int32) []byte {
	return c.get("/v2/movie/subject/" + string(id) + "/reviews")
}

//http://developers.douban.com/wiki/?title=movie_v2#comments
func (c *Client) GetMovieCommentsById(id int32) []byte {
	return c.get("/v2/movie/subject/" + string(id) + "/comments")
}

//http://developers.douban.com/wiki/?title=movie_v2#celebrity
func (c *Client) GetCelebrityById(id int32) []byte {
	return c.get("/v2/movie/celebrity/" + string(id))
}

//http://developers.douban.com/wiki/?title=movie_v2#celebrity-photos
func (c *Client) GetCelebrityPhotosById(id int32) []byte {
	return c.get("/v2/movie/celebrity/" + string(id) + "/photos")
}

//http://developers.douban.com/wiki/?title=movie_v2#works
func (c *Client) GetCelebrityWorksById(id int32) []byte {
	return c.get("/v2/movie/celebrity/" + string(id) + "/works")
}

//http://developers.douban.com/wiki/?title=movie_v2#search
func (c *Client) SearchMovieByKeywords(keywords []string, start, count int32) []byte {
	return c.get(Urlencode("/v2/movie/search", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": string(start),
		"count": string(count),
	}))
}

func (c *Client) SearchMovieByTag(tag string, start, count int32) []byte {
	return c.get(Urlencode("/v2/movie/search", map[string]string{
		"tag":   tag,
		"start": string(start),
		"count": string(count),
	}))
}

//http://developers.douban.com/wiki/?title=movie_v2#nowplaying
func (c *Client) GetNowplayingMovies() []byte {
	return c.get("/v2/movie/nowplaying")
}

//http://developers.douban.com/wiki/?title=movie_v2#coming
func (c *Client) GetComingMovies() []byte {
	return c.get("/v2/movie/coming")
}

//http://developers.douban.com/wiki/?title=movie_v2#top250
func (c *Client) Top250Movies() []byte {
	return c.get("/v2/movie/top250")
}

//http://developers.douban.com/wiki/?title=movie_v2#weekly
func (c *Client) WeeklyMovies() []byte {
	return c.get("/v2/movie/weekly")
}

//http://developers.douban.com/wiki/?title=movie_v2#us-box
func (c *Client) US_Box() []byte {
	return c.get("/v2/movie/us_box")
}

//http://developers.douban.com/wiki/?title=movie_v2#new-movies
func (c *Client) NewMovies() []byte {
	return c.get("/v2/movie/new_movies")
}
<commit_msg>post and music<commit_after>package douban

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const AUTH_HOST = "https://www.douban.com"
const API_HOST = "https://api.douban.com"

type Client struct {
	APT_KEY      string
	API_SECRET   string
	redirct_url  string
	scope        []string
	access_token map[string]interface{}
}

func NewClient(API_KEY, API_SECRET, redrict_url string, scope []string) *Client {
	return &Client{API_KEY, API_SECRET, redrict_url, scope, nil}
}

func (c *Client) get(url string) []byte {
	client := &http.Client{}
	fmt.Println(API_HOST + url)
	req, err := http.NewRequest("GET", API_HOST+url, nil)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Authorization", c.access_token["access_token"].(string))
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	return content
}

/*

func (c *Client) post(purl string, data map[string]([]string)) []byte {
	http.Header.Set("jj", "ss")
	resp, err := http.PostForm(API_HOST+purl, data)
	if err != nil {
		log.Fatal(err)
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return content
}

*/

func (c *Client) post(purl string, data map[string]([]string)) []byte {
	client := &http.Client{}

	req, err := http.NewRequest("GET", API_HOST+purl, strings.NewReader(url.Values(data).Encode()))
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Authorization", "Bearer "+c.access_token["access_token"].(string))
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()

	return content
}

func (c *Client) AuthorizeUrl() string {
	return Urlencode(AUTH_HOST+"/service/auth2/auth", map[string]string{
		"client_id":     c.APT_KEY,
		"redirect_uri":  c.redirct_url,
		"response_type": "code",
		"scope":         strings.Join(c.scope, "+"),
	})
}

func (c *Client) AuthWithToken(token interface{}) {
	switch token.(type) {
	case string:
		c.access_token = map[string]interface{}{
			"access_token": token.(string),
		}
	case map[string]interface{}:
		c.access_token = token.(map[string]interface{})
	}
}

func (c *Client) AuthWithCode(code string) {
	resp, err := http.PostForm(AUTH_HOST+"/service/auth2/token", url.Values{
		"client_id":     {c.APT_KEY},
		"client_secret": {c.API_SECRET},
		"redirect_uri":  {c.redirct_url},
		"grant_type":    {"authorization_code"},
		"code":          {code},
	})
	if err != nil {
		log.Fatal(err)
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	fmt.Println(string(content))
	json.Unmarshal(content, &c.access_token)
}

func (c *Client) RefreshToken() {
	resp, err := http.PostForm(AUTH_HOST+"/service/auth2/token", url.Values{
		"client_id":     {c.APT_KEY},
		"client_secret": {c.API_SECRET},
		"redirect_uri":  {c.redirct_url},
		"grant_type":    {"refresh_token"},
		"refresh_token": {c.access_token["refresh_token"].(string)},
	})
	if err != nil {
		log.Fatal(err)
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	json.Unmarshal(content, &c.access_token)
}

// User

func (c *Client) Me() []byte {
	return c.get("/v2/user/~me")
}

func (c *Client) GetUserbyId(id int) []byte {
	return c.get("/v2/user/" + strconv.Itoa(id))
}

func (c *Client) GetUserbyName(username string) []byte {
	return c.get("/v2/user/" + username)
}

func (c *Client) SearchUserByKeywords(keywords []string, start, count int) []byte {
	return c.get(Urlencode("/v2/user", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

// Book
func (c *Client) GetBookById(id int) []byte {
	return c.get("/v2/book/" + strconv.Itoa(id))
}

func (c *Client) GetBookByISBN(isbn string) []byte {
	return c.get("/v2/book/isbn/" + isbn)
}

func (c *Client) SearchBookByKeywords(keywords []string, start, count int) []byte {
	return c.get(Urlencode("/v2/book/search", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

func (c *Client) SearchBookByTag(tag string, start, count int) []byte {
	return c.get(Urlencode("/v2/book/search", map[string]string{
		"tag":   tag,
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

func (c *Client) GetTagsOfBookById(id int) []byte {
	return c.get("/v2/book/" + strconv.Itoa(id) + "/tags")
}

/*
http://developers.douban.com/wiki/?title=book_v2#get_user_book_tags
func (c *Client) GetTagsOfBooksOfUser() {

}
*/

// http://developers.douban.com/wiki/?title=book_v2#get_user_collections
func (c *Client) GetBookCollectionsOfUser(username string) []byte {
	return c.get("/v2/book/user/" + username + "/collections")
}

// http://developers.douban.com/wiki/?title=book_v2#get_book_collection
func (c *Client) GetBookCollectionOfUser(book_id, username string) []byte {
	return c.get(Urlencode("/v2/book/"+book_id+"/collection", map[string]string{
		"user_id": username,
	}))
}

// http://developers.douban.com/wiki/?title=book_v2#get_user_annotations
func (c *Client) GetUserAnnotations(username string) []byte {
	return c.get("/v2/book/user/" + username + "/annotations")
}

// http://developers.douban.com/wiki/?title=book_v2#get_book_annotations
func (c *Client) GetBookAnnotations(book_id int) []byte {
	return c.get("/v2/book/" + strconv.Itoa(book_id) + "/annotations")
}

// http://developers.douban.com/wiki/?title=book_v2#get_annotation
func (c *Client) GetAnnotationById(id int) []byte {
	return c.get("/v2/book/annotation/" + strconv.Itoa(id))
}

// Movie

func (c *Client) GetMovieById(id int) []byte {
	return c.get("/v2/movie/subject/" + strconv.Itoa(id))
}

func (c *Client) GetMoviePhotosById(id int) []byte {
	return c.get("/v2/movie/subject/" + strconv.Itoa(id) + "/photos")
}

//http://developers.douban.com/wiki/?title=movie_v2#reviews
func (c *Client) GetMovieReviewsById(id int) []byte {
	return c.get("/v2/movie/subject/" + strconv.Itoa(id) + "/reviews")
}

//http://developers.douban.com/wiki/?title=movie_v2#comments
func (c *Client) GetMovieCommentsById(id int) []byte {
	return c.get("/v2/movie/subject/" + strconv.Itoa(id) + "/comments")
}

//http://developers.douban.com/wiki/?title=movie_v2#celebrity
func (c *Client) GetCelebrityById(id int) []byte {
	return c.get("/v2/movie/celebrity/" + strconv.Itoa(id))
}

//http://developers.douban.com/wiki/?title=movie_v2#celebrity-photos
func (c *Client) GetCelebrityPhotosById(id int) []byte {
	return c.get("/v2/movie/celebrity/" + strconv.Itoa(id) + "/photos")
}

//http://developers.douban.com/wiki/?title=movie_v2#works
func (c *Client) GetCelebrityWorksById(id int) []byte {
	return c.get("/v2/movie/celebrity/" + strconv.Itoa(id) + "/works")
}

//http://developers.douban.com/wiki/?title=movie_v2#search
func (c *Client) SearchMovieByKeywords(keywords []string, start, count int) []byte {
	return c.get(Urlencode("/v2/movie/search", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

func (c *Client) SearchMovieByTag(tag string, start, count int) []byte {
	return c.get(Urlencode("/v2/movie/search", map[string]string{
		"tag":   tag,
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

//http://developers.douban.com/wiki/?title=movie_v2#nowplaying
func (c *Client) GetNowplayingMovies() []byte {
	return c.get("/v2/movie/nowplaying")
}

//http://developers.douban.com/wiki/?title=movie_v2#coming
func (c *Client) GetComingMovies() []byte {
	return c.get("/v2/movie/coming")
}

//http://developers.douban.com/wiki/?title=movie_v2#top250
func (c *Client) Top250Movies() []byte {
	return c.get("/v2/movie/top250")
}

//http://developers.douban.com/wiki/?title=movie_v2#weekly
func (c *Client) WeeklyMovies() []byte {
	return c.get("/v2/movie/weekly")
}

//http://developers.douban.com/wiki/?title=movie_v2#us-box
func (c *Client) US_Box() []byte {
	return c.get("/v2/movie/us_box")
}

//http://developers.douban.com/wiki/?title=movie_v2#new-movies
func (c *Client) NewMovies() []byte {
	return c.get("/v2/movie/new_movies")
}

//http://developers.douban.com/wiki/?title=music_v2#get_music
func (c *Client) GetMusicById(id int) []byte {
	return c.get("/v2/music/" + strconv.Itoa(id))
}

func (c *Client) SearchMusicByKeywords(keywords []string, start, count int) []byte {
	return c.get(Urlencode("/v2/music/search", map[string]string{
		"q":     strings.Join(keywords, "+"),
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

func (c *Client) SearchMusicByTag(tag string, start, count int) []byte {
	return c.get(Urlencode("/v2/music/search", map[string]string{
		"tag":   tag,
		"start": strconv.Itoa(start),
		"count": strconv.Itoa(count),
	}))
}

//http://developers.douban.com/wiki/?title=music_v2#get_music_tags
func (c *Client) GetMusicTagsById(id int) []byte {
	return c.get("/v2/music/" + strconv.Itoa(id) + "/tags")
}

//http://developers.douban.com/wiki/?title=music_v2#post_music_review
func (c *Client) PostMusicReview(id int, title, content string, rating int) []byte {
	return c.post("/v2/music/reviews", map[string]([]string){
		"music":   {strconv.Itoa(id)},
		"title":   {title},
		"content": {content},
		"rating":  {strconv.Itoa(rating)},
	})
}
<|endoftext|>


<commit_before>// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff

// Package draw2d is a pure go 2D vector graphics library with support
// for multiple output devices such as images (draw2d), pdf documents
// (draw2dpdf) and opengl (draw2dopengl), which can also be used on the
// google app engine. It can be used as a pure go Cairo alternative.
// draw2d is released under the BSD license.
//
// Features
//
// Operations in draw2d include stroking and filling polygons, arcs,
// Bézier curves, drawing images and text rendering with truetype fonts.
// All drawing operations can be transformed by affine transformations
// (scale, rotation, translation).
//
// Package draw2d follows the conventions of http://www.w3.org/TR/2dcontext for coordinate system, angles, etc...
//
// Installation
//
// To install or update the package draw2d on your system, run:
//   go get -u github.com/llgcode/draw2d
//
// Quick Start
//
// Package draw2d itself provides a graphic context that can draw vector
// graphics and text on an image canvas. The following Go code
// generates a simple drawing and saves it to an image file:
//   // Initialize the graphic context on an RGBA image
//   dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
//   gc := draw2d.NewGraphicContext(dest)
//
//   // Set some properties
//   gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
//   gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
//   gc.SetLineWidth(5)
//
//   // Draw a closed shape
//   gc.MoveTo(10, 10) // should always be called first for a new path
//   gc.LineTo(100, 50)
//   gc.QuadCurveTo(100, 10, 10, 10)
//   gc.Close()
//   gc.FillStroke()
//
//   // Save to file
//   draw2d.SaveToPngFile("hello.png", dest)
//
// There are more examples here:
// https://github.com/llgcode/draw2d/tree/master/samples
//
// Drawing on pdf documents is provided by the draw2dpdf package.
// Drawing on opengl is provided by the draw2dgl package.
// See subdirectories at the bottom of this page.
//
// Testing
//
// The samples are run as tests from the root package folder `draw2d` by:
//   go test ./...
//
// Or if you want to run with test coverage:
//   go test -cover ./... | grep -v "no test"
//
// This will generate output by the different backends in the output folder.
//
// Acknowledgments
//
// Laurent Le Goff wrote this library, inspired by Postscript and
// HTML5 canvas. He implemented the image and opengl backend with the
// freetype-go package. Also he created a pure go Postscript
// interpreter, which can read postscript images and draw to a draw2d
// graphic context (https://github.com/llgcode/ps). Stani Michiels
// implemented the pdf backend with the gofpdf package.
//
// The package depends on freetype-go package for its rasterization
// algorithm.
//
// Packages using draw2d
//
// - https://github.com/llgcode/ps: Postscript interpreter written in Go
//
// - https://github.com/gonum/plot: drawing plots in Go
//
// - https://github.com/muesli/smartcrop: content aware image cropping
//
// - https://github.com/peterhellberg/karta: drawing Voronoi diagrams
//
// - https://github.com/vdobler/chart: basic charts in Go
package draw2d
<commit_msg>fix typo of draw2dgl in docstring of draw2d.go<commit_after>// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff

// Package draw2d is a pure go 2D vector graphics library with support
// for multiple output devices such as images (draw2d), pdf documents
// (draw2dpdf) and opengl (draw2dgl), which can also be used on the
// google app engine. It can be used as a pure go Cairo alternative.
// draw2d is released under the BSD license.
//
// Features
//
// Operations in draw2d include stroking and filling polygons, arcs,
// Bézier curves, drawing images and text rendering with truetype fonts.
// All drawing operations can be transformed by affine transformations
// (scale, rotation, translation).
//
// Package draw2d follows the conventions of http://www.w3.org/TR/2dcontext for coordinate system, angles, etc...
//
// Installation
//
// To install or update the package draw2d on your system, run:
//   go get -u github.com/llgcode/draw2d
//
// Quick Start
//
// Package draw2d itself provides a graphic context that can draw vector
// graphics and text on an image canvas. The following Go code
// generates a simple drawing and saves it to an image file:
//   // Initialize the graphic context on an RGBA image
//   dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
//   gc := draw2d.NewGraphicContext(dest)
//
//   // Set some properties
//   gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
//   gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
//   gc.SetLineWidth(5)
//
//   // Draw a closed shape
//   gc.MoveTo(10, 10) // should always be called first for a new path
//   gc.LineTo(100, 50)
//   gc.QuadCurveTo(100, 10, 10, 10)
//   gc.Close()
//   gc.FillStroke()
//
//   // Save to file
//   draw2d.SaveToPngFile("hello.png", dest)
//
// There are more examples here:
// https://github.com/llgcode/draw2d/tree/master/samples
//
// Drawing on pdf documents is provided by the draw2dpdf package.
// Drawing on opengl is provided by the draw2dgl package.
// See subdirectories at the bottom of this page.
//
// Testing
//
// The samples are run as tests from the root package folder `draw2d` by:
//   go test ./...
//
// Or if you want to run with test coverage:
//   go test -cover ./... | grep -v "no test"
//
// This will generate output by the different backends in the output folder.
//
// Acknowledgments
//
// Laurent Le Goff wrote this library, inspired by Postscript and
// HTML5 canvas. He implemented the image and opengl backend with the
// freetype-go package. Also he created a pure go Postscript
// interpreter, which can read postscript images and draw to a draw2d
// graphic context (https://github.com/llgcode/ps). Stani Michiels
// implemented the pdf backend with the gofpdf package.
//
// The package depends on freetype-go package for its rasterization
// algorithm.
//
// Packages using draw2d
//
// - https://github.com/llgcode/ps: Postscript interpreter written in Go
//
// - https://github.com/gonum/plot: drawing plots in Go
//
// - https://github.com/muesli/smartcrop: content aware image cropping
//
// - https://github.com/peterhellberg/karta: drawing Voronoi diagrams
//
// - https://github.com/vdobler/chart: basic charts in Go
package draw2d
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"math"
	"time"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/wolfeidau/lifx"
)

var info = ninja.LoadModuleInfo("./package.json")

// Default values if we are only setting partial state.
// TODO: @wolfidau This should be coming from the bulb
var defaultTransition = 300
var defaultBrightness float64 = 1
var defaultSaturation float64 = 1
var defaultHue = 0

type LifxDriver struct {
	log       *logger.Logger
	config    *LifxDriverConfig
	conn      *ninja.Connection
	client    *lifx.Client
	sendEvent func(event string, payload interface{}) error

	devices map[string]*lifxDevice
}

// this struct holds channels which are specific to the lifx bulb
type lifxDevice struct {
	illuminance *channels.IlluminanceChannel
}

func NewLifxDriver() {
	d := &LifxDriver{
		log:     logger.GetLogger(info.Name),
		client:  lifx.NewClient(),
		devices: make(map[string]*lifxDevice),
	}

	conn, err := ninja.Connect(info.ID)
	if err != nil {
		d.log.Fatalf("Failed to connect to MQTT: %s", err)
	}

	err = conn.ExportDriver(d)

	if err != nil {
		d.log.Fatalf("Failed to export driver: %s", err)
	}

	go func() {

		sub := d.client.Subscribe()

		for {

			event := <-sub.Events

			switch evnt := event.(type) {
			case *lifx.Bulb:
				if isUnique(evnt) {
					d.log.Infof("creating new light")
					_, err := d.newLight(evnt)
					if err != nil {
						d.log.HandleErrorf(err, "Error creating light instance")
					}
					seenlights = append(seenlights, evnt) //TODO remove bulbs that haven't been seen in a while?
					err = d.client.GetBulbState(evnt)

					if err != nil {
						d.log.Warningf("unable to intiate bulb state request %s", err)
					}
				}
			case *lifx.LightSensorState:
				// handle these vents for each bulb
				if d.devices[evnt.GetLifxAddress()] != nil {
					d.devices[evnt.GetLifxAddress()].illuminance.SendState(float64(evnt.Lux))
				}

			default:
				d.log.Infof("Event %v", event)
			}

		}

	}()

	go d.publishSensorData()

	d.conn = conn
}

type LifxDriverConfig struct {
}

func (d *LifxDriver) Start(config *LifxDriverConfig) error {
	d.log.Infof("Starting with config %v", config)
	d.config = config

	err := d.client.StartDiscovery()
	if err != nil {
		err = fmt.Errorf("Failed to discover bulbs : %s", err)
	}
	return err
}

func (d *LifxDriver) Stop() error {
	return nil
}

func (d *LifxDriver) GetModuleInfo() *model.Module {
	return info
}

func (d *LifxDriver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

//---------------------------------------------------------------[Bulb]----------------------------------------------------------------

func (d *LifxDriver) newLight(bulb *lifx.Bulb) (*devices.LightDevice, error) { //TODO cut this down!

	name := bulb.GetLabel()

	d.log.Infof("Making light with ID: %s Label: %s", bulb.GetLifxAddress(), name)

	light, err := devices.CreateLightDevice(d, &model.Device{
		NaturalID:     bulb.GetLifxAddress(),
		NaturalIDType: "lifx",
		Name:          &name,
		Signatures: &map[string]string{
			"ninja:manufacturer": "Lifx",
			"ninja:productName":  "Lifx Bulb",
			"ninja:productType":  "Light",
			"ninja:thingType":    "light",
		},
	}, d.conn)

	if err != nil {
		d.log.FatalError(err, "Could not create light device")
	}

	light.ApplyOnOff = func(state bool) error {
		var err error
		if state {
			err = d.client.LightOn(bulb)
		} else {
			err = d.client.LightOff(bulb)
		}
		if err != nil {
			return fmt.Errorf("Failed to set on-off state: %s", err)
		}
		return nil
	}

	light.ApplyLightState = func(state *devices.LightDeviceState) error {
		jsonState, _ := json.Marshal(state)
		d.log.Debugf("Sending light state to lifx bulb: %s", jsonState)

		if state.OnOff != nil {
			err := light.ApplyOnOff(*state.OnOff)
			if err != nil {
				return err
			}
		}

		if state.Color != nil || state.Brightness != nil || state.Transition != nil {

			if state.Brightness == nil {
				state.Brightness = &defaultBrightness
			}

			if state.Color == nil {
				state.Color = &channels.ColorState{
					Mode:       "hue",
					Saturation: &defaultSaturation,
				}
			}

			if state.Transition == nil {
				state.Transition = &defaultTransition
			}

			/*if state.Color == nil {
				return fmt.Errorf("Color value missing from batch set")
			}

			if state.Brightness == nil {
				return fmt.Errorf("Brightness value missing from batch set")
			}*/

			switch state.Color.Mode {
			case "hue":
				return d.client.LightColour(
					bulb,
					uint16(*state.Color.Hue*math.MaxUint16),
					uint16(*state.Color.Saturation*math.MaxUint16),
					uint16(*state.Brightness*math.MaxUint16),
					0,
					uint32(*state.Transition),
				)

			case "xy":
				//TODO: Lifx does not support XY color
				return fmt.Errorf("XY color mode is not yet supported in the Lifx driver")

			case "temperature":
				d.client.LightColour(
					bulb,
					0,
					0,
					uint16(*state.Brightness*math.MaxUint16),
					uint16(*state.Color.Temperature),
					uint32(*state.Transition),
				)

			default:
				return fmt.Errorf("Unknown color mode %s", state.Color.Mode)
			}

		}

		return nil
	}

	bulb.SetStateHandler(buildStateHandler(d, bulb, light))

	if err := light.EnableOnOffChannel(); err != nil {
		d.log.FatalError(err, "Could not enable lifx on-off channel")
	}
	if err := light.EnableBrightnessChannel(); err != nil {
		d.log.FatalError(err, "Could not enable lifx brightness channel")
	}
	if err := light.EnableColorChannel("temperature", "hue"); err != nil {
		d.log.FatalError(err, "Could not enable lifx color channel")
	}
	if err := light.EnableTransitionChannel(); err != nil {
		d.log.FatalError(err, "Could not enable lifx transition channel")
	}

	// extra channels for sensors
	illuminance := channels.NewIlluminanceChannel(d)

	if err := d.conn.ExportChannel(light, illuminance, "illuminance"); err != nil {
		d.log.FatalError(err, "Could not enable lifx illuminance channel")
	}

	d.log.Debugf("register illuminance channel for %s", bulb.GetLifxAddress())

	d.devices[bulb.GetLifxAddress()] = &lifxDevice{illuminance}

	return light, nil
}

func buildStateHandler(driver *LifxDriver, bulb *lifx.Bulb, light *devices.LightDevice) lifx.StateHandler {

	return func(bulbState *lifx.BulbState) {

		jsonState, _ := json.Marshal(bulbState)
		driver.log.Debugf("Incoming state: %s", jsonState)

		state := &devices.LightDeviceState{}

		onOff := int(bulbState.Power) > 0
		state.OnOff = &onOff

		brightness := float64(bulbState.Brightness) / math.MaxUint16
		state.Brightness = &brightness

		color := &channels.ColorState{}
		if bulbState.Saturation == 0 {
			color.Mode = "temperature"

			temperature := float64(bulbState.Kelvin)
			color.Temperature = &temperature

		} else {
			color.Mode = "hue"

			hue := float64(bulbState.Hue) / float64(math.MaxUint16)
			color.Hue = &hue

			saturation := float64(bulbState.Saturation) / float64(math.MaxUint16)
			color.Saturation = &saturation
		}

		state.Color = color

		light.SetLightState(state)

		// TODO use the state.Visible to indicate whether a globe is online/offline at the moment

	}
}

// this function publishes information from the sensors on the lifx bulb
// atm this is limited to illuminance
func (d *LifxDriver) publishSensorData() {
	c := time.Tick(30 * time.Second)
	for _ = range c {
		for _, bulb := range d.client.GetBulbs() {
			d.client.GetAmbientLight(bulb)
		}
	}
}

//---------------------------------------------------------------[Utils]----------------------------------------------------------------

var seenlights []*lifx.Bulb

func isUnique(newbulb *lifx.Bulb) bool {
	ret := true
	for _, bulb := range seenlights {
		if bulb.LifxAddress == newbulb.LifxAddress {
			ret = false
		}
	}
	return ret
}
<commit_msg>Small fix for api change.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"math"
	"time"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/wolfeidau/lifx"
)

var info = ninja.LoadModuleInfo("./package.json")

// Default values if we are only setting partial state.
// TODO: @wolfidau This should be coming from the bulb
var defaultTransition = 300
var defaultBrightness float64 = 1
var defaultSaturation float64 = 1
var defaultHue = 0

type LifxDriver struct {
	log       *logger.Logger
	config    *LifxDriverConfig
	conn      *ninja.Connection
	client    *lifx.Client
	sendEvent func(event string, payload interface{}) error

	devices map[string]*lifxDevice
}

// this struct holds channels which are specific to the lifx bulb
type lifxDevice struct {
	illuminance *channels.IlluminanceChannel
}

func NewLifxDriver() {
	d := &LifxDriver{
		log:     logger.GetLogger(info.Name),
		client:  lifx.NewClient(),
		devices: make(map[string]*lifxDevice),
	}

	conn, err := ninja.Connect(info.ID)
	if err != nil {
		d.log.Fatalf("Failed to connect to MQTT: %s", err)
	}

	err = conn.ExportDriver(d)

	if err != nil {
		d.log.Fatalf("Failed to export driver: %s", err)
	}

	go func() {

		sub := d.client.Subscribe()

		for {

			event := <-sub.Events

			switch evnt := event.(type) {
			case *lifx.Bulb:
				if isUnique(evnt) {
					d.log.Infof("creating new light")
					_, err := d.newLight(evnt)
					if err != nil {
						d.log.HandleErrorf(err, "Error creating light instance")
					}
					seenlights = append(seenlights, evnt) //TODO remove bulbs that haven't been seen in a while?
					err = d.client.GetBulbState(evnt)

					if err != nil {
						d.log.Warningf("unable to intiate bulb state request %s", err)
					}
				}
			case *lifx.LightSensorState:
				// handle these vents for each bulb
				if d.devices[evnt.GetLifxAddress()] != nil {
					d.devices[evnt.GetLifxAddress()].illuminance.SendState(float64(evnt.Lux))
				}

			default:
				d.log.Infof("Event %v", event)
			}

		}

	}()

	go d.publishSensorData()

	d.conn = conn
}

type LifxDriverConfig struct {
}

func (d *LifxDriver) Start(config *LifxDriverConfig) error {
	d.log.Infof("Starting with config %v", config)
	d.config = config

	err := d.client.StartDiscovery()
	if err != nil {
		err = fmt.Errorf("Failed to discover bulbs : %s", err)
	}
	return err
}

func (d *LifxDriver) Stop() error {
	return nil
}

func (d *LifxDriver) GetModuleInfo() *model.Module {
	return info
}

func (d *LifxDriver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

//---------------------------------------------------------------[Bulb]----------------------------------------------------------------

func (d *LifxDriver) newLight(bulb *lifx.Bulb) (*devices.LightDevice, error) { //TODO cut this down!

	name := bulb.GetLabel()

	d.log.Infof("Making light with ID: %s Label: %s", bulb.GetLifxAddress(), name)

	light, err := devices.CreateLightDevice(d, &model.Device{
		NaturalID:     bulb.GetLifxAddress(),
		NaturalIDType: "lifx",
		Name:          &name,
		Signatures: &map[string]string{
			"ninja:manufacturer": "Lifx",
			"ninja:productName":  "Lifx Bulb",
			"ninja:productType":  "Light",
			"ninja:thingType":    "light",
		},
	}, d.conn)

	if err != nil {
		d.log.FatalError(err, "Could not create light device")
	}

	light.ApplyOnOff = func(state bool) error {
		var err error
		if state {
			err = d.client.LightOn(bulb)
		} else {
			err = d.client.LightOff(bulb)
		}
		if err != nil {
			return fmt.Errorf("Failed to set on-off state: %s", err)
		}
		return nil
	}

	light.ApplyLightState = func(state *devices.LightDeviceState) error {
		jsonState, _ := json.Marshal(state)
		d.log.Debugf("Sending light state to lifx bulb: %s", jsonState)

		if state.OnOff != nil {
			err := light.ApplyOnOff(*state.OnOff)
			if err != nil {
				return err
			}
		}

		if state.Color != nil || state.Brightness != nil || state.Transition != nil {

			if state.Brightness == nil {
				state.Brightness = &defaultBrightness
			}

			if state.Color == nil {
				state.Color = &channels.ColorState{
					Mode:       "hue",
					Saturation: &defaultSaturation,
				}
			}

			if state.Transition == nil {
				state.Transition = &defaultTransition
			}

			/*if state.Color == nil {
				return fmt.Errorf("Color value missing from batch set")
			}

			if state.Brightness == nil {
				return fmt.Errorf("Brightness value missing from batch set")
			}*/

			switch state.Color.Mode {
			case "hue":
				return d.client.LightColour(
					bulb,
					uint16(*state.Color.Hue*math.MaxUint16),
					uint16(*state.Color.Saturation*math.MaxUint16),
					uint16(*state.Brightness*math.MaxUint16),
					0,
					uint32(*state.Transition),
				)

			case "xy":
				//TODO: Lifx does not support XY color
				return fmt.Errorf("XY color mode is not yet supported in the Lifx driver")

			case "temperature":
				d.client.LightColour(
					bulb,
					0,
					0,
					uint16(*state.Brightness*math.MaxUint16),
					uint16(*state.Color.Temperature),
					uint32(*state.Transition),
				)

			default:
				return fmt.Errorf("Unknown color mode %s", state.Color.Mode)
			}

		}

		return nil
	}

	bulb.SetStateHandler(buildStateHandler(d, bulb, light))

	if err := light.EnableOnOffChannel(); err != nil {
		d.log.FatalError(err, "Could not enable lifx on-off channel")
	}
	if err := light.EnableBrightnessChannel(); err != nil {
		d.log.FatalError(err, "Could not enable lifx brightness channel")
	}
	if err := light.EnableColorChannel("temperature", "hue"); err != nil {
		d.log.FatalError(err, "Could not enable lifx color channel")
	}
	if err := light.EnableTransitionChannel(); err != nil {
		d.log.FatalError(err, "Could not enable lifx transition channel")
	}

	// extra channels for sensors
	illuminance := channels.NewIlluminanceChannel()

	if err := d.conn.ExportChannel(light, illuminance, "illuminance"); err != nil {
		d.log.FatalError(err, "Could not enable lifx illuminance channel")
	}

	d.log.Debugf("register illuminance channel for %s", bulb.GetLifxAddress())

	d.devices[bulb.GetLifxAddress()] = &lifxDevice{illuminance}

	return light, nil
}

func buildStateHandler(driver *LifxDriver, bulb *lifx.Bulb, light *devices.LightDevice) lifx.StateHandler {

	return func(bulbState *lifx.BulbState) {

		jsonState, _ := json.Marshal(bulbState)
		driver.log.Debugf("Incoming state: %s", jsonState)

		state := &devices.LightDeviceState{}

		onOff := int(bulbState.Power) > 0
		state.OnOff = &onOff

		brightness := float64(bulbState.Brightness) / math.MaxUint16
		state.Brightness = &brightness

		color := &channels.ColorState{}
		if bulbState.Saturation == 0 {
			color.Mode = "temperature"

			temperature := float64(bulbState.Kelvin)
			color.Temperature = &temperature

		} else {
			color.Mode = "hue"

			hue := float64(bulbState.Hue) / float64(math.MaxUint16)
			color.Hue = &hue

			saturation := float64(bulbState.Saturation) / float64(math.MaxUint16)
			color.Saturation = &saturation
		}

		state.Color = color

		light.SetLightState(state)

		// TODO use the state.Visible to indicate whether a globe is online/offline at the moment

	}
}

// this function publishes information from the sensors on the lifx bulb
// atm this is limited to illuminance
func (d *LifxDriver) publishSensorData() {
	c := time.Tick(30 * time.Second)
	for _ = range c {
		for _, bulb := range d.client.GetBulbs() {
			d.client.GetAmbientLight(bulb)
		}
	}
}

//---------------------------------------------------------------[Utils]----------------------------------------------------------------

var seenlights []*lifx.Bulb

func isUnique(newbulb *lifx.Bulb) bool {
	ret := true
	for _, bulb := range seenlights {
		if bulb.LifxAddress == newbulb.LifxAddress {
			ret = false
		}
	}
	return ret
}
<|endoftext|>