<commit_before>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package client

import (
	"github.com/zenoss/serviced"
	"net/rpc"
)

// A serviced client.
type ControlClient struct {
	addr      string
	rpcClient *rpc.Client
}

// Ensure that ControlClient implements the ControlPlane interface.
var _ serviced.ControlPlane = &ControlClient{}

// Create a new ControlClient.
func NewControlClient(addr string) (s *ControlClient, err error) {
	s = new(ControlClient)
	s.addr = addr
	rpcClient, err := rpc.DialHTTP("tcp", s.addr)
	s.rpcClient = rpcClient
	return s, err
}

// Return the matching hosts.
func (s *ControlClient) Close() (err error) {
	return s.rpcClient.Close()
}

// Return the matching hosts.
func (s *ControlClient) GetHosts(request serviced.EntityRequest, replyHosts *map[string]*serviced.Host) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHosts", request, replyHosts)
}

func (s *ControlClient) AddHost(host serviced.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddHost", host, unused)
}

func (s *ControlClient) UpdateHost(host serviced.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateHost", host, unused)
}

func (s *ControlClient) RemoveHost(hostId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveHost", hostId, unused)
}

func (s *ControlClient) GetServices(request serviced.EntityRequest, replyServices *[]*serviced.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServices", request, replyServices)
}

func (s *ControlClient) AddService(service serviced.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddService", service, unused)
}

func (s *ControlClient) UpdateService(service serviced.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateService", service, unused)
}

func (s *ControlClient) RemoveService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveService", serviceId, unused)
}

func (s *ControlClient) GetServicesForHost(hostId string, servicesForHost *[]*serviced.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServicesForHost", hostId, servicesForHost)
}

func (s *ControlClient) GetServiceLogs(serviceId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceLogs", serviceId, logs)
}

func (s *ControlClient) GetServiceStateLogs(serviceStateId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceStateLogs", serviceStateId, logs)
}

func (s *ControlClient) GetRunningServicesForHost(hostId string, runningServices *[]*serviced.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServicesForHost", hostId, runningServices)
}

func (s *ControlClient) GetRunningServices(request serviced.EntityRequest, runningServices *[]*serviced.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServices", request, runningServices)
}

func (s *ControlClient) GetServiceStates(serviceId string, states *[]*serviced.ServiceState) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServiceStates", serviceId, states)
}

func (s *ControlClient) StartService(serviceId string, hostId *string) (err error) {
	return s.rpcClient.Call("ControlPlane.StartService", serviceId, hostId)
}

func (s *ControlClient) RestartService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RestartService", serviceId, unused)
}

func (s *ControlClient) StopService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.StopService", serviceId, unused)
}

func (s *ControlClient) UpdateServiceState(state serviced.ServiceState, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateServiceState", state, unused)
}

func (s *ControlClient) GetResourcePools(request serviced.EntityRequest, pools *map[string]*serviced.ResourcePool) (err error) {
	return s.rpcClient.Call("ControlPlane.GetResourcePools", request, pools)
}

func (s *ControlClient) AddResourcePool(pool serviced.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddResourcePool", pool, unused)
}

func (s *ControlClient) UpdateResourcePool(pool serviced.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateResourcePool", pool, unused)
}

func (s *ControlClient) RemoveResourcePool(poolId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveResourcePool", poolId, unused)
}

func (s *ControlClient) GetHostsForResourcePool(poolId string, poolHosts *[]*serviced.PoolHost) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHostsForResourcePool", poolId, poolHosts)
}

func (s *ControlClient) AddHostToResourcePool(poolHost serviced.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddHostToResourcePool", poolHost, unused)
}

func (s *ControlClient) RemoveHostFromResourcePool(poolHost serviced.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveHostFromResourcePool", poolHost, unused)
}

func (s *ControlClient) DeployTemplate(request serviced.ServiceTemplateDeploymentRequest, unused *int) error {
	return s.rpcClient.Call("ControlPlane.DeployTemplate", request, unused)
}

func (s *ControlClient) GetServiceTemplates(unused int, serviceTemplates *map[string]*serviced.ServiceTemplate) error {
	return s.rpcClient.Call("ControlPlane.GetServiceTemplates", unused, serviceTemplates)
}

func (s *ControlClient) AddServiceTemplate(serviceTemplate serviced.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) UpdateServiceTemplate(serviceTemplate serviced.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.UpdateServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) RemoveServiceTemplate(serviceTemplateId string, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveServiceTemplate", serviceTemplateId, unused)
}
<commit_msg>Report where RPC is attempting to connect<commit_after>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package client

import (
	"github.com/zenoss/serviced"
	"net/rpc"
)

// A serviced client.
type ControlClient struct {
	addr      string
	rpcClient *rpc.Client
}

// Ensure that ControlClient implements the ControlPlane interface.
var _ serviced.ControlPlane = &ControlClient{}

// Create a new ControlClient.
func NewControlClient(addr string) (s *ControlClient, err error) {
	s = new(ControlClient)
	s.addr = addr
	glog.Infof("Connecting to %s", addr)
	rpcClient, err := rpc.DialHTTP("tcp", s.addr)
	s.rpcClient = rpcClient
	return s, err
}

// Return the matching hosts.
func (s *ControlClient) Close() (err error) {
	return s.rpcClient.Close()
}

// Return the matching hosts.
func (s *ControlClient) GetHosts(request serviced.EntityRequest, replyHosts *map[string]*serviced.Host) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHosts", request, replyHosts)
}

func (s *ControlClient) AddHost(host serviced.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddHost", host, unused)
}

func (s *ControlClient) UpdateHost(host serviced.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateHost", host, unused)
}

func (s *ControlClient) RemoveHost(hostId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveHost", hostId, unused)
}

func (s *ControlClient) GetServices(request serviced.EntityRequest, replyServices *[]*serviced.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServices", request, replyServices)
}

func (s *ControlClient) AddService(service serviced.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddService", service, unused)
}

func (s *ControlClient) UpdateService(service serviced.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateService", service, unused)
}

func (s *ControlClient) RemoveService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveService", serviceId, unused)
}

func (s *ControlClient) GetServicesForHost(hostId string, servicesForHost *[]*serviced.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServicesForHost", hostId, servicesForHost)
}

func (s *ControlClient) GetServiceLogs(serviceId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceLogs", serviceId, logs)
}

func (s *ControlClient) GetServiceStateLogs(serviceStateId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceStateLogs", serviceStateId, logs)
}

func (s *ControlClient) GetRunningServicesForHost(hostId string, runningServices *[]*serviced.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServicesForHost", hostId, runningServices)
}

func (s *ControlClient) GetRunningServices(request serviced.EntityRequest, runningServices *[]*serviced.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServices", request, runningServices)
}

func (s *ControlClient) GetServiceStates(serviceId string, states *[]*serviced.ServiceState) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServiceStates", serviceId, states)
}

func (s *ControlClient) StartService(serviceId string, hostId *string) (err error) {
	return s.rpcClient.Call("ControlPlane.StartService", serviceId, hostId)
}

func (s *ControlClient) RestartService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RestartService", serviceId, unused)
}

func (s *ControlClient) StopService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.StopService", serviceId, unused)
}

func (s *ControlClient) UpdateServiceState(state serviced.ServiceState, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateServiceState", state, unused)
}

func (s *ControlClient) GetResourcePools(request serviced.EntityRequest, pools *map[string]*serviced.ResourcePool) (err error) {
	return s.rpcClient.Call("ControlPlane.GetResourcePools", request, pools)
}

func (s *ControlClient) AddResourcePool(pool serviced.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddResourcePool", pool, unused)
}

func (s *ControlClient) UpdateResourcePool(pool serviced.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateResourcePool", pool, unused)
}

func (s *ControlClient) RemoveResourcePool(poolId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveResourcePool", poolId, unused)
}

func (s *ControlClient) GetHostsForResourcePool(poolId string, poolHosts *[]*serviced.PoolHost) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHostsForResourcePool", poolId, poolHosts)
}

func (s *ControlClient) AddHostToResourcePool(poolHost serviced.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddHostToResourcePool", poolHost, unused)
}

func (s *ControlClient) RemoveHostFromResourcePool(poolHost serviced.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveHostFromResourcePool", poolHost, unused)
}

func (s *ControlClient) DeployTemplate(request serviced.ServiceTemplateDeploymentRequest, unused *int) error {
	return s.rpcClient.Call("ControlPlane.DeployTemplate", request, unused)
}

func (s *ControlClient) GetServiceTemplates(unused int, serviceTemplates *map[string]*serviced.ServiceTemplate) error {
	return s.rpcClient.Call("ControlPlane.GetServiceTemplates", unused, serviceTemplates)
}

func (s *ControlClient) AddServiceTemplate(serviceTemplate serviced.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) UpdateServiceTemplate(serviceTemplate serviced.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.UpdateServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) RemoveServiceTemplate(serviceTemplateId string, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveServiceTemplate", serviceTemplateId, unused)
}
<|endoftext|>


<commit_before>package auth

import (
	"crypto/sha256"
	"encoding/base64"
	"math/rand"
	"strings"
	"time"
)

const (
	codeVerifierLength      = 32
	PKCEChallengeMethodS256 = "S256"
)

type CodeVerifier struct {
	Value string
}

func CreateCodeVerifier() *CodeVerifier {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	b := make([]byte, codeVerifierLength)
	for i := 0; i < codeVerifierLength; i++ {
		b[i] = byte(r.Intn(255))
	}
	return &CodeVerifier{encode(b)}
}

func (v *CodeVerifier) String() string {
	return v.Value
}

func (v *CodeVerifier) CodeChallengePlain() string {
	return v.Value
}

func (v *CodeVerifier) CodeChallengeS256() string {
	h := sha256.New()
	_, _ = h.Write([]byte(v.Value))
	return encode(h.Sum(nil))
}

func encode(msg []byte) string {
	encoded := base64.StdEncoding.EncodeToString(msg)
	encoded = strings.ReplaceAll(encoded, "+", "-")
	encoded = strings.ReplaceAll(encoded, "/", "_")
	encoded = strings.ReplaceAll(encoded, "=", "")
	return encoded
}
<commit_msg>pkg/client/auth: base64.RawURLEncoding is a thing<commit_after>package auth

import (
	"crypto/sha256"
	"encoding/base64"
	"math/rand"
	"time"
)

const (
	codeVerifierLength      = 32
	PKCEChallengeMethodS256 = "S256"
)

type CodeVerifier struct {
	Value string
}

func CreateCodeVerifier() *CodeVerifier {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	b := make([]byte, codeVerifierLength)
	for i := 0; i < codeVerifierLength; i++ {
		b[i] = byte(r.Intn(255))
	}
	return &CodeVerifier{base64.RawURLEncoding.EncodeToString(b)}
}

func (v *CodeVerifier) String() string {
	return v.Value
}

func (v *CodeVerifier) CodeChallengePlain() string {
	return v.Value
}

func (v *CodeVerifier) CodeChallengeS256() string {
	h := sha256.New()
	_, _ = h.Write([]byte(v.Value))
	return base64.RawURLEncoding.EncodeToString(h.Sum(nil))
}
<|endoftext|>


<commit_before>package dai

import (
	r "github.com/dancannon/gorethink"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/model"
	"github.com/materials-commons/mcstore/pkg/db/schema"
)

// rProjects implements the Projects interface for RethinkDB
type rProjects struct {
	session *r.Session
}

const OwnedProjects = true
const AllProjects = false

// NewRProjects creates a new instance of rProjects.
func NewRProjects(session *r.Session) rProjects {
	return rProjects{
		session: session,
	}
}

// ByID looks up a project by the given id.
func (p rProjects) ByID(id string) (*schema.Project, error) {
	var project schema.Project
	if err := model.Projects.Qs(p.session).ByID(id, &project); err != nil {
		return nil, err
	}
	return &project, nil
}

func (p rProjects) ByName(name string, owner string) (*schema.Project, error) {
	var projects []schema.Project
	rql := r.Table("projects").GetAllByIndex("owner", owner).
		Filter(r.Row.Field("name").Eq(name))

	if err := model.Projects.Qs(p.session).Rows(rql, &projects); err != nil {
		return nil, err
	}

	numProjects := len(projects)
	switch {
	case numProjects == 0:
		return nil, nil
	case numProjects > 1:
		app.Log.Critf("Projects table corrupted, there are multiple projects for user '%s' with name '%s'", owner, name)
		return &projects[0], nil
	default:
		return &projects[0], nil
	}
}

// ForUser
func (p rProjects) ForUser(user string, ownedOnly bool) ([]schema.Project, error) {
	if ownedOnly {
		return p.projectsOwnedByUser(user)
	} else {
		return p.projectsUserHasAccessTo(user)
	}
}

// projectsOwnerByUser returns all projects owned by the user.
func (p rProjects) projectsOwnedByUser(user string) ([]schema.Project, error) {
	var projects []schema.Project
	rql := r.Table("projects").GetAllByIndex("owner", user)
	if err := model.Projects.Qs(p.session).Rows(rql, &projects); err != nil {
		return nil, err
	}
	return projects, nil
}

// projectsUserHasAccessTo returns all projects that user can access, including
// projects the user owns.
func (p rProjects) projectsUserHasAccessTo(user string) ([]schema.Project, error) {
	var projects []schema.Project
	rql := r.Table("access").GetAllByIndex("user_id", user).
		EqJoin("project_id", r.Table("projects")).Zip()
	if err := model.Projects.Qs(p.session).Rows(rql, &projects); err != nil {
		return nil, err
	}
	return projects, nil
}

// Insert creates a new project for the given owner. It also creates the directory associated
// with the project.
func (p rProjects) Insert(project *schema.Project) (*schema.Project, error) {
	if project.DataDir != "" {
		return nil, app.ErrInvalid
	}

	var (
		newProject schema.Project
		newDir     *schema.Directory
		err        error
	)

	if err = model.Projects.Qs(p.session).Insert(project, &newProject); err != nil {
		return nil, app.ErrCreate
	}

	// Add owner to access list
	accessEntry := schema.NewAccess(newProject.ID, newProject.Name, newProject.Owner)
	if err := model.Access.Qs(p.session).Insert(&accessEntry, nil); err != nil {
		return nil, app.ErrCreate
	}

	dir := schema.NewDirectory(project.Name, project.Owner, newProject.ID, "")
	rdirs := NewRDirs(p.session)

	if newDir, err = rdirs.Insert(&dir); err != nil {
		return nil, app.ErrCreate
	}

	newProject.DataDir = newDir.ID
	if err = model.Projects.Qs(p.session).Update(newProject.ID, &newProject); err != nil {
		return &newProject, err
	}

	err = p.AddDirectories(&newProject, newDir.ID)

	return &newProject, err
}

// AddDirectories adds new directories to the project.
func (p rProjects) AddDirectories(project *schema.Project, directoryIDs ...string) error {
	var rverror error

	// Add each directory to the project2datadir table. If there are any errors,
	// remember that we saw an error, but continue on.
	for _, dirID := range directoryIDs {
		p2d := schema.Project2DataDir{
			ProjectID: project.ID,
			DataDirID: dirID,
		}
		if err := model.Projects.Qs(p.session).InsertRaw("project2datadir", p2d, nil); err != nil {
			rverror = app.ErrCreate
		}
	}

	return rverror
}

// HasDirectory checks if the given directoryID is in the given project.
func (p rProjects) HasDirectory(projectID, dirID string) bool {
	rql := model.ProjectDirs.T().GetAllByIndex("datadir_id", dirID)
	var proj2dir []schema.Project2DataDir
	if err := model.ProjectDirs.Qs(p.session).Rows(rql, &proj2dir); err != nil {
		return false
	}

	// Look for matching projectID
	for _, entry := range proj2dir {
		if entry.ProjectID == projectID {
			return true
		}
	}

	return false
}

// Get the access list for this project.
func (p rProjects) AccessList(projectID string) ([]schema.Access, error) {
	rql := r.Table("access").Filter(r.Row.Field("project_id").Eq(projectID))
	var access []schema.Access
	if err := model.Access.Qs(p.session).Rows(rql, &access); err != nil {
		return nil, err
	}
	return access, nil
}
<commit_msg>Don't add directory to project twice. The insert for a directory adds the directory to the project.<commit_after>package dai

import (
	r "github.com/dancannon/gorethink"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/model"
	"github.com/materials-commons/mcstore/pkg/db/schema"
)

// rProjects implements the Projects interface for RethinkDB
type rProjects struct {
	session *r.Session
}

const OwnedProjects = true
const AllProjects = false

// NewRProjects creates a new instance of rProjects.
func NewRProjects(session *r.Session) rProjects {
	return rProjects{
		session: session,
	}
}

// ByID looks up a project by the given id.
func (p rProjects) ByID(id string) (*schema.Project, error) {
	var project schema.Project
	if err := model.Projects.Qs(p.session).ByID(id, &project); err != nil {
		return nil, err
	}
	return &project, nil
}

func (p rProjects) ByName(name string, owner string) (*schema.Project, error) {
	var projects []schema.Project
	rql := r.Table("projects").GetAllByIndex("owner", owner).
		Filter(r.Row.Field("name").Eq(name))

	if err := model.Projects.Qs(p.session).Rows(rql, &projects); err != nil {
		return nil, err
	}

	numProjects := len(projects)
	switch {
	case numProjects == 0:
		return nil, nil
	case numProjects > 1:
		app.Log.Critf("Projects table corrupted, there are multiple projects for user '%s' with name '%s'", owner, name)
		return &projects[0], nil
	default:
		return &projects[0], nil
	}
}

// ForUser
func (p rProjects) ForUser(user string, ownedOnly bool) ([]schema.Project, error) {
	if ownedOnly {
		return p.projectsOwnedByUser(user)
	} else {
		return p.projectsUserHasAccessTo(user)
	}
}

// projectsOwnerByUser returns all projects owned by the user.
func (p rProjects) projectsOwnedByUser(user string) ([]schema.Project, error) {
	var projects []schema.Project
	rql := r.Table("projects").GetAllByIndex("owner", user)
	if err := model.Projects.Qs(p.session).Rows(rql, &projects); err != nil {
		return nil, err
	}
	return projects, nil
}

// projectsUserHasAccessTo returns all projects that user can access, including
// projects the user owns.
func (p rProjects) projectsUserHasAccessTo(user string) ([]schema.Project, error) {
	var projects []schema.Project
	rql := r.Table("access").GetAllByIndex("user_id", user).
		EqJoin("project_id", r.Table("projects")).Zip()
	if err := model.Projects.Qs(p.session).Rows(rql, &projects); err != nil {
		return nil, err
	}
	return projects, nil
}

// Insert creates a new project for the given owner. It also creates the directory associated
// with the project.
func (p rProjects) Insert(project *schema.Project) (*schema.Project, error) {
	if project.DataDir != "" {
		return nil, app.ErrInvalid
	}

	var (
		newProject schema.Project
		newDir     *schema.Directory
		err        error
	)

	if err = model.Projects.Qs(p.session).Insert(project, &newProject); err != nil {
		return nil, app.ErrCreate
	}

	// Add owner to access list
	accessEntry := schema.NewAccess(newProject.ID, newProject.Name, newProject.Owner)
	if err := model.Access.Qs(p.session).Insert(&accessEntry, nil); err != nil {
		return nil, app.ErrCreate
	}

	dir := schema.NewDirectory(project.Name, project.Owner, newProject.ID, "")
	rdirs := NewRDirs(p.session)

	if newDir, err = rdirs.Insert(&dir); err != nil {
		return nil, app.ErrCreate
	}

	newProject.DataDir = newDir.ID
	if err = model.Projects.Qs(p.session).Update(newProject.ID, &newProject); err != nil {
		return &newProject, err
	}

	return &newProject, nil
}

// AddDirectories adds new directories to the project.
func (p rProjects) AddDirectories(project *schema.Project, directoryIDs ...string) error {
	var rverror error

	// Add each directory to the project2datadir table. If there are any errors,
	// remember that we saw an error, but continue on.
	for _, dirID := range directoryIDs {
		p2d := schema.Project2DataDir{
			ProjectID: project.ID,
			DataDirID: dirID,
		}
		if err := model.Projects.Qs(p.session).InsertRaw("project2datadir", p2d, nil); err != nil {
			rverror = app.ErrCreate
		}
	}

	return rverror
}

// HasDirectory checks if the given directoryID is in the given project.
func (p rProjects) HasDirectory(projectID, dirID string) bool {
	rql := model.ProjectDirs.T().GetAllByIndex("datadir_id", dirID)
	var proj2dir []schema.Project2DataDir
	if err := model.ProjectDirs.Qs(p.session).Rows(rql, &proj2dir); err != nil {
		return false
	}

	// Look for matching projectID
	for _, entry := range proj2dir {
		if entry.ProjectID == projectID {
			return true
		}
	}

	return false
}

// Get the access list for this project.
func (p rProjects) AccessList(projectID string) ([]schema.Access, error) {
	rql := r.Table("access").Filter(r.Row.Field("project_id").Eq(projectID))
	var access []schema.Access
	if err := model.Access.Qs(p.session).Rows(rql, &access); err != nil {
		return nil, err
	}
	return access, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Jesse Sipprell. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

package keyctl

import (
	"golang.org/x/sys/unix"
)

type keyID int32

func (id keyID) ID() int32 {
	return int32(id)
}

func newKeyring(id keyID) (*keyring, error) {
	r1, err := unix.KeyctlGetKeyringID(int(id), true)
	if err != nil {
		return nil, err
	}

	if id < 0 {
		r1 = int(id)
	}
	return &keyring{id: keyID(r1)}, nil
}
<commit_msg>Remove pkg/keyctl/keyID.ID<commit_after>// Copyright 2015 Jesse Sipprell. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

package keyctl

import (
	"golang.org/x/sys/unix"
)

type keyID int32

func newKeyring(id keyID) (*keyring, error) {
	r1, err := unix.KeyctlGetKeyringID(int(id), true)
	if err != nil {
		return nil, err
	}

	if id < 0 {
		r1 = int(id)
	}
	return &keyring{id: keyID(r1)}, nil
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package api

import (
	"fmt"
)

// DropMin numbers less than this are non-drop reason codes
var DropMin uint8 = 130

// DropInvalid is the Invalid packet reason.
var DropInvalid uint8 = 2

// These values are shared with bpf/lib/common.h and api/v1/flow/flow.proto.
var errors = map[uint8]string{
	0:   "Success",
	2:   "Invalid packet",
	3:   "Interface",
	4:   "Interface Decrypted",
	5:   "LB, sock cgroup: No backend slot entry found",
	6:   "LB, sock cgroup: No backend entry found",
	7:   "LB, sock cgroup: Reverse entry update failed",
	8:   "LB, sock cgroup: Reverse entry stale",
	9:   "Fragmented packet",
	10:  "Fragmented packet entry update failed",
	11:  "Missed tail call to custom program",
	130: "Invalid source mac",      // Unused
	131: "Invalid destination mac", // Unused
	132: "Invalid source ip",
	133: "Policy denied",
	134: "Invalid packet",
	135: "CT: Truncated or invalid header",
	136: "Fragmentation needed",
	137: "CT: Unknown L4 protocol",
	138: "CT: Can't create entry from packet", // Unused
	139: "Unsupported L3 protocol",
	140: "Missed tail call",
	141: "Error writing to packet",
	142: "Unknown L4 protocol",
	143: "Unknown ICMPv4 code",
	144: "Unknown ICMPv4 type",
	145: "Unknown ICMPv6 code",
	146: "Unknown ICMPv6 type",
	147: "Error retrieving tunnel key",
	148: "Error retrieving tunnel options", // Unused
	149: "Invalid Geneve option",           // Unused
	150: "Unknown L3 target address",
	151: "Stale or unroutable IP",
	152: "No matching local container found", // Unused
	153: "Error while correcting L3 checksum",
	154: "Error while correcting L4 checksum",
	155: "CT: Map insertion failed",
	156: "Invalid IPv6 extension header",
	157: "IP fragmentation not supported",
	158: "Service backend not found",
	160: "No tunnel/encapsulation endpoint (datapath BUG!)",
	161: "Failed to insert into proxymap", // Unused
	162: "Reached EDT rate-limiting drop horizon",
	163: "Unknown connection tracking state",
	164: "Local host is unreachable",
	165: "No configuration available to perform policy decision", // Unused
	166: "Unsupported L2 protocol",
	167: "No mapping for NAT masquerade",
	168: "Unsupported protocol for NAT masquerade",
	169: "FIB lookup failed",
	170: "Encapsulation traffic is prohibited",
	171: "Invalid identity",
	172: "Unknown sender",
	173: "NAT not needed",
	174: "Is a ClusterIP",
	175: "First logical datagram fragment not found",
	176: "Forbidden ICMPv6 message",
	177: "Denied by LB src range check",
	178: "Socket lookup failed",
	179: "Socket assign failed",
	180: "Proxy redirection not supported for protocol",
	181: "Policy denied by denylist",
	182: "VLAN traffic disallowed by VLAN filter",
	183: "Incorrect VNI from VTEP",
}

// DropReason prints the drop reason in a human readable string
func DropReason(reason uint8) string {
	if err, ok := errors[reason]; ok {
		return err
	}
	return fmt.Sprintf("%d", reason)
}
<commit_msg>monitor: Add drop reason for DROP_NAT_46X64_DISABLED<commit_after>// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package api

import (
	"fmt"
)

// DropMin numbers less than this are non-drop reason codes
var DropMin uint8 = 130

// DropInvalid is the Invalid packet reason.
var DropInvalid uint8 = 2

// These values are shared with bpf/lib/common.h and api/v1/flow/flow.proto.
var errors = map[uint8]string{
	0:   "Success",
	2:   "Invalid packet",
	3:   "Interface",
	4:   "Interface Decrypted",
	5:   "LB, sock cgroup: No backend slot entry found",
	6:   "LB, sock cgroup: No backend entry found",
	7:   "LB, sock cgroup: Reverse entry update failed",
	8:   "LB, sock cgroup: Reverse entry stale",
	9:   "Fragmented packet",
	10:  "Fragmented packet entry update failed",
	11:  "Missed tail call to custom program",
	130: "Invalid source mac",      // Unused
	131: "Invalid destination mac", // Unused
	132: "Invalid source ip",
	133: "Policy denied",
	134: "Invalid packet",
	135: "CT: Truncated or invalid header",
	136: "Fragmentation needed",
	137: "CT: Unknown L4 protocol",
	138: "CT: Can't create entry from packet", // Unused
	139: "Unsupported L3 protocol",
	140: "Missed tail call",
	141: "Error writing to packet",
	142: "Unknown L4 protocol",
	143: "Unknown ICMPv4 code",
	144: "Unknown ICMPv4 type",
	145: "Unknown ICMPv6 code",
	146: "Unknown ICMPv6 type",
	147: "Error retrieving tunnel key",
	148: "Error retrieving tunnel options", // Unused
	149: "Invalid Geneve option",           // Unused
	150: "Unknown L3 target address",
	151: "Stale or unroutable IP",
	152: "No matching local container found", // Unused
	153: "Error while correcting L3 checksum",
	154: "Error while correcting L4 checksum",
	155: "CT: Map insertion failed",
	156: "Invalid IPv6 extension header",
	157: "IP fragmentation not supported",
	158: "Service backend not found",
	160: "No tunnel/encapsulation endpoint (datapath BUG!)",
	161: "NAT 46/64 not enabled",
	162: "Reached EDT rate-limiting drop horizon",
	163: "Unknown connection tracking state",
	164: "Local host is unreachable",
	165: "No configuration available to perform policy decision", // Unused
	166: "Unsupported L2 protocol",
	167: "No mapping for NAT masquerade",
	168: "Unsupported protocol for NAT masquerade",
	169: "FIB lookup failed",
	170: "Encapsulation traffic is prohibited",
	171: "Invalid identity",
	172: "Unknown sender",
	173: "NAT not needed",
	174: "Is a ClusterIP",
	175: "First logical datagram fragment not found",
	176: "Forbidden ICMPv6 message",
	177: "Denied by LB src range check",
	178: "Socket lookup failed",
	179: "Socket assign failed",
	180: "Proxy redirection not supported for protocol",
	181: "Policy denied by denylist",
	182: "VLAN traffic disallowed by VLAN filter",
	183: "Incorrect VNI from VTEP",
}

// DropReason prints the drop reason in a human readable string
func DropReason(reason uint8) string {
	if err, ok := errors[reason]; ok {
		return err
	}
	return fmt.Sprintf("%d", reason)
}
<|endoftext|>


<commit_before>/*
Package dispatch is used to dispatch irc messages to event handlers in an
concurrent fashion. It supports various event handler types to easily
extract information from events, as well as define more succint handlers.
*/
package dispatch

import (
	"runtime"
	"strings"
	"sync"

	"github.com/aarondl/ultimateq/irc"
	"github.com/inconshreveable/log15"
)

// DispatchCore is a core for any dispatching mechanisms that includes a sync'd
// list of channels, channel identification services, and a waiter to
// synchronize the exit of all the event handlers sharing this core.
type DispatchCore struct {
	log     log15.Logger
	waiter  sync.WaitGroup
	chans   []string
	protect sync.RWMutex
}

// NewDispatchCore initializes a dispatch core
func NewDispatchCore(logger log15.Logger, chans ...string) *DispatchCore {
	d := &DispatchCore{log: logger}
	d.channels(chans)

	return d
}

// SetChannels sets the active channels for this dispatcher.
func (d *DispatchCore) SetChannels(chans []string) {
	d.protect.Lock()
	d.channels(chans)
	d.protect.Unlock()
}

// Channels gets the active channels for this dispatcher.
func (d *DispatchCore) Channels() (chans []string) {
	d.protect.Lock()
	defer d.protect.Unlock()

	if d.chans == nil {
		return
	}
	chans = make([]string, len(d.chans))
	copy(chans, d.chans)
	return
}

// AddChannels adds channels to the active channels for this dispatcher.
func (d *DispatchCore) AddChannels(chans ...string) {
	if 0 == len(chans) {
		return
	}
	d.protect.Lock()
	defer d.protect.Unlock()

	if d.chans == nil {
		d.chans = make([]string, 0, len(chans))
	}

	for i := 0; i < len(chans); i++ {
		addchan := strings.ToLower(chans[i])
		found := false
		for j, length := 0, len(d.chans); j < length; j++ {
			if d.chans[j] == addchan {
				found = true
				break
			}
		}
		if !found {
			d.chans = append(d.chans, addchan)
		}
	}
}

// RemoveChannels removes channels to the active channels for this dispatcher.
func (d *DispatchCore) RemoveChannels(chans ...string) {
	if 0 == len(chans) {
		return
	}
	d.protect.Lock()
	defer d.protect.Unlock()

	if d.chans == nil || 0 == len(d.chans) {
		return
	}

	for i := 0; i < len(chans); i++ {
		removechan := strings.ToLower(chans[i])
		for j, length := 0, len(d.chans); j < length; j++ {
			if d.chans[j] == removechan {
				if length == 1 {
					d.chans = nil
					return
				}
				if j < length-1 {
					d.chans[j], d.chans[length-1] =
						d.chans[length-1], d.chans[j]
				}
				d.chans = d.chans[:length-1]
				length--
			}
		}
	}
}

// channels sets the active channels for this dispatcher. Not thread
// safe.
func (d *DispatchCore) channels(chans []string) {
	length := len(chans)
	if length == 0 {
		d.chans = nil
	} else {
		d.chans = make([]string, length)
		for i := 0; i < length; i++ {
			d.chans[i] = strings.ToLower(chans[i])
		}
	}
}

// HandlerStarted tells the core that a handler has started and it should be
// waited on.
func (d *DispatchCore) HandlerStarted() {
	d.waiter.Add(1)
}

// HandlerFinished tells the core that a handler has ended.
func (d *DispatchCore) HandlerFinished() {
	d.waiter.Done()
}

// WaitForHandlers waits for the unfinished handlers to finish.
func (d *DispatchCore) WaitForHandlers() {
	d.waiter.Wait()
}

// CheckTarget describes a dispatching target. It checks both if it is a
// channel, and if it is a channel, if that channel is an active one for
// this dispatchcore.
func (d *DispatchCore) CheckTarget(ev *irc.Event) (isChan, hasChan bool) {
	d.protect.RLock()
	defer d.protect.RUnlock()

	isChan = ev.IsTargetChan()
	hasChan = isChan && d.hasChannel(strings.ToLower(ev.Target()))
	return isChan, hasChan
}

// hasChannel checks to see if the dispatch core's channel list includes a
// channel.
func (d *DispatchCore) hasChannel(channel string) bool {
	if d.chans == nil {
		return true
	}

	targ := strings.ToLower(channel)
	for i := 0; i < len(d.chans); i++ {
		if targ == d.chans[i] {
			return true
		}
	}
	return false
}

// PanicHandler catches any panics and logs a stack trace
func (d *DispatchCore) PanicHandler() {
	recovered := recover()
	if recovered == nil {
		return
	}
	buf := make([]byte, 1024)
	runtime.Stack(buf, false)
	d.log.Error("Handler failed", "panic", recovered)
	d.log.Error(string(buf))
}
<commit_msg>Changes hasChannel() check from null-checking to checking length of d.chans > 0 entries.<commit_after>/*
Package dispatch is used to dispatch irc messages to event handlers in an
concurrent fashion. It supports various event handler types to easily
extract information from events, as well as define more succint handlers.
*/
package dispatch

import (
	"runtime"
	"strings"
	"sync"

	"github.com/aarondl/ultimateq/irc"
	"github.com/inconshreveable/log15"
)

// DispatchCore is a core for any dispatching mechanisms that includes a sync'd
// list of channels, channel identification services, and a waiter to
// synchronize the exit of all the event handlers sharing this core.
type DispatchCore struct {
	log     log15.Logger
	waiter  sync.WaitGroup
	chans   []string
	protect sync.RWMutex
}

// NewDispatchCore initializes a dispatch core
func NewDispatchCore(logger log15.Logger, chans ...string) *DispatchCore {
	d := &DispatchCore{log: logger}
	d.channels(chans)

	return d
}

// SetChannels sets the active channels for this dispatcher.
func (d *DispatchCore) SetChannels(chans []string) {
	d.protect.Lock()
	d.channels(chans)
	d.protect.Unlock()
}

// Channels gets the active channels for this dispatcher.
func (d *DispatchCore) Channels() (chans []string) {
	d.protect.Lock()
	defer d.protect.Unlock()

	if d.chans == nil {
		return
	}
	chans = make([]string, len(d.chans))
	copy(chans, d.chans)
	return
}

// AddChannels adds channels to the active channels for this dispatcher.
func (d *DispatchCore) AddChannels(chans ...string) {
	if 0 == len(chans) {
		return
	}
	d.protect.Lock()
	defer d.protect.Unlock()

	if d.chans == nil {
		d.chans = make([]string, 0, len(chans))
	}

	for i := 0; i < len(chans); i++ {
		addchan := strings.ToLower(chans[i])
		found := false
		for j, length := 0, len(d.chans); j < length; j++ {
			if d.chans[j] == addchan {
				found = true
				break
			}
		}
		if !found {
			d.chans = append(d.chans, addchan)
		}
	}
}

// RemoveChannels removes channels to the active channels for this dispatcher.
func (d *DispatchCore) RemoveChannels(chans ...string) {
	if 0 == len(chans) {
		return
	}
	d.protect.Lock()
	defer d.protect.Unlock()

	if d.chans == nil || 0 == len(d.chans) {
		return
	}

	for i := 0; i < len(chans); i++ {
		removechan := strings.ToLower(chans[i])
		for j, length := 0, len(d.chans); j < length; j++ {
			if d.chans[j] == removechan {
				if length == 1 {
					d.chans = nil
					return
				}
				if j < length-1 {
					d.chans[j], d.chans[length-1] =
						d.chans[length-1], d.chans[j]
				}
				d.chans = d.chans[:length-1]
				length--
			}
		}
	}
}

// channels sets the active channels for this dispatcher. Not thread
// safe.
func (d *DispatchCore) channels(chans []string) {
	length := len(chans)
	if length == 0 {
		d.chans = nil
	} else {
		d.chans = make([]string, length)
		for i := 0; i < length; i++ {
			d.chans[i] = strings.ToLower(chans[i])
		}
	}
}

// HandlerStarted tells the core that a handler has started and it should be
// waited on.
func (d *DispatchCore) HandlerStarted() {
	d.waiter.Add(1)
}

// HandlerFinished tells the core that a handler has ended.
func (d *DispatchCore) HandlerFinished() {
	d.waiter.Done()
}

// WaitForHandlers waits for the unfinished handlers to finish.
func (d *DispatchCore) WaitForHandlers() {
	d.waiter.Wait()
}

// CheckTarget describes a dispatching target. It checks both if it is a
// channel, and if it is a channel, if that channel is an active one for
// this dispatchcore.
func (d *DispatchCore) CheckTarget(ev *irc.Event) (isChan, hasChan bool) {
	d.protect.RLock()
	defer d.protect.RUnlock()

	isChan = ev.IsTargetChan()
	hasChan = isChan && d.hasChannel(strings.ToLower(ev.Target()))
	return isChan, hasChan
}

// hasChannel checks to see if the dispatch core's channel list includes a
// channel.
func (d *DispatchCore) hasChannel(channel string) bool {
	if len(d.chans) == 0 {
		return true
	}

	targ := strings.ToLower(channel)
	for i := 0; i < len(d.chans); i++ {
		if targ == d.chans[i] {
			return true
		}
	}
	return false
}

// PanicHandler catches any panics and logs a stack trace
func (d *DispatchCore) PanicHandler() {
	recovered := recover()
	if recovered == nil {
		return
	}
	buf := make([]byte, 1024)
	runtime.Stack(buf, false)
	d.log.Error("Handler failed", "panic", recovered)
	d.log.Error(string(buf))
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/UHERO/rest-api/common"
	"github.com/UHERO/rest-api/data"
	"strings"
)

const authPrefix = "Bearer "

// CreateApplication returns a handler that will create applications
func CreateApplication(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var dataResource ApplicationResource
		// Decode the incoming Task json
		err := json.NewDecoder(r.Body).Decode(&dataResource)
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"Invalid Application Data",
				500,
			)
			return
		}
		application := &dataResource.Data
		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		log.Printf("username: %s", appClaims.Username)
		_, err = applicationRepository.CreateApplication(appClaims.Username, application)
		if err != nil {
			panic(err)
		}
		j, err := json.Marshal(ApplicationResource{Data: *application})
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"An unexpected error has occurred",
				500,
			)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		w.Write(j)
	}
}

func ValidApiKey(applicationRepository *data.ApplicationRepository) func(http.ResponseWriter, *http.Request, http.HandlerFunc) {
	return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		authString := r.Header.Get("Authorization")
		if !strings.HasPrefix(authString, authPrefix) {
			common.DisplayAppError(
				w,
				errors.New("No Bearer Token"),
				"No Bearer Token!",
				401,
			)
			return
		}
		applications, err := applicationRepository.GetApplicationsByApiKey(strings.TrimPrefix(authString, authPrefix))
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"Error Veryfying API Key!",
				500,
			)
			return
		}
		if len(applications) == 0 {
			common.DisplayAppError(
				w,
				err,
				"Invalid API Key!",
				401,
			)
			return
		}
		origin := r.Header.Get("Origin")
		if strings.HasPrefix(origin, "http://localhost") ||
			strings.HasPrefix(origin, applications[0].Hostname) ||
			origin == "" {
			w.Header().Add("Access-Control-Allow-Origin", origin)
		}
		log.Print("Off to the next handler")
		next(w, r)
	}
}

func CORSOptionsHandler(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if (r.Method == http.MethodOptions) {
		w.Header().Add("Access-Control-Allow-Origin", "*")
		w.Header().Add("Access-Control-Allow-Credentials", "true")
		w.Header().Add("Access-Control-Allow-Methods", "GET, POST")
		w.Header().Add("Access-Control-Allow-Headers", "authorization")
		w.WriteHeader(http.StatusOK)
		w.Write(nil)
		return
	}
	next(w, r)
}

// UpdateApplication will return a handler for updating an application
func UpdateApplication(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var dataResource ApplicationResource
		// Decode the incoming application json
		err := json.NewDecoder(r.Body).Decode(&dataResource)
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"Invalid Application Data",
				500,
			)
			return
		}
		vars := mux.Vars(r)
		id, err := strconv.ParseInt(vars["id"], 10, 64)
		if err != nil {
			panic(err)
		}
		application := &dataResource.Data
		application.Id = id

		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		log.Printf("username: %s", appClaims.Username)

		_, err = applicationRepository.UpdateApplication(appClaims.Username, application)
		if err != nil {
			panic(err)
		}

		j, err := json.Marshal(ApplicationResource{Data: *application})
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"An unexpected error has occurred",
				500,
			)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
	}
}

// ReadApplications returns a handler that returns all of the user's applications
func ReadApplications(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		applications, err := applicationRepository.GetAllApplications(appClaims.Username)
		if err != nil {
			panic(err)
		}
		j, err := json.Marshal(ApplicationsResource{Data: applications})
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"An unexpected error has occurred",
				500,
			)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
	}
}

// DeleteApplication returns a handler that deletes an application
func DeleteApplication(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		id, err := strconv.ParseInt(vars["id"], 10, 64)
		if err != nil {
			panic(err)
		}

		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		_, err = applicationRepository.DeleteApplication(appClaims.Username, id)
		if err != nil {
			panic(err)
		}

		w.WriteHeader(http.StatusNoContent)
	}
}
<commit_msg>changed HasPrefix to Contains for comparing Origin to ApiApplication Hostname<commit_after>package controllers

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/UHERO/rest-api/common"
	"github.com/UHERO/rest-api/data"
	"strings"
)

const authPrefix = "Bearer "

// CreateApplication returns a handler that will create applications
func CreateApplication(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var dataResource ApplicationResource
		// Decode the incoming Task json
		err := json.NewDecoder(r.Body).Decode(&dataResource)
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"Invalid Application Data",
				500,
			)
			return
		}
		application := &dataResource.Data
		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		log.Printf("username: %s", appClaims.Username)
		_, err = applicationRepository.CreateApplication(appClaims.Username, application)
		if err != nil {
			panic(err)
		}
		j, err := json.Marshal(ApplicationResource{Data: *application})
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"An unexpected error has occurred",
				500,
			)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		w.Write(j)
	}
}

func ValidApiKey(applicationRepository *data.ApplicationRepository) func(http.ResponseWriter, *http.Request, http.HandlerFunc) {
	return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		authString := r.Header.Get("Authorization")
		if !strings.HasPrefix(authString, authPrefix) {
			common.DisplayAppError(
				w,
				errors.New("No Bearer Token"),
				"No Bearer Token!",
				401,
			)
			return
		}
		applications, err := applicationRepository.GetApplicationsByApiKey(strings.TrimPrefix(authString, authPrefix))
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"Error Veryfying API Key!",
				500,
			)
			return
		}
		if len(applications) == 0 {
			common.DisplayAppError(
				w,
				err,
				"Invalid API Key!",
				401,
			)
			return
		}
		origin := r.Header.Get("Origin")
		if strings.HasPrefix(origin, "http://localhost") ||
			strings.Contains(origin, applications[0].Hostname) ||
			origin == "" {
			w.Header().Add("Access-Control-Allow-Origin", origin)
		}
		log.Print("Off to the next handler")
		next(w, r)
	}
}

func CORSOptionsHandler(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if (r.Method == http.MethodOptions) {
		w.Header().Add("Access-Control-Allow-Origin", "*")
		w.Header().Add("Access-Control-Allow-Credentials", "true")
		w.Header().Add("Access-Control-Allow-Methods", "GET, POST")
		w.Header().Add("Access-Control-Allow-Headers", "authorization")
		w.WriteHeader(http.StatusOK)
		w.Write(nil)
		return
	}
	next(w, r)
}

// UpdateApplication will return a handler for updating an application
func UpdateApplication(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var dataResource ApplicationResource
		// Decode the incoming application json
		err := json.NewDecoder(r.Body).Decode(&dataResource)
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"Invalid Application Data",
				500,
			)
			return
		}
		vars := mux.Vars(r)
		id, err := strconv.ParseInt(vars["id"], 10, 64)
		if err != nil {
			panic(err)
		}
		application := &dataResource.Data
		application.Id = id

		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		log.Printf("username: %s", appClaims.Username)

		_, err = applicationRepository.UpdateApplication(appClaims.Username, application)
		if err != nil {
			panic(err)
		}

		j, err := json.Marshal(ApplicationResource{Data: *application})
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"An unexpected error has occurred",
				500,
			)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
	}
}

// ReadApplications returns a handler that returns all of the user's applications
func ReadApplications(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		applications, err := applicationRepository.GetAllApplications(appClaims.Username)
		if err != nil {
			panic(err)
		}
		j, err := json.Marshal(ApplicationsResource{Data: applications})
		if err != nil {
			common.DisplayAppError(
				w,
				err,
				"An unexpected error has occurred",
				500,
			)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
	}
}

// DeleteApplication returns a handler that deletes an application
func DeleteApplication(applicationRepository data.Repository) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		id, err := strconv.ParseInt(vars["id"], 10, 64)
		if err != nil {
			panic(err)
		}

		appClaims, ok := common.FromContext(r.Context())
		if ok != true {
			panic(errors.New("cannot get value from context"))
		}
		_, err = applicationRepository.DeleteApplication(appClaims.Username, id)
		if err != nil {
			panic(err)
		}

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


<commit_before>/*
 The markov plugin listens passively to all messages and creates markov
 chains for each nick.
*/
package markov

import (
	"fmt"
	"github.com/collinvandyck/gesture/core"
	"github.com/collinvandyck/gesture/state"
	"log"
	"math/rand"
	"strings"
	"sync"
)

type markovState struct {
	PrefixLength int
	Chains       map[string]map[string][]string // map[user]map[prefix][]chains
}

const (
	maxWords       = 100
	maxChainLength = 1000
)

var (
	// todo: make prefix length configurable
	markov      = markovState{PrefixLength: 1, Chains: make(map[string]map[string][]string)}
	mutex       sync.Mutex
	pluginState = state.NewState("markov")
)

func Create(bot *core.Gobot, config map[string]interface{}) {
	if err := pluginState.Load(&markov); err != nil {
		log.Printf("Could not load plugin state: %s", err)
	}

	bot.ListenFor("^ *markov *$", func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
		output, err := generateRandom()
		if err != nil {
			return bot.Error(err)
		}
		msg.Send(output)
		return bot.KeepGoing()
	})

	// generate a chain for the specified user
	bot.ListenFor("^ *markov +(.+)", func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
		output, err := generate(matches[1])
		if err != nil {
			return bot.Error(err)
		}
		msg.Send(output)
		return bot.KeepGoing()
	})

	// listen to everything
	bot.ListenFor("(.*)", func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
		user := msg.User
		text := matches[0]
		record(user, text)
		return bot.KeepGoing()
	})
}

// getChainMap gets the map for a particular user, or a new map with all of the data for all users
func getChainMap(user string) (map[string][]string, error) {
	if user != "" {
		userMap, ok := markov.Chains[user]
		if !ok {
			return nil, fmt.Errorf("No chain could be found for %s", user)
		}
		return userMap, nil
	}
	if len(markov.Chains) == 0 {
		return nil, fmt.Errorf("No chains could be found")
	}
	// combine all of the users' maps
	result := make(map[string][]string)
	for _, userChainMap := range markov.Chains {
		// userChainMap is a map[string][]string
		for prefix, userChain := range userChainMap {
			chain := result[prefix]
			if chain != nil {
				chain = make([]string,0)
			}
			for _, chainItem := range userChain {
				chain = append(chain, chainItem)
			}
			result[prefix] = chain
		}
	}
	return result, nil
}

func generateRandom() (string, error) {
	return generate("")
}

func generate(user string) (string, error) {
	chainMap, err := getChainMap(user)
	if err != nil {
		return "", err
	}
	p := newPrefix(markov.PrefixLength)
	var words []string
	for i := 0; i < maxWords; i++ {
		choices := chainMap[p.String()]
		if len(choices) == 0 {
			break
		}
		next := choices[rand.Intn(len(choices))]
		words = append(words, next)
		p.shift(next)
	}
	return strings.Join(words, " "), nil
}

// record breaks up the text into tokens and then creates chains for that user
func record(user, text string) error {
	p := newPrefix(markov.PrefixLength)
	tokens := strings.Split(text, " ")
	userMap, ok := markov.Chains[user]
	if !ok {
		markov.Chains[user] = make(map[string][]string)
		userMap = markov.Chains[user]
	}
	for _, token := range tokens {
		if strings.HasPrefix("http", token) {
			continue
		}
		str := p.String()
		if !contains(userMap[str], token) {
			userMap[str] = append(userMap[str], token)
			p.shift(token)
			// only allow maxChainLength items in a particular chain for a prefix
			if len(userMap[str]) > maxChainLength {
				userMap[str] = userMap[str][len(userMap[str])-maxChainLength:]
			}
		}
	}
	return pluginState.Save(markov, false)
}

func contains(tokens []string, token string) bool {
	for _, word := range tokens {
		if word == token {
			return true
		}
	}
	return false
}
<commit_msg>add local changes for insults, and directly addressing the bot<commit_after>/*
 The markov plugin listens passively to all messages and creates markov
 chains for each nick.
*/
package markov

import (
	"fmt"
	"github.com/collinvandyck/gesture/core"
	"github.com/collinvandyck/gesture/state"
	"log"
	"math/rand"
	"strings"
	"strconv"
	"sync"
	"time"
)

type markovState struct {
	PrefixLength int
	Chains       map[string]map[string][]string // map[user]map[prefix][]chains
}

const (
	maxWords       = 20
	maxChainLength = 1000
)

var (
	// todo: make prefix length configurable
	markov      = markovState{PrefixLength: 1, Chains: make(map[string]map[string][]string)}
	mutex       sync.Mutex
	lastInduction	time.Time
	pluginState = state.NewState("markov")
)

var induction = [...]string {"1. Welcome to ##gucs. This is an *un*official GU CS channel. Banter and realtalk are welcome.",
                             "2. Please report your dealios to Knifa on a regular basis.",
                             "3. Please familiarise yourself with the channel anthem: https://www.youtube.com/watch?v=fA2AiedVCmw",
                             "4. Play nice with the bot."}

var insult_a = [...]string {"a lazy", "a stupid", "an insecure", "an idiotic", "a slimy", "a slutty", "a smelly", "a pompous", "a communist", "an elitist"}
var insult_b = [...]string {"douche", "ass", "turd", "rectum", "butt", "cock", "shit", "crotch", "prick", "boner", "dick"}
var insult_c = [...]string {"pilot", "canoe", "captain", "pirate", "hammer", "jockey", "waffle", "goblin", "biscuit", "clown", "monster", "hound", "dragon", "balloon"}

func Create(bot *core.Gobot, config map[string]interface{}) {
	if err := pluginState.Load(&markov); err != nil {
		log.Printf("Could not load plugin state: %s", err)
	}

	bot.ListenFor("^ *!insult *$", func(msg core.Message, matches []string) core.Response {
		user := msg.User

		msg.Send(user+", you're nothing but "+insult_a[rand.Intn(len(insult_a))]+" "+insult_b[rand.Intn(len(insult_b))]+"-"+insult_c[rand.Intn(len(insult_c))])
		return bot.Stop()
	})

	bot.ListenFor("^ *!insult +(.+)*$", func(msg core.Message, matches []string) core.Response {
		msg.Send(matches[1]+", you're nothing but "+insult_a[rand.Intn(len(insult_a))]+" "+insult_b[rand.Intn(len(insult_b))]+"-"+insult_c[rand.Intn(len(insult_c))])
		return bot.Stop()
	})

	bot.ListenFor("^ *!quityourjob +(.+)*$", func(msg core.Message, matches []string) core.Response {
		msg.Send(matches[1]+", quit your job!")
		return bot.Stop()
	})

	bot.ListenFor("^ *!induct *$", func(msg core.Message, matches []string) core.Response {
		elapsed := time.Now().Sub(lastInduction)
		if elapsed.Seconds() < 60 {
			msg.Reply("Rule "+strconv.Itoa(len(induction))+", dumbass.")
		} else {
			lastInduction = time.Now()
			for i := 0; i < len(induction); i++ {
				msg.Reply(induction[i])
			}
		}
		return bot.Stop()
	})

	bot.ListenFor("^ *!induct +(.+)", func(msg core.Message, matches []string) core.Response {
		elapsed := time.Now().Sub(lastInduction)
		if elapsed.Seconds() < 60 {
			msg.Reply("Rule "+strconv.Itoa(len(induction))+", dumbass.")
		} else {
			lastInduction = time.Now()
			for i := 0; i < len(induction); i++ {
				msg.Send(matches[1]+": "+induction[i])
			}
		}
		return bot.Stop()
	})

	bot.ListenFor(fmt.Sprintf("^ *%s:.*$", bot.Name), func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
fmt.Printf("matches length: %d: %s\n", len(matches), strings.Join(matches, " "));

		tokens := strings.Split(matches[0], " ");
		if (len(tokens) <= 1) {
			return bot.Stop()
		}
		tokens2 := tokens[1:len(tokens)];
fmt.Printf("tokens length: %d: %s\n", len(tokens2), strings.Join(tokens2, " "));
fmt.Printf("random choice: %s\n", tokens2[rand.Intn(len(tokens2))]);

		output, err := generateRandomSeeded(tokens2[rand.Intn(len(tokens2))])
		if err != nil {
			return bot.Error(err)
		}
		msg.Send(output)
		return bot.Stop()
	})

	bot.ListenFor("^ *markov *$", func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
		output, err := generateRandom()
		if err != nil {
			return bot.Error(err)
		}
		msg.Send(output)
		return bot.KeepGoing()
	})

	// generate a chain for the specified user
	bot.ListenFor("^ *markov +(.+)", func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
		output, err := generate(matches[1])
		if err != nil {
			return bot.Error(err)
		}
		msg.Send(output)
		return bot.KeepGoing()
	})

	// listen to everything
	bot.ListenFor("(.*)", func(msg core.Message, matches []string) core.Response {
		mutex.Lock()
		defer mutex.Unlock()
		user := msg.User
		text := matches[0]
		record(user, text)
		foobar := rand.Intn(100)
		if foobar < 1 {
			output, err := generateRandom()
			if err != nil {
				return bot.Error(err)
			}
			msg.Send(output)
		}
		return bot.KeepGoing()
	})
}

// getChainMap gets the map for a particular user, or a new map with all of the data for all users
func getChainMap(user string) (map[string][]string, error) {
	if user != "" {
		userMap, ok := markov.Chains[user]
		if !ok {
			return nil, fmt.Errorf("No chain could be found for %s", user)
		}
		return userMap, nil
	}
	if len(markov.Chains) == 0 {
		return nil, fmt.Errorf("No chains could be found")
	}
	// combine all of the users' maps
	result := make(map[string][]string)
	for _, userChainMap := range markov.Chains {
		// userChainMap is a map[string][]string
		for prefix, userChain := range userChainMap {
			chain := result[prefix]
			if chain != nil {
				chain = make([]string,0)
			}
			for _, chainItem := range userChain {
				chain = append(chain, chainItem)
			}
			result[prefix] = chain
		}
	}
	return result, nil
}

func generateRandom() (string, error) {
	return generate("")
}

func generateRandomSeeded(seed string) (string, error) {
	chainMap, err := getChainMap("")
	if err != nil {
		return "", err
	}
	p := newPrefix(markov.PrefixLength)
	var words []string
	words = append(words, seed)
	p.shift(seed)
	for i := 0; i < maxWords; i++ {
		choices := chainMap[p.String()]
		if len(choices) == 0 {
			break
		}
		next := choices[rand.Intn(len(choices))]
		words = append(words, next)
		p.shift(next)
	}
	return strings.Join(words, " "), nil
}

func generate(user string) (string, error) {
	chainMap, err := getChainMap(user)
	if err != nil {
		return "", err
	}
	p := newPrefix(markov.PrefixLength)
	var words []string
	for i := 0; i < maxWords; i++ {
		choices := chainMap[p.String()]
		if len(choices) == 0 {
			break
		}
		next := choices[rand.Intn(len(choices))]
		words = append(words, next)
		p.shift(next)
	}
	return strings.Join(words, " "), nil
}

// record breaks up the text into tokens and then creates chains for that user
func record(user, text string) error {
	p := newPrefix(markov.PrefixLength)
	tokens := strings.Split(text, " ")
	userMap, ok := markov.Chains[user]
	if !ok {
		markov.Chains[user] = make(map[string][]string)
		userMap = markov.Chains[user]
	}
	for _, token := range tokens {
		if strings.HasPrefix("http", token) {
			continue
		}
		str := p.String()
		if !contains(userMap[str], token) {
			userMap[str] = append(userMap[str], token)
			p.shift(token)
			// only allow maxChainLength items in a particular chain for a prefix
			if len(userMap[str]) > maxChainLength {
				userMap[str] = userMap[str][len(userMap[str])-maxChainLength:]
			}
		}
	}
	return pluginState.Save(markov, false)
}

func contains(tokens []string, token string) bool {
	for _, word := range tokens {
		if word == token {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"

	"github.com/InnovaCo/serve/manifest"
	"github.com/InnovaCo/serve/utils"
	"strings"
)

func init() {
	manifest.PluginRegestry.Add("build.debian", BuildDebian{})
}

type BuildDebian struct{}

func (p BuildDebian) Run(data manifest.Manifest) error {
	env := make(map[string]string)

	// required fields
	env["MANIFEST_PACKAGE"] = data.GetString("package")
	env["MANIFEST_INFO_NAME"] = data.GetString("name")
	env["MANIFEST_INFO_VERSION"] = data.GetString("version")
	env["MANIFEST_BUILD_DEBIAN_SECTION"] = data.GetString("category")
	env["MANIFEST_INFO_CATEGORY"] = data.GetString("category")
	env["MANIFEST_BUILD_DEBIAN_MAINTAINER_NAME"] = data.GetString("maintainer-name")
	env["MANIFEST_BUILD_DEBIAN_MAINTAINER_EMAIL"] = data.GetString("maintainer-email")
	env["MANIFEST_BUILD_DEBIAN_INSTALL_ROOT"] = data.GetString("install-root")

	daemon := data.GetString("daemon")
	daemonArgs := data.GetString("daemon-args")

	if daemon != "" && data.GetBool("consul-supervisor") {
		daemonArgs = fmt.Sprintf(
			"consul supervisor --service '%s/%s' --port $PORT1 start %s %s",
			data.GetString("category"),
			data.GetString("package"),
			daemon,
			daemonArgs,
		)

		daemon = "/usr/local/bin/serve-tools"
	}

	// optional fields
	env["MANIFEST_BUILD_DEBIAN_DAEMON"] = daemon
	env["MANIFEST_BUILD_DEBIAN_DAEMON_ARGS"] = daemonArgs
	env["MANIFEST_BUILD_DEBIAN_SERVICE_OWNER"] = data.GetString("service-owner")
	env["MANIFEST_BUILD_DEBIAN_DAEMON_USER"] = data.GetString("daemon-user")
	env["MANIFEST_BUILD_DEBIAN_DAEMON_PORT"] = data.GetString("daemon-port")
	env["MANIFEST_BUILD_DEBIAN_MAKE_PIDFILE"] = data.GetString("make-pidfile")
	if d := data.GetString("depends"); d[0] == '[' {
		env["MANIFEST_BUILD_DEBIAN_DEPENDS"] = strings.Replace(string([]byte(d)[1:(len(d) - 1)]), " ", ", ", -1)
	} else {
		env["MANIFEST_BUILD_DEBIAN_DEPENDS"] = d
	}
	env["MANIFEST_BUILD_DEBIAN_DESCRIPTION"] = data.GetString("description")
	env["MANIFEST_BUILD_DEBIAN_INIT"] = data.GetString("init")
	env["MANIFEST_BUILD_DEBIAN_CRON"] = data.GetString("cron")

	env["GO_PIPELINE_LABEL"] = data.GetString("build-number")
	env["GO_STAGE_COUNTER"] = data.GetString("stage-counter")

	return utils.RunCmdWithEnv(
		fmt.Sprintf("%s/go/debian-build.sh --distribution=%s", data.GetString("ci-tools-path"), data.GetString("distribution")),
		env,
	)
}
<commit_msg>clear<commit_after>package build

import (
	"fmt"
	"strings"

	"github.com/InnovaCo/serve/manifest"
	"github.com/InnovaCo/serve/utils"
)

func init() {
	manifest.PluginRegestry.Add("build.debian", BuildDebian{})
}

type BuildDebian struct{}

func (p BuildDebian) Run(data manifest.Manifest) error {
	env := make(map[string]string)

	// required fields
	env["MANIFEST_PACKAGE"] = data.GetString("package")
	env["MANIFEST_INFO_NAME"] = data.GetString("name")
	env["MANIFEST_INFO_VERSION"] = data.GetString("version")
	env["MANIFEST_BUILD_DEBIAN_SECTION"] = data.GetString("category")
	env["MANIFEST_INFO_CATEGORY"] = data.GetString("category")
	env["MANIFEST_BUILD_DEBIAN_MAINTAINER_NAME"] = data.GetString("maintainer-name")
	env["MANIFEST_BUILD_DEBIAN_MAINTAINER_EMAIL"] = data.GetString("maintainer-email")
	env["MANIFEST_BUILD_DEBIAN_INSTALL_ROOT"] = data.GetString("install-root")

	daemon := data.GetString("daemon")
	daemonArgs := data.GetString("daemon-args")

	if daemon != "" && data.GetBool("consul-supervisor") {
		daemonArgs = fmt.Sprintf(
			"consul supervisor --service '%s/%s' --port $PORT1 start %s %s",
			data.GetString("category"),
			data.GetString("package"),
			daemon,
			daemonArgs,
		)

		daemon = "/usr/local/bin/serve-tools"
	}

	// optional fields
	env["MANIFEST_BUILD_DEBIAN_DAEMON"] = daemon
	env["MANIFEST_BUILD_DEBIAN_DAEMON_ARGS"] = daemonArgs
	env["MANIFEST_BUILD_DEBIAN_SERVICE_OWNER"] = data.GetString("service-owner")
	env["MANIFEST_BUILD_DEBIAN_DAEMON_USER"] = data.GetString("daemon-user")
	env["MANIFEST_BUILD_DEBIAN_DAEMON_PORT"] = data.GetString("daemon-port")
	env["MANIFEST_BUILD_DEBIAN_MAKE_PIDFILE"] = data.GetString("make-pidfile")
	deps := data.GetString("depends")
	if deps[0] == '[' {
		deps = strings.Replace(strings.Trim(deps, "[]"), " ", ", ", -1)
	}
	env["MANIFEST_BUILD_DEBIAN_DEPENDS"] = deps
	env["MANIFEST_BUILD_DEBIAN_DESCRIPTION"] = data.GetString("description")
	env["MANIFEST_BUILD_DEBIAN_INIT"] = data.GetString("init")
	env["MANIFEST_BUILD_DEBIAN_CRON"] = data.GetString("cron")

	env["GO_PIPELINE_LABEL"] = data.GetString("build-number")
	env["GO_STAGE_COUNTER"] = data.GetString("stage-counter")

	return utils.RunCmdWithEnv(
		fmt.Sprintf("%s/go/debian-build.sh --distribution=%s", data.GetString("ci-tools-path"), data.GetString("distribution")),
		env,
	)
}
<|endoftext|>


<commit_before>// properties: This package implements read/write operations on .properties file
//
// .properties is a file extension for files mainly used in Java related
// technologies to store the configurable parameters of an application.
// They can also be used for storing strings for Internationalization and
// localization; these are known as Property Resource Bundles.
//
// Each parameter is stored as a pair of strings, one storing the name of
// the parameter (called the key), and the other storing the value.
//
// Each line in a .properties file normally stores a single property.
// Several formats are possible for each line, including key=value,
// key = value, key:value, and key value.
//
// .properties files can use the number sign (#) or the exclamation mark (!)
// as the first non blank character in a line to denote that all text following
// it is a comment. The backwards slash is used to escape a character.
// An example of a properties file is provided below.
//
// # You are reading the ".properties" entry.
// ! The exclamation mark can also mark text as comments.
// website = http://en.wikipedia.org/
// language = English
// # The backslash below tells the application to continue reading
// # the value onto the next line.
// message = Welcome to \
//           Wikipedia!
// # Add spaces to the key
// key\ with\ spaces = This is the value that could be looked up with the \
// key "key with spaces".
// # Empty lines are skipped
//
// # Unicode
// unicode=\u041f\u0440\u0438\u0432\u0435\u0442, \u0421\u043e\u0432\u0430!
// # Comment
//
//
// In the example above, website would be a key, and its corresponding
// value would be http://en.wikipedia.org/. While the number sign and the
// exclamation mark marks text as comments, it has no effect when it is part
// of a property. Thus, the key message has the value Welcome to Wikipedia!
// and not Welcome to Wikipedia. Note also that all of the whitespace in
// front of Wikipedia! is excluded completely.
//
// The encoding of a .properties file is ISO-8859-1, also known as Latin-1. All non-Latin-1 characters must be entered by using Unicode escape characters, e. g. \uHHHH where HHHH is a hexadecimal index of the character in the Unicode character set. This allows for using .properties files as resource bundles for localization. A non-Latin-1 text file can be converted to a correct .properties file by using the native2ascii tool that is shipped with the JDK or by using a tool, such as po2prop[1], that manages the transformation from a bilingual localization format into .properties escaping.
//
// From Wikipedia, the free encyclopedia
// http://en.wikipedia.org/wiki/.properties
package properties

import "io"
import "os"
import "utf8"

// Error represents an unexpected behavior.
type Error struct {
	os.ErrorString;
}

// ErrMalformedUtf8Encoding means that it was not possible to convert \uXXXX
// string to utf8 rune.
var ErrMalformedUtf8Encoding os.Error = &Error{"malformed \\uxxxx encoding"}

// Reads key value pairs from reader and returns map[string]string
func Load(src io.Reader) (props map[string]string, err os.Error) {
	err = nil;
	lr := newLineReader(src);
	props = make(map[string]string);
	for {
		s, e := lr.readLine();
		if e == os.EOF {
			break
		}
		if e != nil {
			return nil, e
		}

		keyLen := 0;
		precedingBackslash := false;
		hasSep := false;
		valueStart := len(s);

		for keyLen < len(s) {
			c := s[keyLen];

			if (c == '=' || c == ':') && !precedingBackslash {
				valueStart = keyLen + 1;
				hasSep = true;
				break;
			} else if (c == ' ' || c == '\t' || c == '\f') && !precedingBackslash {
				valueStart = keyLen + 1;
				break;
			}
			if c == '\\' {
				precedingBackslash = !precedingBackslash
			} else {
				precedingBackslash = false
			}

			keyLen++;
		}

		for valueStart < len(s) {
			c := s[valueStart];
			if c != ' ' && c != '\t' && c != '\f' {
				if !hasSep && (c == '=' || c == ':') {
					hasSep = true
				} else {
					break
				}
			}
			valueStart++;
		}
		key, err := decodeString(s[0:keyLen]);
		if err != nil {
			return nil, err
		}
		value, err := decodeString(s[valueStart:len(s)]);
		if err != nil {
			return nil, err
		}
		props[key] = value;
	}
	return props, err;
}

// Decodes \t,\n,\r,\f and \uXXXX characters in string
func decodeString(in string) (string, os.Error) {
	out := make([]byte, len(in));
	o := 0;
	for i := 0; i < len(in); {
		if in[i] == '\\' {
			i++;
			switch in[i] {
			case 'u':
				i++;
				rune := 0;
				for j := 0; j < 4; j++ {
					switch {
					case in[i] >= '0' && in[i] <= '9':
						rune = (rune << 4) + int(in[i]) - '0';
						break;
					case in[i] >= 'a' && in[i] <= 'f':
						rune = (rune << 4) + 10 + int(in[i]) - 'a';
						break;
					case in[i] >= 'A' && in[i] <= 'F':
						rune = (rune << 4) + 10 + int(in[i]) - 'A';
						break;
					default:
						return "", ErrMalformedUtf8Encoding
					}
					i++;
				}
				bytes := make([]byte, utf8.RuneLen(rune));
				bytesWritten := utf8.EncodeRune(rune, bytes);
				for j := 0; j < bytesWritten; j++ {
					out[o] = bytes[j];
					o++;
				}
				continue;
			case 't':
				out[o] = '\t';
				o++;
				i++;
				continue;
			case 'r':
				out[o] = '\r';
				o++;
				i++;
				continue;
			case 'n':
				out[o] = '\n';
				o++;
				i++;
				continue;
			case 'f':
				out[o] = '\f';
				o++;
				i++;
				continue;
			}
			out[o] = in[i];
			o++;
			i++;
			continue;
		}
		out[o] = in[i];
		o++;
		i++;
	}

	return string(out[0:o]), nil;
}

// Read in a "logical line" from an InputStream/Reader, skip all comment
// and blank lines and filter out those leading whitespace characters
// (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
type lineReader struct {
	reader		io.Reader;
	buffer		[]byte;
	lineBuffer	[]byte;
	limit		int;
	offset		int;
	exhausted	bool;
}

func newLineReader(r io.Reader) *lineReader {
	n := new(lineReader);
	n.reader = r;
	n.buffer = make([]byte, 1024);
	n.lineBuffer = make([]byte, 1024);
	n.limit = 0;
	n.offset = 0;
	n.exhausted = false;
	return n;
}

// Returns the "logical line" from given reader
func (lr *lineReader) readLine() (line string, e os.Error) {
	if lr.exhausted {
		return "", os.EOF
	}
	nextCharIndex := 0;
	char := byte(0);

	skipLF := false;
	skipWhiteSpace := true;
	appendedLineBegin := false;
	isNewLine := true;
	isCommentLine := false;
	precedingBackslash := false;

	for {
		if lr.offset >= lr.limit {
			lr.limit, e = io.ReadFull(lr.reader, lr.buffer);
			lr.offset = 0;
			if e == os.EOF {
				lr.exhausted = true;
				if isCommentLine {
					return "", os.EOF
				}
				return string(lr.lineBuffer[0:nextCharIndex]), nil;
			}
			if e == io.ErrUnexpectedEOF {
				if isCommentLine {
					return "", os.EOF
				}
				continue;
			}
			if e != nil {
				lr.exhausted = true;
				return "", e;
			}
		}

		char = lr.buffer[lr.offset];
		lr.offset++;

		if skipLF {
			skipLF = false;
			if char == '\n' {
				continue
			}
		}

		if skipWhiteSpace {
			if char == ' ' || char == '\t' || char == '\f' {
				continue
			}
			if !appendedLineBegin && (char == '\r' || char == '\n') {
				continue
			}
			skipWhiteSpace = false;
			appendedLineBegin = false;
		}

		if isNewLine {
			isNewLine = false;
			if char == '#' || char == '!' {
				isCommentLine = true;
				continue;
			}
		}

		if char != '\n' && char != '\r' {
			lr.lineBuffer[nextCharIndex] = char;
			nextCharIndex++;
			if nextCharIndex == len(lr.lineBuffer) {
				newBuffer := make([]byte, len(lr.lineBuffer)*2);
				for i, x := range lr.lineBuffer {
					newBuffer[i] = x
				}
				lr.lineBuffer = newBuffer;
			}
			//flip the preceding backslash flag
			precedingBackslash = char == '\\' && !precedingBackslash;
		} else {
			// reached EOL
			if isCommentLine || nextCharIndex == 0 {
				isCommentLine = false;
				isNewLine = true;
				skipWhiteSpace = true;
				nextCharIndex = 0;
				continue;
			}
			if lr.offset >= lr.limit {
				lr.limit, e = io.ReadFull(lr.reader, lr.buffer);
				lr.offset = 0;
				if e == os.EOF || e == io.ErrUnexpectedEOF {
					lr.exhausted = true;
					return string(lr.lineBuffer[0:nextCharIndex]), nil;
				}
				if e != nil {
					lr.exhausted = true;
					return "", e;
				}
			}
			if precedingBackslash {
				nextCharIndex--;
				//skip the leading whitespace characters in following line
				skipWhiteSpace = true;
				appendedLineBegin = true;
				precedingBackslash = false;
				if char == '\r' {
					skipLF = true
				}
			} else {
				return string(lr.lineBuffer[0:nextCharIndex]), nil
			}
		}
	}

	return "", nil;
}
<commit_msg>wrap long line in package comment<commit_after>// properties: This package implements read/write operations on .properties file
//
// .properties is a file extension for files mainly used in Java related
// technologies to store the configurable parameters of an application.
// They can also be used for storing strings for Internationalization and
// localization; these are known as Property Resource Bundles.
//
// Each parameter is stored as a pair of strings, one storing the name of
// the parameter (called the key), and the other storing the value.
//
// Each line in a .properties file normally stores a single property.
// Several formats are possible for each line, including key=value,
// key = value, key:value, and key value.
//
// .properties files can use the number sign (#) or the exclamation mark (!)
// as the first non blank character in a line to denote that all text following
// it is a comment. The backwards slash is used to escape a character.
// An example of a properties file is provided below.
//
// # You are reading the ".properties" entry.
// ! The exclamation mark can also mark text as comments.
// website = http://en.wikipedia.org/
// language = English
// # The backslash below tells the application to continue reading
// # the value onto the next line.
// message = Welcome to \
//           Wikipedia!
// # Add spaces to the key
// key\ with\ spaces = This is the value that could be looked up with the \
// key "key with spaces".
// # Empty lines are skipped
//
// # Unicode
// unicode=\u041f\u0440\u0438\u0432\u0435\u0442, \u0421\u043e\u0432\u0430!
// # Comment
//
//
// In the example above, website would be a key, and its corresponding
// value would be http://en.wikipedia.org/. While the number sign and the
// exclamation mark marks text as comments, it has no effect when it is part
// of a property. Thus, the key message has the value Welcome to Wikipedia!
// and not Welcome to Wikipedia. Note also that all of the whitespace in
// front of Wikipedia! is excluded completely.
//
// The encoding of a .properties file is ISO-8859-1, also known as Latin-1.
// All non-Latin-1 characters must be entered by using Unicode escape characters,
// e. g. \uHHHH where HHHH is a hexadecimal index of the character in the Unicode
// character set. This allows for using .properties files as resource bundles for
// localization. A non-Latin-1 text file can be converted to a correct .properties
// file by using the native2ascii tool that is shipped with the JDK or by using
// a tool, such as po2prop, that manages the transformation from a bilingual
// localization format into .properties escaping.
//
// From Wikipedia, the free encyclopedia
// http://en.wikipedia.org/wiki/.properties
package properties

import "io"
import "os"
import "utf8"

// Error represents an unexpected behavior.
type Error struct {
	os.ErrorString;
}

// ErrMalformedUtf8Encoding means that it was not possible to convert \uXXXX
// string to utf8 rune.
var ErrMalformedUtf8Encoding os.Error = &Error{"malformed \\uxxxx encoding"}

// Reads key value pairs from reader and returns map[string]string
func Load(src io.Reader) (props map[string]string, err os.Error) {
	err = nil;
	lr := newLineReader(src);
	props = make(map[string]string);
	for {
		s, e := lr.readLine();
		if e == os.EOF {
			break
		}
		if e != nil {
			return nil, e
		}

		keyLen := 0;
		precedingBackslash := false;
		hasSep := false;
		valueStart := len(s);

		for keyLen < len(s) {
			c := s[keyLen];

			if (c == '=' || c == ':') && !precedingBackslash {
				valueStart = keyLen + 1;
				hasSep = true;
				break;
			} else if (c == ' ' || c == '\t' || c == '\f') && !precedingBackslash {
				valueStart = keyLen + 1;
				break;
			}
			if c == '\\' {
				precedingBackslash = !precedingBackslash
			} else {
				precedingBackslash = false
			}

			keyLen++;
		}

		for valueStart < len(s) {
			c := s[valueStart];
			if c != ' ' && c != '\t' && c != '\f' {
				if !hasSep && (c == '=' || c == ':') {
					hasSep = true
				} else {
					break
				}
			}
			valueStart++;
		}
		key, err := decodeString(s[0:keyLen]);
		if err != nil {
			return nil, err
		}
		value, err := decodeString(s[valueStart:len(s)]);
		if err != nil {
			return nil, err
		}
		props[key] = value;
	}
	return props, err;
}

// Decodes \t,\n,\r,\f and \uXXXX characters in string
func decodeString(in string) (string, os.Error) {
	out := make([]byte, len(in));
	o := 0;
	for i := 0; i < len(in); {
		if in[i] == '\\' {
			i++;
			switch in[i] {
			case 'u':
				i++;
				rune := 0;
				for j := 0; j < 4; j++ {
					switch {
					case in[i] >= '0' && in[i] <= '9':
						rune = (rune << 4) + int(in[i]) - '0';
						break;
					case in[i] >= 'a' && in[i] <= 'f':
						rune = (rune << 4) + 10 + int(in[i]) - 'a';
						break;
					case in[i] >= 'A' && in[i] <= 'F':
						rune = (rune << 4) + 10 + int(in[i]) - 'A';
						break;
					default:
						return "", ErrMalformedUtf8Encoding
					}
					i++;
				}
				bytes := make([]byte, utf8.RuneLen(rune));
				bytesWritten := utf8.EncodeRune(rune, bytes);
				for j := 0; j < bytesWritten; j++ {
					out[o] = bytes[j];
					o++;
				}
				continue;
			case 't':
				out[o] = '\t';
				o++;
				i++;
				continue;
			case 'r':
				out[o] = '\r';
				o++;
				i++;
				continue;
			case 'n':
				out[o] = '\n';
				o++;
				i++;
				continue;
			case 'f':
				out[o] = '\f';
				o++;
				i++;
				continue;
			}
			out[o] = in[i];
			o++;
			i++;
			continue;
		}
		out[o] = in[i];
		o++;
		i++;
	}

	return string(out[0:o]), nil;
}

// Read in a "logical line" from an InputStream/Reader, skip all comment
// and blank lines and filter out those leading whitespace characters
// (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
type lineReader struct {
	reader		io.Reader;
	buffer		[]byte;
	lineBuffer	[]byte;
	limit		int;
	offset		int;
	exhausted	bool;
}

func newLineReader(r io.Reader) *lineReader {
	n := new(lineReader);
	n.reader = r;
	n.buffer = make([]byte, 1024);
	n.lineBuffer = make([]byte, 1024);
	n.limit = 0;
	n.offset = 0;
	n.exhausted = false;
	return n;
}

// Returns the "logical line" from given reader
func (lr *lineReader) readLine() (line string, e os.Error) {
	if lr.exhausted {
		return "", os.EOF
	}
	nextCharIndex := 0;
	char := byte(0);

	skipLF := false;
	skipWhiteSpace := true;
	appendedLineBegin := false;
	isNewLine := true;
	isCommentLine := false;
	precedingBackslash := false;

	for {
		if lr.offset >= lr.limit {
			lr.limit, e = io.ReadFull(lr.reader, lr.buffer);
			lr.offset = 0;
			if e == os.EOF {
				lr.exhausted = true;
				if isCommentLine {
					return "", os.EOF
				}
				return string(lr.lineBuffer[0:nextCharIndex]), nil;
			}
			if e == io.ErrUnexpectedEOF {
				if isCommentLine {
					return "", os.EOF
				}
				continue;
			}
			if e != nil {
				lr.exhausted = true;
				return "", e;
			}
		}

		char = lr.buffer[lr.offset];
		lr.offset++;

		if skipLF {
			skipLF = false;
			if char == '\n' {
				continue
			}
		}

		if skipWhiteSpace {
			if char == ' ' || char == '\t' || char == '\f' {
				continue
			}
			if !appendedLineBegin && (char == '\r' || char == '\n') {
				continue
			}
			skipWhiteSpace = false;
			appendedLineBegin = false;
		}

		if isNewLine {
			isNewLine = false;
			if char == '#' || char == '!' {
				isCommentLine = true;
				continue;
			}
		}

		if char != '\n' && char != '\r' {
			lr.lineBuffer[nextCharIndex] = char;
			nextCharIndex++;
			if nextCharIndex == len(lr.lineBuffer) {
				newBuffer := make([]byte, len(lr.lineBuffer)*2);
				for i, x := range lr.lineBuffer {
					newBuffer[i] = x
				}
				lr.lineBuffer = newBuffer;
			}
			//flip the preceding backslash flag
			precedingBackslash = char == '\\' && !precedingBackslash;
		} else {
			// reached EOL
			if isCommentLine || nextCharIndex == 0 {
				isCommentLine = false;
				isNewLine = true;
				skipWhiteSpace = true;
				nextCharIndex = 0;
				continue;
			}
			if lr.offset >= lr.limit {
				lr.limit, e = io.ReadFull(lr.reader, lr.buffer);
				lr.offset = 0;
				if e == os.EOF || e == io.ErrUnexpectedEOF {
					lr.exhausted = true;
					return string(lr.lineBuffer[0:nextCharIndex]), nil;
				}
				if e != nil {
					lr.exhausted = true;
					return "", e;
				}
			}
			if precedingBackslash {
				nextCharIndex--;
				//skip the leading whitespace characters in following line
				skipWhiteSpace = true;
				appendedLineBegin = true;
				precedingBackslash = false;
				if char == '\r' {
					skipLF = true
				}
			} else {
				return string(lr.lineBuffer[0:nextCharIndex]), nil
			}
		}
	}

	return "", nil;
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"log"
	"net/http"

	"gopkg.in/alecthomas/kingpin.v2"
)

type config struct {
	githubkey    *string
	organization *string
}

var _config config

// Program to read in poms and determine
func main() {

	_config = getConfiguration()
	runServer()

	log.Println("Done")
}

func showRepos(w http.ResponseWriter, r *http.Request) {
	repos := getAllRepos(*_config.organization)
	data := map[string]interface{}{
		"Repos": repos,
	}
	showTemplatedFile(w, "html/default.html", data)
}

func runServer() {
	http.HandleFunc("/", handler)

	// TODO: Allow port to be passed in
	log.Println("Webserver ready")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path[1:]

	switch path {
	case "about":
		http.NotFound(w, r)
	case "":
		showRepos(w, r)
	default:
		http.NotFound(w, r)
	}
}

// TODO: Read these parameters from either command line or config file
// Can look at calendar pi on how to read configuration from file
func getConfiguration() config {
	config := config{}
	config.organization = kingpin.Arg("org", "Github organization to analyze for upgrades").Required().String()
	config.githubkey = kingpin.Arg("githubkey", "Api key for github.").Required().String()
	kingpin.Version("1.0.0")
	kingpin.CommandLine.VersionFlag.Short('v')
	kingpin.CommandLine.HelpFlag.Short('?')
	kingpin.Parse()
	return config
}

func showTemplatedFile(w http.ResponseWriter, filename string, data map[string]interface{}) {

	t, err := template.ParseFiles(filename)
	if err != nil {
		log.Println("Error while parsing template file", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = t.Execute(w, data)
	if err != nil {
		log.Println("Error while showing list ", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<commit_msg>api (#7)<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"log"
	"net/http"

	"gopkg.in/alecthomas/kingpin.v2"
)

type config struct {
	githubkey    *string
	organization *string
}

var _config config

// Program to read in poms and determine
func main() {

	_config = getConfiguration()
	runServer()

	log.Println("Done")
}

func showRepos(w http.ResponseWriter, r *http.Request) {
	repos := getAllRepos(*_config.organization)
	data := map[string]interface{}{
		"Repos": repos,
	}
	showTemplatedFile(w, "html/default.html", data)
}

func runServer() {
	http.HandleFunc("/", handler)

	// TODO: Allow port to be passed in
	log.Println("Webserver ready")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path[1:]

	switch path {
	case "api":
		repos := getAllRepos(*_config.organization)
		w.Header().Set("Content-Type", "application/javascript")
		json.NewEncoder(w).Encode(repos)
	case "":
		showRepos(w, r)
	default:
		http.NotFound(w, r)
	}
}

// TODO: Read these parameters from either command line or config file
// Can look at calendar pi on how to read configuration from file
func getConfiguration() config {
	config := config{}
	config.organization = kingpin.Arg("org", "Github organization to analyze for upgrades").Required().String()
	config.githubkey = kingpin.Arg("githubkey", "Api key for github.").Required().String()
	kingpin.Version("1.0.0")
	kingpin.CommandLine.VersionFlag.Short('v')
	kingpin.CommandLine.HelpFlag.Short('?')
	kingpin.Parse()
	return config
}

func showTemplatedFile(w http.ResponseWriter, filename string, data map[string]interface{}) {

	t, err := template.ParseFiles(filename)
	if err != nil {
		log.Println("Error while parsing template file", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = t.Execute(w, data)
	if err != nil {
		log.Println("Error while showing list ", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>package eventsource

import (
	"context"
	"errors"
	"fmt"
	"io"
	"reflect"
	"strings"
	"time"
)

const (
	msgUnhandledEvent = "aggregate was unable to handle event"
)

// Aggregate represents the aggregate root in the domain driven design sense.  It aggregates the events and presents the
// current view of the domain object
type Aggregate interface {

	// On will be called for each event processed; returns true if aggregate was able to handle the event, false otherwise
	On(event Event) bool
}

type Repository interface {
	Bind(events ...Event) error
	Load(ctx context.Context, aggregateID string) (Aggregate, error)
	Save(ctx context.Context, events ...Event) error
	New() Aggregate
}

type repository struct {
	prototype  reflect.Type
	store      Store
	serializer Serializer
	types      map[string]reflect.Type
	writer     io.Writer
	debug      bool
}

func New(prototype Aggregate, opts ...Option) Repository {
	t := reflect.TypeOf(prototype)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	r := &repository{
		prototype:  t,
		store:      newMemoryStore(),
		serializer: JSONSerializer(),
		types:      map[string]reflect.Type{},
	}

	for _, opt := range opts {
		opt(r)
	}

	return r
}

func (r *repository) logf(format string, args ...interface{}) {
	if !r.debug {
		return
	}

	now := time.Now().Format(time.StampMilli)
	io.WriteString(r.writer, now)
	io.WriteString(r.writer, " ")

	fmt.Fprintf(r.writer, format, args...)
	if !strings.HasSuffix(format, "\n") {
		io.WriteString(r.writer, "\n")
	}
}

func EventType(event Event) (string, reflect.Type) {
	t := reflect.TypeOf(event)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	if v, ok := event.(EventTyper); ok {
		return v.EventType(), t
	}

	return t.Name(), t
}

func (r *repository) Bind(events ...Event) error {
	for _, event := range events {
		if event == nil {
			return errors.New("attempt to bind nil event")
		}

		err := r.serializer.Bind(event)
		if err != nil {
			return err
		}

		eventType, typ := EventType(event)
		r.logf("Binding %12s => %#v", eventType, event)
		r.types[eventType] = typ
	}

	return nil
}

// New returns a new instance of the aggregate
func (r *repository) New() Aggregate {
	return reflect.New(r.prototype).Interface().(Aggregate)
}

func (r *repository) Save(ctx context.Context, events ...Event) error {
	if len(events) == 0 {
		return nil
	}

	var aggregateID string
	history := make(History, 0, len(events))
	for _, event := range events {
		record, err := r.serializer.Serialize(event)
		if err != nil {
			return err
		}

		aggregateID = event.AggregateID()

		history = append(history, record)
	}

	return r.store.Save(ctx, aggregateID, history...)
}

func (r *repository) Load(ctx context.Context, aggregateID string) (Aggregate, error) {
	history, err := r.store.Fetch(ctx, aggregateID, 0)
	if err != nil {
		return nil, err
	}

	entryCount := len(history)
	if entryCount == 0 {
		return nil, errors.New("not found")
	}

	r.logf("Loaded %v event(s) for aggregate id, %v", entryCount, aggregateID)
	aggregate := r.New()

	for _, record := range history {
		event, err := r.serializer.Deserialize(record)
		if err != nil {
			return nil, err
		}

		ok := aggregate.On(event)
		if !ok {
			eventType, _ := EventType(event)
			return nil, fmt.Errorf(msgUnhandledEvent + " - " + eventType)
		}
	}

	return aggregate.(Aggregate), nil
}

type Option func(registry *repository)

func WithStore(store Store) Option {
	return func(registry *repository) {
		registry.store = store
	}
}

func WithSerializer(serializer Serializer) Option {
	return func(registry *repository) {
		registry.serializer = serializer
	}
}

func WithDebug(w io.Writer) Option {
	return func(registry *repository) {
		registry.debug = true
		registry.writer = w
	}
}
<commit_msg>- added docs<commit_after>package eventsource

import (
	"context"
	"errors"
	"fmt"
	"io"
	"reflect"
	"strings"
	"time"
)

const (
	msgUnhandledEvent = "aggregate was unable to handle event"
)

// Aggregate represents the aggregate root in the domain driven design sense.  It aggregates the events and presents the
// current view of the domain object
type Aggregate interface {

	// On will be called for each event processed; returns true if aggregate was able to handle the event, false otherwise
	On(event Event) bool
}

type Repository interface {
	Bind(events ...Event) error
	Load(ctx context.Context, aggregateID string) (Aggregate, error)
	Save(ctx context.Context, events ...Event) error
	New() Aggregate
}

type repository struct {
	prototype  reflect.Type
	store      Store
	serializer Serializer
	types      map[string]reflect.Type
	writer     io.Writer
	debug      bool
}

func New(prototype Aggregate, opts ...Option) Repository {
	t := reflect.TypeOf(prototype)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	r := &repository{
		prototype:  t,
		store:      newMemoryStore(),
		serializer: JSONSerializer(),
		types:      map[string]reflect.Type{},
	}

	for _, opt := range opts {
		opt(r)
	}

	return r
}

func (r *repository) logf(format string, args ...interface{}) {
	if !r.debug {
		return
	}

	now := time.Now().Format(time.StampMilli)
	io.WriteString(r.writer, now)
	io.WriteString(r.writer, " ")

	fmt.Fprintf(r.writer, format, args...)
	if !strings.HasSuffix(format, "\n") {
		io.WriteString(r.writer, "\n")
	}
}

// EventType extracts name of the event type along with the reflect.Type of the event.
//
// Primarily useful for serializers that need to understand how marshal and unmarshal instances of Event to a []byte
func EventType(event Event) (string, reflect.Type) {
	t := reflect.TypeOf(event)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	if v, ok := event.(EventTyper); ok {
		return v.EventType(), t
	}

	return t.Name(), t
}

func (r *repository) Bind(events ...Event) error {
	for _, event := range events {
		if event == nil {
			return errors.New("attempt to bind nil event")
		}

		err := r.serializer.Bind(event)
		if err != nil {
			return err
		}

		eventType, typ := EventType(event)
		r.logf("Binding %12s => %#v", eventType, event)
		r.types[eventType] = typ
	}

	return nil
}

// New returns a new instance of the aggregate
func (r *repository) New() Aggregate {
	return reflect.New(r.prototype).Interface().(Aggregate)
}

func (r *repository) Save(ctx context.Context, events ...Event) error {
	if len(events) == 0 {
		return nil
	}

	var aggregateID string
	history := make(History, 0, len(events))
	for _, event := range events {
		record, err := r.serializer.Serialize(event)
		if err != nil {
			return err
		}

		aggregateID = event.AggregateID()

		history = append(history, record)
	}

	return r.store.Save(ctx, aggregateID, history...)
}

func (r *repository) Load(ctx context.Context, aggregateID string) (Aggregate, error) {
	history, err := r.store.Fetch(ctx, aggregateID, 0)
	if err != nil {
		return nil, err
	}

	entryCount := len(history)
	if entryCount == 0 {
		return nil, errors.New("not found")
	}

	r.logf("Loaded %v event(s) for aggregate id, %v", entryCount, aggregateID)
	aggregate := r.New()

	for _, record := range history {
		event, err := r.serializer.Deserialize(record)
		if err != nil {
			return nil, err
		}

		ok := aggregate.On(event)
		if !ok {
			eventType, _ := EventType(event)
			return nil, fmt.Errorf(msgUnhandledEvent + " - " + eventType)
		}
	}

	return aggregate.(Aggregate), nil
}

type Option func(registry *repository)

func WithStore(store Store) Option {
	return func(registry *repository) {
		registry.store = store
	}
}

func WithSerializer(serializer Serializer) Option {
	return func(registry *repository) {
		registry.serializer = serializer
	}
}

func WithDebug(w io.Writer) Option {
	return func(registry *repository) {
		registry.debug = true
		registry.writer = w
	}
}
<|endoftext|>