<commit_before>package data

import (
	"fmt"
	"time"

	"github.com/feedhenry/mcp-standalone/pkg/mobile"
	"github.com/pkg/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	v1 "k8s.io/client-go/pkg/api/v1"
)

const apiKeyMapName = "mcp-mobile-keys"

// MobileAppValidator defines what a validator should do
type MobileAppValidator interface {
	PreCreate(a *mobile.App) error
	PreUpdate(old *mobile.App, new *mobile.App) error
}

// MobileAppRepo interacts with the data store that backs the mobile objects
type MobileAppRepo struct {
	client    corev1.ConfigMapInterface
	validator MobileAppValidator
}

// NewMobileAppRepo instansiates a new MobileAppRepo
func NewMobileAppRepo(c corev1.ConfigMapInterface, v MobileAppValidator) *MobileAppRepo {
	rep := &MobileAppRepo{
		client:    c,
		validator: v,
	}
	if rep.validator == nil {
		rep.validator = &DefaultMobileAppValidator{}
	}
	return rep
}

// UpdateAppApiKeys adds new app api key to apiKey mapping
func (mar *MobileAppRepo) UpdateAppAPIKeys(app *mobile.App) error {
	cm, err := mar.client.Get(apiKeyMapName, meta_v1.GetOptions{})
	if err != nil {
		return errors.Wrap(err, "updating api key config map, could not read")
	}
	if cm.Data == nil {
		cm.Data = map[string]string{}
	}
	cm.Data[app.ID] = app.APIKey
	if _, err := mar.client.Update(cm); err != nil {
		return errors.Wrap(err, "updating api key map, could not save map")
	}
	return nil
}

// DeleteAppAPIKey remove api key from apiKey mapping
func (mar *MobileAppRepo) RemoveAppAPIKeyByID(appID string) error {
	cm, err := mar.client.Get(apiKeyMapName, meta_v1.GetOptions{})
	if err != nil {
		return errors.Wrap(err, "deleting api key map, could not read")
	}
	if cm.Data == nil {
		cm.Data = map[string]string{}
	}
	delete(cm.Data, appID)
	if _, err := mar.client.Update(cm); err != nil {
		return errors.Wrap(err, "deleting api key map, could not save map")
	}
	return nil
}

// CreateAppAPIMap Ensure that the API Key map is created
func (mar *MobileAppRepo) CreateAppAPIKeyMap() error {
	_, err := mar.client.Get(apiKeyMapName, meta_v1.GetOptions{})
	if err != nil {
		// apiKey map may not exist, create it
		_, err := mar.client.Create(&v1.ConfigMap{
			ObjectMeta: meta_v1.ObjectMeta{
				Name: apiKeyMapName,
			},
			Data: map[string]string{},
		})
		return err
	}
	return nil
}

// ReadByName attempts to read a mobile app by its unique name
func (mar *MobileAppRepo) ReadByName(name string) (*mobile.App, error) {
	_, cm, err := mar.readMobileAppAndConfigMap(name)
	if err != nil {
		return nil, errors.Wrap(err, "failed to retrieve mobile app ")
	}
	return convertConfigMapToMobileApp(cm), nil
}

// Create creates a mobile app object. Fails on duplicates
func (mar *MobileAppRepo) Create(app *mobile.App) error {
	if err := mar.validator.PreCreate(app); err != nil {
		return errors.Wrap(err, "validation failed during create")
	}
	app.ID = app.Name + "-" + fmt.Sprintf("%v", time.Now().Unix())
	app.MetaData["created"] = time.Now().Format("2006-01-02 15:04:05")
	cm := convertMobileAppToConfigMap(app)
	if _, err := mar.client.Create(cm); err != nil {
		return errors.Wrap(err, "failed to create underlying configmap for mobile app")
	}
	return nil
}

//DeleteByName will delte the underlying configmap
func (mar *MobileAppRepo) DeleteByName(name string) error {
	return mar.client.Delete(name, &meta_v1.DeleteOptions{})
}

//List will list the configmaps and convert them to mobileapps
func (mar *MobileAppRepo) List() ([]*mobile.App, error) {
	list, err := mar.client.List(meta_v1.ListOptions{LabelSelector: "group=mobileapp"})
	if err != nil {
		return nil, errors.Wrap(err, "failed to list mobileapp configmaps")
	}

	var apps = []*mobile.App{}
	for _, a := range list.Items {
		apps = append(apps, convertConfigMapToMobileApp(&a))
	}
	return apps, nil
}

// Update will update the underlying configmap with the new details
func (mar *MobileAppRepo) Update(app *mobile.App) (*mobile.App, error) {
	old, cm, err := mar.readMobileAppAndConfigMap(app.Name)
	if err != nil {
		return nil, err
	}
	if err := mar.validator.PreUpdate(old, app); err != nil {
		return nil, errors.Wrap(err, "validation failed before update")
	}
	cm.Data["name"] = app.Name
	cm.Data["clientType"] = app.ClientType
	var cmap *v1.ConfigMap
	cmap, err = mar.client.Update(cm)
	if err != nil {
		return nil, errors.Wrap(err, "failed to update mobile app configmap")
	}
	return convertConfigMapToMobileApp(cmap), nil
}

func convertConfigMapToMobileApp(m *v1.ConfigMap) *mobile.App {
	return &mobile.App{
		ID:          m.Name,
		Name:        m.Data["name"],
		ClientType:  m.Data["clientType"],
		APIKey:      m.Data["apiKey"],
		Labels:      m.Labels,
		Description: m.Data["description"],
		MetaData: map[string]string{
			"icon":    m.Annotations["icon"],
			"created": m.Annotations["created"],
		},
	}
}

func convertMobileAppToConfigMap(app *mobile.App) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: meta_v1.ObjectMeta{
			Name: app.ID,
			Labels: map[string]string{
				"group": "mobileapp",
				"name":  app.Name,
			},
			Annotations: map[string]string{
				"icon":    app.MetaData["icon"],
				"created": app.MetaData["created"],
			},
		},
		Data: map[string]string{
			"name":        app.Name,
			"clientType":  app.ClientType,
			"apiKey":      app.APIKey,
			"description": app.Description,
		},
	}
}

func (mar *MobileAppRepo) readMobileAppAndConfigMap(name string) (*mobile.App, *v1.ConfigMap, error) {
	cm, err := mar.readUnderlyingConfigMap(&mobile.App{Name: name})
	if err != nil {
		return nil, nil, err
	}
	app := convertConfigMapToMobileApp(cm)
	return app, cm, err
}

func (mar *MobileAppRepo) readUnderlyingConfigMap(a *mobile.App) (*v1.ConfigMap, error) {
	cm, err := mar.client.Get(a.Name, meta_v1.GetOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "failed to read underlying configmap for app "+a.Name)
	}
	return cm, nil
}

//NewMobileAppRepoBuilder creates a new instance of a MobileAppRepoBuilder
func NewMobileAppRepoBuilder() mobile.AppRepoBuilder {
	return &MobileAppRepoBuilder{}
}

// MobileAppRepoBuilder builds a MobileAppRepo
type MobileAppRepoBuilder struct {
	client corev1.ConfigMapInterface
}

// WithClient sets the client to use
func (marb *MobileAppRepoBuilder) WithClient(c corev1.ConfigMapInterface) mobile.AppRepoBuilder {
	return &MobileAppRepoBuilder{
		client: c,
	}
}

// Build builds the final repo
func (marb *MobileAppRepoBuilder) Build() mobile.AppCruder {
	return NewMobileAppRepo(marb.client, DefaultMobileAppValidator{})
}
<commit_msg>FH-4070 Match comment with function name<commit_after>package data

import (
	"fmt"
	"time"

	"github.com/feedhenry/mcp-standalone/pkg/mobile"
	"github.com/pkg/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	v1 "k8s.io/client-go/pkg/api/v1"
)

const apiKeyMapName = "mcp-mobile-keys"

// MobileAppValidator defines what a validator should do
type MobileAppValidator interface {
	PreCreate(a *mobile.App) error
	PreUpdate(old *mobile.App, new *mobile.App) error
}

// MobileAppRepo interacts with the data store that backs the mobile objects
type MobileAppRepo struct {
	client    corev1.ConfigMapInterface
	validator MobileAppValidator
}

// NewMobileAppRepo instansiates a new MobileAppRepo
func NewMobileAppRepo(c corev1.ConfigMapInterface, v MobileAppValidator) *MobileAppRepo {
	rep := &MobileAppRepo{
		client:    c,
		validator: v,
	}
	if rep.validator == nil {
		rep.validator = &DefaultMobileAppValidator{}
	}
	return rep
}

// UpdateAppApiKeys adds new app api key to apiKey mapping
func (mar *MobileAppRepo) UpdateAppAPIKeys(app *mobile.App) error {
	cm, err := mar.client.Get(apiKeyMapName, meta_v1.GetOptions{})
	if err != nil {
		return errors.Wrap(err, "updating api key config map, could not read")
	}
	if cm.Data == nil {
		cm.Data = map[string]string{}
	}
	cm.Data[app.ID] = app.APIKey
	if _, err := mar.client.Update(cm); err != nil {
		return errors.Wrap(err, "updating api key map, could not save map")
	}
	return nil
}

// RemoveAppAPIKeyByID remove api key from apiKey mapping
func (mar *MobileAppRepo) RemoveAppAPIKeyByID(appID string) error {
	cm, err := mar.client.Get(apiKeyMapName, meta_v1.GetOptions{})
	if err != nil {
		return errors.Wrap(err, "deleting api key map, could not read")
	}
	if cm.Data == nil {
		cm.Data = map[string]string{}
	}
	delete(cm.Data, appID)
	if _, err := mar.client.Update(cm); err != nil {
		return errors.Wrap(err, "deleting api key map, could not save map")
	}
	return nil
}

// CreateAppAPIMap Ensure that the API Key map is created
func (mar *MobileAppRepo) CreateAppAPIKeyMap() error {
	_, err := mar.client.Get(apiKeyMapName, meta_v1.GetOptions{})
	if err != nil {
		// apiKey map may not exist, create it
		_, err := mar.client.Create(&v1.ConfigMap{
			ObjectMeta: meta_v1.ObjectMeta{
				Name: apiKeyMapName,
			},
			Data: map[string]string{},
		})
		return err
	}
	return nil
}

// ReadByName attempts to read a mobile app by its unique name
func (mar *MobileAppRepo) ReadByName(name string) (*mobile.App, error) {
	_, cm, err := mar.readMobileAppAndConfigMap(name)
	if err != nil {
		return nil, errors.Wrap(err, "failed to retrieve mobile app ")
	}
	return convertConfigMapToMobileApp(cm), nil
}

// Create creates a mobile app object. Fails on duplicates
func (mar *MobileAppRepo) Create(app *mobile.App) error {
	if err := mar.validator.PreCreate(app); err != nil {
		return errors.Wrap(err, "validation failed during create")
	}
	app.ID = app.Name + "-" + fmt.Sprintf("%v", time.Now().Unix())
	app.MetaData["created"] = time.Now().Format("2006-01-02 15:04:05")
	cm := convertMobileAppToConfigMap(app)
	if _, err := mar.client.Create(cm); err != nil {
		return errors.Wrap(err, "failed to create underlying configmap for mobile app")
	}
	return nil
}

//DeleteByName will delte the underlying configmap
func (mar *MobileAppRepo) DeleteByName(name string) error {
	return mar.client.Delete(name, &meta_v1.DeleteOptions{})
}

//List will list the configmaps and convert them to mobileapps
func (mar *MobileAppRepo) List() ([]*mobile.App, error) {
	list, err := mar.client.List(meta_v1.ListOptions{LabelSelector: "group=mobileapp"})
	if err != nil {
		return nil, errors.Wrap(err, "failed to list mobileapp configmaps")
	}

	var apps = []*mobile.App{}
	for _, a := range list.Items {
		apps = append(apps, convertConfigMapToMobileApp(&a))
	}
	return apps, nil
}

// Update will update the underlying configmap with the new details
func (mar *MobileAppRepo) Update(app *mobile.App) (*mobile.App, error) {
	old, cm, err := mar.readMobileAppAndConfigMap(app.Name)
	if err != nil {
		return nil, err
	}
	if err := mar.validator.PreUpdate(old, app); err != nil {
		return nil, errors.Wrap(err, "validation failed before update")
	}
	cm.Data["name"] = app.Name
	cm.Data["clientType"] = app.ClientType
	var cmap *v1.ConfigMap
	cmap, err = mar.client.Update(cm)
	if err != nil {
		return nil, errors.Wrap(err, "failed to update mobile app configmap")
	}
	return convertConfigMapToMobileApp(cmap), nil
}

func convertConfigMapToMobileApp(m *v1.ConfigMap) *mobile.App {
	return &mobile.App{
		ID:          m.Name,
		Name:        m.Data["name"],
		ClientType:  m.Data["clientType"],
		APIKey:      m.Data["apiKey"],
		Labels:      m.Labels,
		Description: m.Data["description"],
		MetaData: map[string]string{
			"icon":    m.Annotations["icon"],
			"created": m.Annotations["created"],
		},
	}
}

func convertMobileAppToConfigMap(app *mobile.App) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: meta_v1.ObjectMeta{
			Name: app.ID,
			Labels: map[string]string{
				"group": "mobileapp",
				"name":  app.Name,
			},
			Annotations: map[string]string{
				"icon":    app.MetaData["icon"],
				"created": app.MetaData["created"],
			},
		},
		Data: map[string]string{
			"name":        app.Name,
			"clientType":  app.ClientType,
			"apiKey":      app.APIKey,
			"description": app.Description,
		},
	}
}

func (mar *MobileAppRepo) readMobileAppAndConfigMap(name string) (*mobile.App, *v1.ConfigMap, error) {
	cm, err := mar.readUnderlyingConfigMap(&mobile.App{Name: name})
	if err != nil {
		return nil, nil, err
	}
	app := convertConfigMapToMobileApp(cm)
	return app, cm, err
}

func (mar *MobileAppRepo) readUnderlyingConfigMap(a *mobile.App) (*v1.ConfigMap, error) {
	cm, err := mar.client.Get(a.Name, meta_v1.GetOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "failed to read underlying configmap for app "+a.Name)
	}
	return cm, nil
}

//NewMobileAppRepoBuilder creates a new instance of a MobileAppRepoBuilder
func NewMobileAppRepoBuilder() mobile.AppRepoBuilder {
	return &MobileAppRepoBuilder{}
}

// MobileAppRepoBuilder builds a MobileAppRepo
type MobileAppRepoBuilder struct {
	client corev1.ConfigMapInterface
}

// WithClient sets the client to use
func (marb *MobileAppRepoBuilder) WithClient(c corev1.ConfigMapInterface) mobile.AppRepoBuilder {
	return &MobileAppRepoBuilder{
		client: c,
	}
}

// Build builds the final repo
func (marb *MobileAppRepoBuilder) Build() mobile.AppCruder {
	return NewMobileAppRepo(marb.client, DefaultMobileAppValidator{})
}
<|endoftext|>


<commit_before>package handler

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/drone/drone/pkg/build/script"
	"github.com/drone/drone/pkg/database"
	. "github.com/drone/drone/pkg/model"
	"github.com/drone/drone/pkg/queue"
	"github.com/plouc/go-gitlab-client"
)

type GitlabHandler struct {
	queue   *queue.Queue
	apiPath string
}

func NewGitlabHandler(queue *queue.Queue) *GitlabHandler {
	return &GitlabHandler{
		queue:   queue,
		apiPath: "/api/v3",
	}
}

func (g *GitlabHandler) Add(w http.ResponseWriter, r *http.Request, u *User) error {
	settings := database.SettingsMust()
	teams, err := database.ListTeams(u.ID)
	if err != nil {
		return err
	}
	data := struct {
		User     *User
		Teams    []*Team
		Settings *Settings
	}{u, teams, settings}
	// if the user hasn't linked their GitLab account
	// render a different template
	if len(u.GitlabToken) == 0 {
		return RenderTemplate(w, "gitlab_link.html", &data)
	}
	// otherwise display the template for adding
	// a new GitLab repository.
	return RenderTemplate(w, "gitlab_add.html", &data)
}

func (g *GitlabHandler) Link(w http.ResponseWriter, r *http.Request, u *User) error {
	token := strings.TrimSpace(r.FormValue("token"))

	if len(u.GitlabToken) == 0 || token != u.GitlabToken && len(token) > 0 {
		u.GitlabToken = token
		settings := database.SettingsMust()
		gl := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, u.GitlabToken)
		_, err := gl.CurrentUser()
		if err != nil {
			return fmt.Errorf("Private Token is not valid: %q", err)
		}
		if err := database.SaveUser(u); err != nil {
			return RenderError(w, err, http.StatusBadRequest)
		}
	}

	http.Redirect(w, r, "/new/gitlab", http.StatusSeeOther)
	return nil
}

func (g *GitlabHandler) ReLink(w http.ResponseWriter, r *http.Request, u *User) error {
	data := struct {
		User *User
	}{u}
	return RenderTemplate(w, "gitlab_link.html", &data)
}

func (g *GitlabHandler) Create(w http.ResponseWriter, r *http.Request, u *User) error {
	teamName := r.FormValue("team")
	owner := r.FormValue("owner")
	name := r.FormValue("name")

	repo, err := g.newGitlabRepo(u, owner, name)
	if err != nil {
		return err
	}

	if len(teamName) > 0 {
		team, err := database.GetTeamSlug(teamName)
		if err != nil {
			return fmt.Errorf("Unable to find Team %s.", teamName)
		}

		// user must be an admin member of the team
		if ok, _ := database.IsMemberAdmin(u.ID, team.ID); !ok {
			return fmt.Errorf("Invalid permission to access Team %s.", teamName)
		}
		repo.TeamID = team.ID
	}

	// Save to the database
	if err := database.SaveRepo(repo); err != nil {
		return fmt.Errorf("Error saving repository to the database. %s", err)
	}

	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
}

func (g *GitlabHandler) newGitlabRepo(u *User, owner, name string) (*Repo, error) {
	settings := database.SettingsMust()
	gl := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, u.GitlabToken)

	project, err := gl.Project(ns(owner, name))
	if err != nil {
		return nil, err
	}

	var cloneUrl string
	if project.Public {
		cloneUrl = project.HttpRepoUrl
	} else {
		cloneUrl = project.SshRepoUrl
	}

	repo, err := NewRepo(settings.GitlabDomain, owner, name, ScmGit, cloneUrl)
	if err != nil {
		return nil, err
	}

	repo.UserID = u.ID
	repo.Private = !project.Public
	if repo.Private {
		// name the key
		keyName := fmt.Sprintf("%s@%s", repo.Owner, settings.Domain)

		// TODO: (fudanchii) check if we already opted to use UserKey

		// create the github key, or update if one already exists
		if err := gl.AddProjectDeployKey(ns(owner, name), keyName, repo.PublicKey); err != nil {
			return nil, fmt.Errorf("Unable to add Public Key to your GitLab repository.")
		}
	}

	link := fmt.Sprintf("%s://%s/hook/gitlab?id=%s", settings.Scheme, settings.Domain, repo.Slug)
	if err := gl.AddProjectHook(ns(owner, name), link, true, false, true); err != nil {
		return nil, fmt.Errorf("Unable to add Hook to your GitLab repository.")
	}

	return repo, err
}

func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
	rID := r.FormValue("id")
	repo, err := database.GetRepoSlug(rID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	user, err := database.GetUser(repo.UserID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	payload, _ := ioutil.ReadAll(r.Body)
	parsed, err := gogitlab.ParseHook(payload)
	if err != nil {
		return err
	}
	if parsed.ObjectKind == "merge_request" {
		fmt.Println(string(payload))
		if err := g.PullRequestHook(parsed, repo, user); err != nil {
			return err
		}
		return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
	}

	if len(parsed.After) == 0 {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	_, err = database.GetCommitHash(parsed.After, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		println("commit already exists")
		return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = parsed.Branch()
	commit.Hash = parsed.After
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()

	head := parsed.Head()
	commit.Message = head.Message
	commit.Timestamp = head.Timestamp
	if head.Author != nil {
		commit.SetAuthor(head.Author.Email)
	} else {
		commit.SetAuthor(parsed.UserName)
	}

	// get the github settings from the database
	settings := database.SettingsMust()

	// get the drone.yml file from GitHub
	client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)

	content, err := client.RepoRawFile(ns(repo.Owner, repo.Name), commit.Hash, ".drone.yml")
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// parse the build script
	buildscript, err := script.ParseBuild(content, repo.Params)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})

	// OK!
	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)

}

func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, user *User) error {
	obj := p.ObjectAttributes

	// Gitlab may trigger multiple hooks upon updating merge requests status
	// only build when it was just opened and the merge hasn't been checked yet.
	if !(obj.State == "opened" && obj.MergeStatus == "unchecked") {
		println("Ignore GitLab Merge Requests")
		return nil
	}

	settings := database.SettingsMust()

	client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)

	// GitLab merge-requests hook doesn't include repository data.
	// Have to fetch it manually
	src, err := client.RepoBranch(strconv.Itoa(obj.SourceProjectId), obj.SourceBranch)
	if err != nil {
		return err
	}

	_, err = database.GetCommitHash(src.Commit.Id, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		println("commit already exists")
		return err
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = src.Name
	commit.Hash = src.Commit.Id
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()

	commit.Message = src.Commit.Message
	commit.Timestamp = src.Commit.AuthoredDateRaw
	commit.SetAuthor(src.Commit.Author.Email)

	content, err := client.RepoRawFile(strconv.Itoa(obj.SourceProjectId), commit.Hash, ".drone.yml")
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return fmt.Errorf("Failed to save build: %q", err)
		}
		return fmt.Errorf("Error to fetch build script: %q", err)
	}

	// parse the build script
	buildscript, err := script.ParseBuild(content, repo.Params)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return fmt.Errorf("Failed to save build: %q", err)
		}
		return fmt.Errorf("Failed to parse build script: %q", err)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return fmt.Errorf("Failed to save commit: %q", err)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return fmt.Errorf("Failed to save build: %q", err)
	}

	g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})

	return nil
}

// ns namespaces user and repo.
// Returns user%2Frepo
func ns(user, repo string) string {
	return fmt.Sprintf("%s%%2F%s", user, repo)
}
<commit_msg>Set commit.PullRequest for PullRequestHook<commit_after>package handler

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/drone/drone/pkg/build/script"
	"github.com/drone/drone/pkg/database"
	. "github.com/drone/drone/pkg/model"
	"github.com/drone/drone/pkg/queue"
	"github.com/plouc/go-gitlab-client"
)

type GitlabHandler struct {
	queue   *queue.Queue
	apiPath string
}

func NewGitlabHandler(queue *queue.Queue) *GitlabHandler {
	return &GitlabHandler{
		queue:   queue,
		apiPath: "/api/v3",
	}
}

func (g *GitlabHandler) Add(w http.ResponseWriter, r *http.Request, u *User) error {
	settings := database.SettingsMust()
	teams, err := database.ListTeams(u.ID)
	if err != nil {
		return err
	}
	data := struct {
		User     *User
		Teams    []*Team
		Settings *Settings
	}{u, teams, settings}
	// if the user hasn't linked their GitLab account
	// render a different template
	if len(u.GitlabToken) == 0 {
		return RenderTemplate(w, "gitlab_link.html", &data)
	}
	// otherwise display the template for adding
	// a new GitLab repository.
	return RenderTemplate(w, "gitlab_add.html", &data)
}

func (g *GitlabHandler) Link(w http.ResponseWriter, r *http.Request, u *User) error {
	token := strings.TrimSpace(r.FormValue("token"))

	if len(u.GitlabToken) == 0 || token != u.GitlabToken && len(token) > 0 {
		u.GitlabToken = token
		settings := database.SettingsMust()
		gl := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, u.GitlabToken)
		_, err := gl.CurrentUser()
		if err != nil {
			return fmt.Errorf("Private Token is not valid: %q", err)
		}
		if err := database.SaveUser(u); err != nil {
			return RenderError(w, err, http.StatusBadRequest)
		}
	}

	http.Redirect(w, r, "/new/gitlab", http.StatusSeeOther)
	return nil
}

func (g *GitlabHandler) ReLink(w http.ResponseWriter, r *http.Request, u *User) error {
	data := struct {
		User *User
	}{u}
	return RenderTemplate(w, "gitlab_link.html", &data)
}

func (g *GitlabHandler) Create(w http.ResponseWriter, r *http.Request, u *User) error {
	teamName := r.FormValue("team")
	owner := r.FormValue("owner")
	name := r.FormValue("name")

	repo, err := g.newGitlabRepo(u, owner, name)
	if err != nil {
		return err
	}

	if len(teamName) > 0 {
		team, err := database.GetTeamSlug(teamName)
		if err != nil {
			return fmt.Errorf("Unable to find Team %s.", teamName)
		}

		// user must be an admin member of the team
		if ok, _ := database.IsMemberAdmin(u.ID, team.ID); !ok {
			return fmt.Errorf("Invalid permission to access Team %s.", teamName)
		}
		repo.TeamID = team.ID
	}

	// Save to the database
	if err := database.SaveRepo(repo); err != nil {
		return fmt.Errorf("Error saving repository to the database. %s", err)
	}

	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
}

func (g *GitlabHandler) newGitlabRepo(u *User, owner, name string) (*Repo, error) {
	settings := database.SettingsMust()
	gl := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, u.GitlabToken)

	project, err := gl.Project(ns(owner, name))
	if err != nil {
		return nil, err
	}

	var cloneUrl string
	if project.Public {
		cloneUrl = project.HttpRepoUrl
	} else {
		cloneUrl = project.SshRepoUrl
	}

	repo, err := NewRepo(settings.GitlabDomain, owner, name, ScmGit, cloneUrl)
	if err != nil {
		return nil, err
	}

	repo.UserID = u.ID
	repo.Private = !project.Public
	if repo.Private {
		// name the key
		keyName := fmt.Sprintf("%s@%s", repo.Owner, settings.Domain)

		// TODO: (fudanchii) check if we already opted to use UserKey

		// create the github key, or update if one already exists
		if err := gl.AddProjectDeployKey(ns(owner, name), keyName, repo.PublicKey); err != nil {
			return nil, fmt.Errorf("Unable to add Public Key to your GitLab repository.")
		}
	}

	link := fmt.Sprintf("%s://%s/hook/gitlab?id=%s", settings.Scheme, settings.Domain, repo.Slug)
	if err := gl.AddProjectHook(ns(owner, name), link, true, false, true); err != nil {
		return nil, fmt.Errorf("Unable to add Hook to your GitLab repository.")
	}

	return repo, err
}

func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
	rID := r.FormValue("id")
	repo, err := database.GetRepoSlug(rID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	user, err := database.GetUser(repo.UserID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	payload, _ := ioutil.ReadAll(r.Body)
	parsed, err := gogitlab.ParseHook(payload)
	if err != nil {
		return err
	}
	if parsed.ObjectKind == "merge_request" {
		fmt.Println(string(payload))
		if err := g.PullRequestHook(parsed, repo, user); err != nil {
			return err
		}
		return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
	}

	if len(parsed.After) == 0 {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	_, err = database.GetCommitHash(parsed.After, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		fmt.Println("commit already exists")
		return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = parsed.Branch()
	commit.Hash = parsed.After
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()

	head := parsed.Head()
	commit.Message = head.Message
	commit.Timestamp = head.Timestamp
	if head.Author != nil {
		commit.SetAuthor(head.Author.Email)
	} else {
		commit.SetAuthor(parsed.UserName)
	}

	// get the github settings from the database
	settings := database.SettingsMust()

	// get the drone.yml file from GitHub
	client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)

	content, err := client.RepoRawFile(ns(repo.Owner, repo.Name), commit.Hash, ".drone.yml")
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// parse the build script
	buildscript, err := script.ParseBuild(content, repo.Params)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})

	// OK!
	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)

}

func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, user *User) error {
	obj := p.ObjectAttributes

	// Gitlab may trigger multiple hooks upon updating merge requests status
	// only build when it was just opened and the merge hasn't been checked yet.
	if !(obj.State == "opened" && obj.MergeStatus == "unchecked") {
		fmt.Println("Ignore GitLab Merge Requests")
		return nil
	}

	settings := database.SettingsMust()

	client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)

	// GitLab merge-requests hook doesn't include repository data.
	// Have to fetch it manually
	src, err := client.RepoBranch(strconv.Itoa(obj.SourceProjectId), obj.SourceBranch)
	if err != nil {
		return err
	}

	_, err = database.GetCommitHash(src.Commit.Id, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		fmt.Println("commit already exists")
		return err
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = src.Name
	commit.Hash = src.Commit.Id
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()
	commit.PullRequest = strconv.Itoa(obj.IId)

	commit.Message = src.Commit.Message
	commit.Timestamp = src.Commit.AuthoredDateRaw
	commit.SetAuthor(src.Commit.Author.Email)

	content, err := client.RepoRawFile(strconv.Itoa(obj.SourceProjectId), commit.Hash, ".drone.yml")
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return fmt.Errorf("Failed to save build: %q", err)
		}
		return fmt.Errorf("Error to fetch build script: %q", err)
	}

	// parse the build script
	buildscript, err := script.ParseBuild(content, repo.Params)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return fmt.Errorf("Failed to save build: %q", err)
		}
		return fmt.Errorf("Failed to parse build script: %q", err)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return fmt.Errorf("Failed to save commit: %q", err)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return fmt.Errorf("Failed to save build: %q", err)
	}

	g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})

	return nil
}

// ns namespaces user and repo.
// Returns user%2Frepo
func ns(user, repo string) string {
	return fmt.Sprintf("%s%%2F%s", user, repo)
}
<|endoftext|>


<commit_before>// Copyright 2018 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvimutil

import (
	"bytes"
	"context"
	"errors"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/haya14busa/errorformat"
	"github.com/neovim/go-client/nvim"
	"go.opencensus.io/trace"
	"go.uber.org/zap"

	"github.com/zchee/nvim-go/pkg/buildctxt"
	"github.com/zchee/nvim-go/pkg/config"
	"github.com/zchee/nvim-go/pkg/fs"
	"github.com/zchee/nvim-go/pkg/logger"
)

// regexp pattern: https://regex101.com/r/bUVZpH/2
var errRe = regexp.MustCompile(`(?m)^(?:#\s([[:graph:]]+))?(?:[\s\t]+)?([^\s:]+):(\d+)(?::(\d+))?(?::)?\s(.*)`)

// ParseError parses a typical Go tools error messages.
func ParseError(ctx context.Context, errmsg []byte, cwd string, bctxt *buildctxt.Build, ignoreDirs []string) ([]*nvim.QuickfixError, error) {
	defer Profile(ctx, time.Now(), "ParseError")
	span := trace.FromContext(ctx)
	span.SetName("ParseError")
	defer span.End()

	if config.IsDebug() {
		log := logger.FromContext(ctx)
		efm, err := errorformat.NewErrorformat([]string{`%f:%l:%c: %m`, `%-G%.%#`})
		if err != nil {
			return nil, err
		}
		s := efm.NewScanner(bytes.NewReader(errmsg))
		for s.Scan() {
			log.Debug("errorformat: `%%f:%l:%%c: %m`, `%%-G%.%%#`", zap.Any("s.Entry", s.Entry()))
		}
	}

	var (
		// packagePath for the save the error files parent directory.
		// It will be re-assigned if "# " is in the error message.
		packagePath string
		errlist     []*nvim.QuickfixError
	)

	// m[1]: package path with "# " prefix
	// m[2]: error files relative path
	// m[3]: line number of error point
	// m[4]: column number of error point
	// m[5]: error description text
	for _, m := range errRe.FindAllSubmatch(errmsg, -1) {
		if m[1] != nil {
			// Save the package path for the second subsequent errors
			packagePath = string(m[1])
		}
		filename := string(m[2])

		// Avoid the local package error. like "package foo" and edit "cmd/foo/main.go"
		if !filepath.IsAbs(filename) && packagePath != "" {
			// Joins the packagePath and error file
			filename = filepath.Join(packagePath, filepath.Base(filename))
		}

		// Cleanup filename to relative path of current working directory
		switch bctxt.Tool {
		case "go":
			var sep string
			switch {
			// filename has not directory path
			case filepath.Dir(filename) == ".":
				filename = filepath.Join(cwd, filename)
			// not contains '#' package title in errror
			case strings.HasPrefix(filename, cwd):
				sep = cwd
				filename = strings.TrimPrefix(filename, sep+string(filepath.Separator))
			// filename is like "github.com/foo/bar.go"
			case strings.HasPrefix(filename, fs.TrimGoPath(cwd)):
				sep = fs.TrimGoPath(cwd) + string(filepath.Separator)
				filename = strings.TrimPrefix(filename, sep)
			default:
				filename = fs.JoinGoPath(filename)
			}
		case "gb":
			// gb compiler error messages is relative filename path of project root dir
			if !filepath.IsAbs(filename) {
				filename = filepath.Join(bctxt.ProjectRoot, "src", filename)
			}
		default:
			return nil, errors.New("unknown compiler tool")
		}

		// Finally, try to convert the relative path from cwd
		filename = fs.Rel(cwd, filename)
		if ignoreDirs != nil {
			if contains(filename, ignoreDirs) {
				continue
			}
		}

		// line is necessary for error messages
		line, err := strconv.Atoi(string(m[3]))
		if err != nil {
			return nil, err
		}

		// Ignore err because fail strconv.Atoi will assign 0 to col
		col, _ := strconv.Atoi(string(m[4]))

		errlist = append(errlist, &nvim.QuickfixError{
			FileName: filename,
			LNum:     line,
			Col:      col,
			Text:     string(bytes.TrimSpace(m[5])),
		})
	}

	return errlist, nil
}

func contains(s string, substr []string) bool {
	for _, str := range substr {
		if strings.Contains(s, str) {
			return true
		}
	}
	return false
}
<commit_msg>pkg/nvimutil: fix errorformat import path<commit_after>// Copyright 2018 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvimutil

import (
	"bytes"
	"context"
	"errors"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/reviewdog/errorformat"
	"github.com/neovim/go-client/nvim"
	"go.opencensus.io/trace"
	"go.uber.org/zap"

	"github.com/zchee/nvim-go/pkg/buildctxt"
	"github.com/zchee/nvim-go/pkg/config"
	"github.com/zchee/nvim-go/pkg/fs"
	"github.com/zchee/nvim-go/pkg/logger"
)

// regexp pattern: https://regex101.com/r/bUVZpH/2
var errRe = regexp.MustCompile(`(?m)^(?:#\s([[:graph:]]+))?(?:[\s\t]+)?([^\s:]+):(\d+)(?::(\d+))?(?::)?\s(.*)`)

// ParseError parses a typical Go tools error messages.
func ParseError(ctx context.Context, errmsg []byte, cwd string, bctxt *buildctxt.Build, ignoreDirs []string) ([]*nvim.QuickfixError, error) {
	defer Profile(ctx, time.Now(), "ParseError")
	span := trace.FromContext(ctx)
	span.SetName("ParseError")
	defer span.End()

	if config.IsDebug() {
		log := logger.FromContext(ctx)
		efm, err := errorformat.NewErrorformat([]string{`%f:%l:%c: %m`, `%-G%.%#`})
		if err != nil {
			return nil, err
		}
		s := efm.NewScanner(bytes.NewReader(errmsg))
		for s.Scan() {
			log.Debug("errorformat: `%%f:%l:%%c: %m`, `%%-G%.%%#`", zap.Any("s.Entry", s.Entry()))
		}
	}

	var (
		// packagePath for the save the error files parent directory.
		// It will be re-assigned if "# " is in the error message.
		packagePath string
		errlist     []*nvim.QuickfixError
	)

	// m[1]: package path with "# " prefix
	// m[2]: error files relative path
	// m[3]: line number of error point
	// m[4]: column number of error point
	// m[5]: error description text
	for _, m := range errRe.FindAllSubmatch(errmsg, -1) {
		if m[1] != nil {
			// Save the package path for the second subsequent errors
			packagePath = string(m[1])
		}
		filename := string(m[2])

		// Avoid the local package error. like "package foo" and edit "cmd/foo/main.go"
		if !filepath.IsAbs(filename) && packagePath != "" {
			// Joins the packagePath and error file
			filename = filepath.Join(packagePath, filepath.Base(filename))
		}

		// Cleanup filename to relative path of current working directory
		switch bctxt.Tool {
		case "go":
			var sep string
			switch {
			// filename has not directory path
			case filepath.Dir(filename) == ".":
				filename = filepath.Join(cwd, filename)
			// not contains '#' package title in errror
			case strings.HasPrefix(filename, cwd):
				sep = cwd
				filename = strings.TrimPrefix(filename, sep+string(filepath.Separator))
			// filename is like "github.com/foo/bar.go"
			case strings.HasPrefix(filename, fs.TrimGoPath(cwd)):
				sep = fs.TrimGoPath(cwd) + string(filepath.Separator)
				filename = strings.TrimPrefix(filename, sep)
			default:
				filename = fs.JoinGoPath(filename)
			}
		case "gb":
			// gb compiler error messages is relative filename path of project root dir
			if !filepath.IsAbs(filename) {
				filename = filepath.Join(bctxt.ProjectRoot, "src", filename)
			}
		default:
			return nil, errors.New("unknown compiler tool")
		}

		// Finally, try to convert the relative path from cwd
		filename = fs.Rel(cwd, filename)
		if ignoreDirs != nil {
			if contains(filename, ignoreDirs) {
				continue
			}
		}

		// line is necessary for error messages
		line, err := strconv.Atoi(string(m[3]))
		if err != nil {
			return nil, err
		}

		// Ignore err because fail strconv.Atoi will assign 0 to col
		col, _ := strconv.Atoi(string(m[4]))

		errlist = append(errlist, &nvim.QuickfixError{
			FileName: filename,
			LNum:     line,
			Col:      col,
			Text:     string(bytes.TrimSpace(m[5])),
		})
	}

	return errlist, nil
}

func contains(s string, substr []string) bool {
	for _, str := range substr {
		if strings.Contains(s, str) {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package visitors

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/linuxboot/fiano/pkg/uefi"
)

// Table prints the GUIDS, types and sizes as a compact table.
type Table struct {
	W         *tabwriter.Writer
	Layout    bool
	TopLevel  bool
	indent    int
	offset    uint64
	curOffset uint64
	depth     int
}

// Run wraps Visit and performs some setup and teardown tasks.
func (v *Table) Run(f uefi.Firmware) error {
	return f.Apply(v)
}

// Visit applies the Table visitor to any Firmware type.
func (v *Table) Visit(f uefi.Firmware) error {
	var offset uint64
	switch f := f.(type) {
	case *uefi.FlashImage:
		v.depth = v.indent + 1
		return v.printRow(f, "Image", "", "", 0, 0)
	case *uefi.FirmwareVolume:
		return v.printRow(f, "FV", f.FileSystemGUID.String(), "", v.offset+f.FVOffset, v.offset+f.FVOffset+f.DataOffset)
	case *uefi.File:
		// TODO: make name part of the file node
		return v.printRow(f, "File", f.Header.GUID.String(), f.Header.Type, v.curOffset, v.curOffset+f.DataOffset)
	case *uefi.Section:
		// Reset offset to O for (compressed) section content
		return v.printRow(f, "Sec", f.String(), f.Type, v.curOffset, 0)
	case *uefi.FlashDescriptor:
		v.depth = v.indent + 1
		return v.printRow(f, "IFD", "", "", 0, 0)
	case *uefi.BIOSRegion:
		v.depth = v.indent + 1
		if f.FRegion != nil {
			offset = uint64(f.FRegion.BaseOffset())
		}
		return v.printRow(f, "BIOS", "", "", offset, offset)
	case *uefi.BIOSPadding:
		return v.printRow(f, "BIOS Pad", "", "", v.offset+f.Offset, 0)
	case *uefi.NVarStore:
		return v.printRow(f, "NVAR Store", "", "", v.curOffset, v.curOffset)
	case *uefi.NVar:
		return v.printRow(f, "NVAR", f.GUID.String(), f, v.curOffset, v.curOffset+uint64(f.DataOffset))
	case *uefi.RawRegion:
		v.depth = v.indent + 1
		if f.FRegion != nil {
			offset = uint64(f.FRegion.BaseOffset())
		}
		return v.printRow(f, f.Type().String(), "", "", offset, offset)
	default:
		return v.printRow(f, fmt.Sprintf("%T", f), "", "", 0, 0)
	}
}

func indent(n int) string {
	return strings.Repeat(" ", n)
}

func (v *Table) printRow(f uefi.Firmware, node, name, typez interface{}, offset, dataOffset uint64) error {
	if v.W == nil {
		v.W = tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
		defer func() { v.W.Flush() }()
		if v.Layout {
			fmt.Fprintf(v.W, "%sNode\tGUID/Name\tOffset\tSize\n", indent(v.indent))
		} else {
			fmt.Fprintf(v.W, "%sNode\tGUID/Name\tType\tSize\n", indent(v.indent))
		}
	}
	length := uint64(len(f.Buf()))
	if typez == "" {
		if uefi.IsErased(f.Buf(), uefi.Attributes.ErasePolarity) {
			typez = "(empty)"
		}
	}
	if v.Layout {
		if name == "" {
			name = typez
		}
		fmt.Fprintf(v.W, "%s%v\t%v\t%#08x\t%#08x\n", indent(v.indent), node, name, offset, length)
	} else {
		fmt.Fprintf(v.W, "%s%v\t%v\t%v\t%#8x\n", indent(v.indent), node, name, typez, length)
	}
	v2 := *v
	v2.indent++
	v2.offset = dataOffset
	v2.curOffset = v2.offset
	if !v.TopLevel || v.indent < v.depth {

		if err := f.ApplyChildren(&v2); err != nil {
			return err
		}
	}
	v.curOffset += length
	switch f := f.(type) {
	case *uefi.FirmwareVolume:
		// Print free space at the end of the volume
		if v.Layout {
			fmt.Fprintf(v.W, "%s%v\t%v\t%#08x\t%#08x\n", indent(v2.indent), "Free", "", offset+length-f.FreeSpace, f.FreeSpace)
		} else {
			fmt.Fprintf(v.W, "%s%v\t%v\t%v\t%#8x\n", indent(v2.indent), "Free", "", "", f.FreeSpace)
		}
	case *uefi.NVarStore:
		// Print free space and GUID store
		if v.Layout {
			fmt.Fprintf(v.W, "%s%v\t%v\t%#08x\t%#08x\n", indent(v2.indent), "Free", "", offset+f.FreeSpaceOffset, f.GUIDStoreOffset-f.FreeSpaceOffset)
			fmt.Fprintf(v.W, "%s%v\t%v\t%#08x\t%#08x\n", indent(v2.indent), "GUIDStore", fmt.Sprintf("%d GUID", len(f.GUIDStore)), offset+f.GUIDStoreOffset, f.Length-f.GUIDStoreOffset)
		} else {
			fmt.Fprintf(v.W, "%s%v\t%v\t%v\t%#8x\n", indent(v2.indent), "Free", "", "", f.GUIDStoreOffset-f.FreeSpaceOffset)
			fmt.Fprintf(v.W, "%s%v\t%v\t%v\t%#8x\n", indent(v2.indent), "GUIDStore", "", fmt.Sprintf("%d GUID", len(f.GUIDStore)), f.Length-f.GUIDStoreOffset)
		}
	case *uefi.File:
		// Align
		// TODO: do we need the complex align logic from assemble?
		v.curOffset = uefi.Align8(v.curOffset)
	}
	return nil
}

func init() {
	RegisterCLI("table", "print out important information in a pretty table", 0, func(args []string) (uefi.Visitor, error) {
		return &Table{}, nil
	})
	RegisterCLI("layout-table", "print out offset and size information of top level firmware volumes in a pretty table", 0, func(args []string) (uefi.Visitor, error) {
		return &Table{Layout: true, TopLevel: true}, nil
	})
	RegisterCLI("layout-table-full", "print out offset and size information in a pretty table", 0, func(args []string) (uefi.Visitor, error) {
		return &Table{Layout: true}, nil
	})
}
<commit_msg>visitor/table: Refactor printRow for layout view<commit_after>// Copyright 2018 the LinuxBoot Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package visitors

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/linuxboot/fiano/pkg/uefi"
)

// Table prints the GUIDS, types and sizes as a compact table.
type Table struct {
	W         *tabwriter.Writer
	Layout    bool
	TopLevel  bool
	indent    int
	offset    uint64
	curOffset uint64
	depth     int
}

// Run wraps Visit and performs some setup and teardown tasks.
func (v *Table) Run(f uefi.Firmware) error {
	return f.Apply(v)
}

// Visit applies the Table visitor to any Firmware type.
func (v *Table) Visit(f uefi.Firmware) error {
	var offset uint64
	switch f := f.(type) {
	case *uefi.FlashImage:
		v.depth = v.indent + 1
		return v.printFirmware(f, "Image", "", "", 0, 0)
	case *uefi.FirmwareVolume:
		return v.printFirmware(f, "FV", f.FileSystemGUID.String(), "", v.offset+f.FVOffset, v.offset+f.FVOffset+f.DataOffset)
	case *uefi.File:
		// TODO: make name part of the file node
		return v.printFirmware(f, "File", f.Header.GUID.String(), f.Header.Type, v.curOffset, v.curOffset+f.DataOffset)
	case *uefi.Section:
		// Reset offset to O for (compressed) section content
		return v.printFirmware(f, "Sec", f.String(), f.Type, v.curOffset, 0)
	case *uefi.FlashDescriptor:
		v.depth = v.indent + 1
		return v.printFirmware(f, "IFD", "", "", 0, 0)
	case *uefi.BIOSRegion:
		v.depth = v.indent + 1
		if f.FRegion != nil {
			offset = uint64(f.FRegion.BaseOffset())
		}
		return v.printFirmware(f, "BIOS", "", "", offset, offset)
	case *uefi.BIOSPadding:
		return v.printFirmware(f, "BIOS Pad", "", "", v.offset+f.Offset, 0)
	case *uefi.NVarStore:
		return v.printFirmware(f, "NVAR Store", "", "", v.curOffset, v.curOffset)
	case *uefi.NVar:
		return v.printFirmware(f, "NVAR", f.GUID.String(), f, v.curOffset, v.curOffset+uint64(f.DataOffset))
	case *uefi.RawRegion:
		v.depth = v.indent + 1
		if f.FRegion != nil {
			offset = uint64(f.FRegion.BaseOffset())
		}
		return v.printFirmware(f, f.Type().String(), "", "", offset, offset)
	default:
		return v.printFirmware(f, fmt.Sprintf("%T", f), "", "", 0, 0)
	}
}

func indent(n int) string {
	return strings.Repeat(" ", n)
}

func (v *Table) printFirmware(f uefi.Firmware, node, name, typez interface{}, offset, dataOffset uint64) error {
	if v.W == nil {
		v.W = tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
		defer func() { v.W.Flush() }()
		if v.Layout {
			fmt.Fprintf(v.W, "%sNode\tGUID/Name\tOffset\tSize\n", indent(v.indent))
		} else {
			fmt.Fprintf(v.W, "%sNode\tGUID/Name\tType\tSize\n", indent(v.indent))
		}
	}
	length := uint64(len(f.Buf()))
	if typez == "" {
		if uefi.IsErased(f.Buf(), uefi.Attributes.ErasePolarity) {
			typez = "(empty)"
		}
	}
	v.printRow(node, name, typez, offset, length)

	v2 := *v
	v2.indent++
	v2.offset = dataOffset
	v2.curOffset = v2.offset
	if !v.TopLevel || v.indent < v.depth {

		if err := f.ApplyChildren(&v2); err != nil {
			return err
		}
	}
	v.curOffset += length
	switch f := f.(type) {
	case *uefi.FirmwareVolume:
		// Print free space at the end of the volume
		v2.printRow("Free", "", "", offset+length-f.FreeSpace, f.FreeSpace)
	case *uefi.NVarStore:
		// Print free space and GUID store
		v2.printRow("Free", "", "", offset+f.FreeSpaceOffset, f.GUIDStoreOffset-f.FreeSpaceOffset)
		v2.printRow("GUIDStore", "", fmt.Sprintf("%d GUID", len(f.GUIDStore)), offset+f.GUIDStoreOffset, f.Length-f.GUIDStoreOffset)
	case *uefi.File:
		// Align
		// TODO: do we need the complex align logic from assemble?
		v.curOffset = uefi.Align8(v.curOffset)
	}
	return nil
}

func (v *Table) printRow(node, name, typez interface{}, offset, length uint64) {
	if v.Layout {
		if name == "" {
			name = typez
		}
		fmt.Fprintf(v.W, "%s%v\t%v\t%#08x\t%#08x\n", indent(v.indent), node, name, offset, length)
	} else {
		fmt.Fprintf(v.W, "%s%v\t%v\t%v\t%#8x\n", indent(v.indent), node, name, typez, length)
	}
}

func init() {
	RegisterCLI("table", "print out important information in a pretty table", 0, func(args []string) (uefi.Visitor, error) {
		return &Table{}, nil
	})
	RegisterCLI("layout-table", "print out offset and size information of top level firmware volumes in a pretty table", 0, func(args []string) (uefi.Visitor, error) {
		return &Table{Layout: true, TopLevel: true}, nil
	})
	RegisterCLI("layout-table-full", "print out offset and size information in a pretty table", 0, func(args []string) (uefi.Visitor, error) {
		return &Table{Layout: true}, nil
	})
}
<|endoftext|>


<commit_before>package plugin

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"github.com/golang/protobuf/proto"
	"os"
)

//
// XDrive and plugin commincate via so called delim stream.  Each message starts with
// an unsigned uvarint32 for message length (uvarint32 encoding as defined in protobuf,
// notice, it is unsigned).   Then, a protobuf message.
//
// NOTE: that the message size can be 0 -- in fact, the trivial message of last
// read op, is size 0.  Must handle correctly.
//

// golang binary.ReadUvarint requires a byte reader.
var stdin *bufio.Reader = bufio.NewReader(os.Stdin)

func DelimRead(pb proto.Message) error {
	DbgLog("Delim read ... ")
	msgsz, err := binary.ReadUvarint(stdin)
	if err != nil {
		DbgLogIfErr(err, "Delim read error, msgsz is %d", msgsz)
		return err
	}

	DbgLog("Delim read msg %d bytes ... ", msgsz)
	buf := make([]byte, msgsz)
	rsz, err := stdin.Read(buf)

	if uint64(rsz) != msgsz {
		DbgLogIfErr(err, "Delim read data error, msgsz is %d", msgsz)
		// don't check err, because EOF is a real error here.
		return fmt.Errorf("delim read short read msg")
	}

	err = proto.Unmarshal(buf, pb)
	DbgLogIfErr(err, "Unmarshal error")
	return err
}

// NOTE: we do not wrap os.Stdout with a bufio -- actually, better not, because we
// want to push message over the wire instead of buffering it.
func DelimWrite(pb proto.Message) error {
	msg, err := proto.Marshal(pb)
	if err != nil {
		DbgLogIfErr(err, "Marshal error.")
		return err
	}

	msgsz := len(msg)
	DbgLog("Delim write %d bytes ... ", msgsz)

	szbuf := make([]byte, 20)
	szsz := binary.PutUvarint(szbuf, uint64(msgsz))
	DbgLog("Delim write %d bytes, szsz %d.", msgsz, szsz)
	wsz, err := os.Stdout.Write(szbuf[:szsz])
	if wsz != szsz {
		DbgLog("Delim write msg sz %d short write (%d)", szsz, wsz)
		return fmt.Errorf("delim write short write msg sz")
	}

	if msgsz > 0 {
		wsz, err = os.Stdout.Write(msg)
		if wsz != msgsz {
			DbgLog("Delim write msg %d bytes short write (%d)", msgsz, wsz)
			return fmt.Errorf("delim write short write msg")
		}
	}

	return nil
}
<commit_msg>Handle short read.<commit_after>package plugin

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"github.com/golang/protobuf/proto"
	"os"
)

//
// XDrive and plugin commincate via so called delim stream.  Each message starts with
// an unsigned uvarint32 for message length (uvarint32 encoding as defined in protobuf,
// notice, it is unsigned).   Then, a protobuf message.
//
// NOTE: that the message size can be 0 -- in fact, the trivial message of last
// read op, is size 0.  Must handle correctly.
//

// golang binary.ReadUvarint requires a byte reader.
var stdin *bufio.Reader = bufio.NewReader(os.Stdin)

func loopread(buf []byte, msgsz int) (int, error) {
	var sz int
	for sz = 0; sz < msgsz; {
		rsz, err := stdin.Read(buf[sz:])
		if err != nil {
			return sz, err
		}
		sz += rsz
	}
	return sz, nil
}

func DelimRead(pb proto.Message) error {
	DbgLog("Delim read ... ")
	msgsz, err := binary.ReadUvarint(stdin)
	if err != nil {
		DbgLogIfErr(err, "Delim read error, msgsz is %d", msgsz)
		return err
	}

	DbgLog("Delim read msg %d bytes ... ", msgsz)
	buf := make([]byte, msgsz)
	rsz, err := loopread(buf, int(msgsz))

	if uint64(rsz) != msgsz {
		DbgLog("Delim read short read.  msgsz is %d, read %d, err %v.", msgsz, rsz, err)
		// don't check err, because EOF is a real error here.
		return fmt.Errorf("delim read short read msg")
	}

	err = proto.Unmarshal(buf, pb)
	DbgLogIfErr(err, "Unmarshal error")
	return err
}

// NOTE: we do not wrap os.Stdout with a bufio -- actually, better not, because we
// want to push message over the wire instead of buffering it.
func DelimWrite(pb proto.Message) error {
	msg, err := proto.Marshal(pb)
	if err != nil {
		DbgLogIfErr(err, "Marshal error.")
		return err
	}

	msgsz := len(msg)
	DbgLog("Delim write %d bytes ... ", msgsz)

	szbuf := make([]byte, 20)
	szsz := binary.PutUvarint(szbuf, uint64(msgsz))
	DbgLog("Delim write %d bytes, szsz %d.", msgsz, szsz)
	wsz, err := os.Stdout.Write(szbuf[:szsz])
	if wsz != szsz {
		DbgLog("Delim write msg sz %d short write (%d)", szsz, wsz)
		return fmt.Errorf("delim write short write msg sz")
	}

	if msgsz > 0 {
		wsz, err = os.Stdout.Write(msg)
		if wsz != msgsz {
			DbgLog("Delim write msg %d bytes short write (%d)", msgsz, wsz)
			return fmt.Errorf("delim write short write msg")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package genhandler

import (
	"bytes"
	"strings"
	"text/template"

	"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	"github.com/pkg/errors"
)

var (
	errNoTargetService = errors.New("no target service defined in the file")
)

type param struct {
	*descriptor.File
	Imports    []descriptor.GoPackage
	SwagBuffer []byte
}

func applyTemplate(p param) (string, error) {
	w := bytes.NewBuffer(nil)
	if err := headerTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := regTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	type swaggerTmpl struct {
		FileName string
		Swagger  string
	}

	if err := footerTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := patternsTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	return w.String(), nil
}

var (
	funcMap = template.FuncMap{
		"dotToUnderscore": func(s string) string { return strings.Replace(strings.Replace(s, ".", "_", -1), "/", "_", -1) },
		"byteStr":         func(b []byte) string { return string(b) },
		"escapeBackTicks": func(s string) string { return strings.Replace(s, "`", "` + \"``\" + `", -1) },
	}

	headerTemplate = template.Must(template.New("header").Parse(`
// Code generated by protoc-gen-goclay
// source: {{.GetName}}
// DO NOT EDIT!

/*
Package {{.GoPkg.Name}} is a self-registering gRPC and JSON+Swagger service definition.

It conforms to the github.com/utrack/clay Service interface.
*/
package {{.GoPkg.Name}}
import (
	{{range $i := .Imports}}{{if $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}

	{{range $i := .Imports}}{{if not $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
)

// Update your shared lib or downgrade generator to v1 if there's an error
var _ = transport.IsVersion2

var _ chi.Router
var _ runtime.Marshaler
`))
	regTemplate = template.Must(template.New("svc-reg").Funcs(funcMap).Parse(`
{{define "base"}}
{{range $svc := .Services}}
// {{$svc.GetName}}Desc is a descriptor/registrator for the {{$svc.GetName}}Server.
type {{$svc.GetName}}Desc struct {
      svc {{$svc.GetName}}Server
}

// New{{$svc.GetName}}ServiceDesc creates new registrator for the {{$svc.GetName}}Server.
func New{{$svc.GetName}}ServiceDesc(svc {{$svc.GetName}}Server) *{{$svc.GetName}}Desc {
      return &{{$svc.GetName}}Desc{svc:svc}
}

// RegisterGRPC implements service registrator interface.
func (d *{{$svc.GetName}}Desc) RegisterGRPC(s *grpc.Server) {
      Register{{$svc.GetName}}Server(s,d.svc)
}

// SwaggerDef returns this file's Swagger definition.
func (d *{{$svc.GetName}}Desc) SwaggerDef() []byte {
      return _swaggerDef_{{dotToUnderscore $.GetName}}
}

// RegisterHTTP registers this service's HTTP handlers/bindings.
func (d *{{$svc.GetName}}Desc) RegisterHTTP(mux transport.Router) {
	{{range $m := $svc.Methods}}
	// Handlers for {{$m.GetName}}
	{{range $b := $m.Bindings}}
	mux.MethodFunc(pattern_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}},"{{$b.HTTPMethod}}", func(w http.ResponseWriter, r *http.Request) {
          defer r.Body.Close()

	  var req {{$m.RequestType.GetName}}
          err := unmarshaler_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(r,&req)
	  if err != nil {
	    httpruntime.SetError(r.Context(),r,w,errors.Wrap(err,"couldn't parse request"))
	    return
	  }

	  ret,err := d.svc.{{$m.GetName}}(r.Context(),&req)
	  if err != nil {
	    httpruntime.SetError(r.Context(),r,w,errors.Wrap(err,"returned from handler"))
	    return
	  }

          _,outbound := httpruntime.MarshalerForRequest(r)
          w.Header().Set("Content-Type", outbound.ContentType())
	  err = outbound.Marshal(w, ret)
	  if err != nil {
	    httpruntime.SetError(r.Context(),r,w,errors.Wrap(err,"couldn't write response"))
	    return
	  }
      })
      {{end}}
      {{end}}
}
{{end}}
{{end}} // base service handler ended
`))

	footerTemplate = template.Must(template.New("footer").Funcs(funcMap).Parse(`
var _swaggerDef_{{dotToUnderscore .GetName}} = []byte(` + "`" + `{{escapeBackTicks (byteStr .SwagBuffer)}}` + `
` + "`)" + `
`))

	patternsTemplate = template.Must(template.New("patterns").Parse(`
{{define "base"}}
var (
{{range $svc := .Services}}
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
	pattern_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = "{{$b.PathTmpl.Template}}"
        unmarshaler_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = func(r *http.Request,req *{{$m.RequestType.GetName}}) error {

        {{if not $b.Body}}
        {{else}}
          {{template "unmbody" .}}
        {{end}}
        {{if not $b.PathParams}}
        {{ else }}
          {{template "unmpath" .}}
        {{end}}
        }
{{end}}
{{end}}
{{end}}
)
{{end}}
{{define "unmbody"}}
          inbound,_ := httpruntime.MarshalerForRequest(r)
	  return errors.Wrap(inbound.Unmarshal(r.Body,req),"couldn't read request JSON")
{{end}}
{{define "unmpath"}}
	  rctx := chi.RouteContext(r.Context())
          if rctx == nil {
            panic("Only chi router is supported for GETs atm")
	  }
          for pos,k := range rctx.URLParams.Keys {
	    runtime.PopulateFieldFromPath(req, k, rctx.URLParams.Values[pos])
          }
          return nil
{{end}}
`))
)
<commit_msg>Now rendering empty GET requests w/o params correctly<commit_after>package genhandler

import (
	"bytes"
	"strings"
	"text/template"

	"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	"github.com/pkg/errors"
)

var (
	errNoTargetService = errors.New("no target service defined in the file")
)

type param struct {
	*descriptor.File
	Imports    []descriptor.GoPackage
	SwagBuffer []byte
}

func applyTemplate(p param) (string, error) {
	w := bytes.NewBuffer(nil)
	if err := headerTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := regTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	type swaggerTmpl struct {
		FileName string
		Swagger  string
	}

	if err := footerTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := patternsTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	return w.String(), nil
}

var (
	funcMap = template.FuncMap{
		"dotToUnderscore": func(s string) string { return strings.Replace(strings.Replace(s, ".", "_", -1), "/", "_", -1) },
		"byteStr":         func(b []byte) string { return string(b) },
		"escapeBackTicks": func(s string) string { return strings.Replace(s, "`", "` + \"``\" + `", -1) },
	}

	headerTemplate = template.Must(template.New("header").Parse(`
// Code generated by protoc-gen-goclay
// source: {{.GetName}}
// DO NOT EDIT!

/*
Package {{.GoPkg.Name}} is a self-registering gRPC and JSON+Swagger service definition.

It conforms to the github.com/utrack/clay Service interface.
*/
package {{.GoPkg.Name}}
import (
	{{range $i := .Imports}}{{if $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}

	{{range $i := .Imports}}{{if not $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
)

// Update your shared lib or downgrade generator to v1 if there's an error
var _ = transport.IsVersion2

var _ chi.Router
var _ runtime.Marshaler
`))
	regTemplate = template.Must(template.New("svc-reg").Funcs(funcMap).Parse(`
{{define "base"}}
{{range $svc := .Services}}
// {{$svc.GetName}}Desc is a descriptor/registrator for the {{$svc.GetName}}Server.
type {{$svc.GetName}}Desc struct {
      svc {{$svc.GetName}}Server
}

// New{{$svc.GetName}}ServiceDesc creates new registrator for the {{$svc.GetName}}Server.
func New{{$svc.GetName}}ServiceDesc(svc {{$svc.GetName}}Server) *{{$svc.GetName}}Desc {
      return &{{$svc.GetName}}Desc{svc:svc}
}

// RegisterGRPC implements service registrator interface.
func (d *{{$svc.GetName}}Desc) RegisterGRPC(s *grpc.Server) {
      Register{{$svc.GetName}}Server(s,d.svc)
}

// SwaggerDef returns this file's Swagger definition.
func (d *{{$svc.GetName}}Desc) SwaggerDef() []byte {
      return _swaggerDef_{{dotToUnderscore $.GetName}}
}

// RegisterHTTP registers this service's HTTP handlers/bindings.
func (d *{{$svc.GetName}}Desc) RegisterHTTP(mux transport.Router) {
	{{range $m := $svc.Methods}}
	// Handlers for {{$m.GetName}}
	{{range $b := $m.Bindings}}
	mux.MethodFunc(pattern_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}},"{{$b.HTTPMethod}}", func(w http.ResponseWriter, r *http.Request) {
          defer r.Body.Close()

	  var req {{$m.RequestType.GetName}}
          err := unmarshaler_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(r,&req)
	  if err != nil {
	    httpruntime.SetError(r.Context(),r,w,errors.Wrap(err,"couldn't parse request"))
	    return
	  }

	  ret,err := d.svc.{{$m.GetName}}(r.Context(),&req)
	  if err != nil {
	    httpruntime.SetError(r.Context(),r,w,errors.Wrap(err,"returned from handler"))
	    return
	  }

          _,outbound := httpruntime.MarshalerForRequest(r)
          w.Header().Set("Content-Type", outbound.ContentType())
	  err = outbound.Marshal(w, ret)
	  if err != nil {
	    httpruntime.SetError(r.Context(),r,w,errors.Wrap(err,"couldn't write response"))
	    return
	  }
      })
      {{end}}
      {{end}}
}
{{end}}
{{end}} // base service handler ended
`))

	footerTemplate = template.Must(template.New("footer").Funcs(funcMap).Parse(`
var _swaggerDef_{{dotToUnderscore .GetName}} = []byte(` + "`" + `{{escapeBackTicks (byteStr .SwagBuffer)}}` + `
` + "`)" + `
`))

	patternsTemplate = template.Must(template.New("patterns").Parse(`
{{define "base"}}
var (
{{range $svc := .Services}}
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
	pattern_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = "{{$b.PathTmpl.Template}}"
        unmarshaler_goclay_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = func(r *http.Request,req *{{$m.RequestType.GetName}}) error {

        {{if $b.Body}}
          {{template "unmbody" .}}
        {{end}}
        {{if $b.PathParams}}
          {{template "unmpath" .}}
        {{end}}

        {{if and (not $b.Body) (not $b.PathParams)}}
        return nil
        {{end}}
        }
{{end}}
{{end}}
{{end}}
)
{{end}}
{{define "unmbody"}}
          inbound,_ := httpruntime.MarshalerForRequest(r)
	  return errors.Wrap(inbound.Unmarshal(r.Body,req),"couldn't read request JSON")
{{end}}
{{define "unmpath"}}
	  rctx := chi.RouteContext(r.Context())
          if rctx == nil {
            panic("Only chi router is supported for GETs atm")
	  }
          for pos,k := range rctx.URLParams.Keys {
	    runtime.PopulateFieldFromPath(req, k, rctx.URLParams.Values[pos])
          }
          return nil
{{end}}
`))
)
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/TykTechnologies/tyk/config"
)

type NodeResponseOK struct {
	Status  string
	Message map[string]string
	Nonce   string
}

type DashboardServiceSender interface {
	Init() error
	Register() error
	DeRegister() error
	StartBeating() error
	StopBeating()
}

type HTTPDashboardHandler struct {
	RegistrationEndpoint   string
	DeRegistrationEndpoint string
	HeartBeatEndpoint      string
	Secret                 string

	heartBeatStopSentinel bool
}

func initialiseClient(timeout time.Duration) *http.Client {
	client := &http.Client{}
	if config.Global().HttpServerOptions.UseSSL {
		// Setup HTTPS client
		tlsConfig := &tls.Config{
			InsecureSkipVerify: config.Global().HttpServerOptions.SSLInsecureSkipVerify,
		}
		transport := &http.Transport{TLSClientConfig: tlsConfig}
		client = &http.Client{Transport: transport, Timeout: timeout}
	} else {
		client = &http.Client{Timeout: timeout}
	}
	return client
}

func reLogin() {
	if !config.Global().UseDBAppConfigs {
		return
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Registering node (again).")
	DashService.StopBeating()
	if err := DashService.DeRegister(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not deregister: ", err)
	}

	time.Sleep(5 * time.Second)

	if err := DashService.Register(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not register: ", err)
	} else {
		go DashService.StartBeating()
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Recovering configurations, reloading...")
	reloadURLStructure(nil)
}

func (h *HTTPDashboardHandler) Init() error {
	h.RegistrationEndpoint = buildConnStr("/register/node")
	h.DeRegistrationEndpoint = buildConnStr("/system/node")
	h.HeartBeatEndpoint = buildConnStr("/register/ping")
	if h.Secret = config.Global().NodeSecret; h.Secret == "" {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Fatal("Node secret is not set, required for dashboard connection")
	}
	return nil
}

func (h *HTTPDashboardHandler) Register() error {
	req := h.newRequest(h.RegistrationEndpoint)
	c := initialiseClient(5 * time.Second)
	resp, err := c.Do(req)

	if err != nil {
		log.Errorf("Request failed with error %v; retrying in 5s", err)
		time.Sleep(time.Second * 5)
		return h.Register()
	} else if resp != nil && resp.StatusCode != 200 {
		log.Errorf("Response failed with code %d; retrying in 5s", resp.StatusCode)
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the NodeID
	var found bool
	NodeID, found = val.Message["NodeID"]
	if !found {
		log.Error("Failed to register node, retrying in 5s")
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	log.WithFields(logrus.Fields{
		"prefix": "dashboard",
		"id":     NodeID,
	}).Info("Node registered")

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Debug("Registration Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) StartBeating() error {
	for !h.heartBeatStopSentinel {
		if err := h.sendHeartBeat(); err != nil {
			log.Warning(err)
		}
		time.Sleep(time.Second * 2)
	}

	log.Info("Stopped Heartbeat")
	h.heartBeatStopSentinel = false
	return nil
}

func (h *HTTPDashboardHandler) StopBeating() {
	h.heartBeatStopSentinel = true
}

func (h *HTTPDashboardHandler) newRequest(endpoint string) *http.Request {
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("authorization", h.Secret)
	req.Header.Set("x-tyk-hostname", hostDetails.Hostname)
	return req
}

func (h *HTTPDashboardHandler) sendHeartBeat() error {
	req := h.newRequest(h.HeartBeatEndpoint)
	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)
	c := initialiseClient(5 * time.Second)

	resp, err := c.Do(req)
	if err != nil || resp.StatusCode != 200 {
		return errors.New("dashboard is down? Heartbeat is failing")
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	//log.Debug("Heartbeat Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) DeRegister() error {
	req := h.newRequest(h.DeRegistrationEndpoint)

	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)

	c := initialiseClient(5 * time.Second)
	resp, err := c.Do(req)

	if err != nil {
		return fmt.Errorf("deregister request failed with error %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("deregister request failed with status %v", resp.StatusCode)
	}

	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Info("De-registered.")

	return nil
}
<commit_msg>Reuse client for heartbeat. (#1946)<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/TykTechnologies/tyk/config"
)

type NodeResponseOK struct {
	Status  string
	Message map[string]string
	Nonce   string
}

type DashboardServiceSender interface {
	Init() error
	Register() error
	DeRegister() error
	StartBeating() error
	StopBeating()
}

type HTTPDashboardHandler struct {
	RegistrationEndpoint   string
	DeRegistrationEndpoint string
	HeartBeatEndpoint      string
	Secret                 string

	heartBeatStopSentinel bool
}

func initialiseClient(timeout time.Duration) *http.Client {
	client := &http.Client{}
	if config.Global().HttpServerOptions.UseSSL {
		// Setup HTTPS client
		tlsConfig := &tls.Config{
			InsecureSkipVerify: config.Global().HttpServerOptions.SSLInsecureSkipVerify,
		}
		transport := &http.Transport{TLSClientConfig: tlsConfig}
		client = &http.Client{Transport: transport, Timeout: timeout}
	} else {
		client = &http.Client{Timeout: timeout}
	}
	return client
}

func reLogin() {
	if !config.Global().UseDBAppConfigs {
		return
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Registering node (again).")
	DashService.StopBeating()
	if err := DashService.DeRegister(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not deregister: ", err)
	}

	time.Sleep(5 * time.Second)

	if err := DashService.Register(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not register: ", err)
	} else {
		go DashService.StartBeating()
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Recovering configurations, reloading...")
	reloadURLStructure(nil)
}

func (h *HTTPDashboardHandler) Init() error {
	h.RegistrationEndpoint = buildConnStr("/register/node")
	h.DeRegistrationEndpoint = buildConnStr("/system/node")
	h.HeartBeatEndpoint = buildConnStr("/register/ping")
	if h.Secret = config.Global().NodeSecret; h.Secret == "" {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Fatal("Node secret is not set, required for dashboard connection")
	}
	return nil
}

func (h *HTTPDashboardHandler) Register() error {
	req := h.newRequest(h.RegistrationEndpoint)
	c := initialiseClient(5 * time.Second)
	resp, err := c.Do(req)

	if err != nil {
		log.Errorf("Request failed with error %v; retrying in 5s", err)
		time.Sleep(time.Second * 5)
		return h.Register()
	} else if resp != nil && resp.StatusCode != 200 {
		log.Errorf("Response failed with code %d; retrying in 5s", resp.StatusCode)
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the NodeID
	var found bool
	NodeID, found = val.Message["NodeID"]
	if !found {
		log.Error("Failed to register node, retrying in 5s")
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	log.WithFields(logrus.Fields{
		"prefix": "dashboard",
		"id":     NodeID,
	}).Info("Node registered")

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Debug("Registration Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) StartBeating() error {

	req := h.newRequest(h.HeartBeatEndpoint)

	client := initialiseClient(5 * time.Second)

	for !h.heartBeatStopSentinel {
		if err := h.sendHeartBeat(req, client); err != nil {
			log.Warning(err)
		}
		time.Sleep(time.Second * 2)
	}

	log.Info("Stopped Heartbeat")
	h.heartBeatStopSentinel = false
	return nil
}

func (h *HTTPDashboardHandler) StopBeating() {
	h.heartBeatStopSentinel = true
}

func (h *HTTPDashboardHandler) newRequest(endpoint string) *http.Request {
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("authorization", h.Secret)
	req.Header.Set("x-tyk-hostname", hostDetails.Hostname)
	return req
}

func (h *HTTPDashboardHandler) sendHeartBeat(req *http.Request, client *http.Client) error {

	resp, err := client.Do(req)
	if err != nil {
		return errors.New("dashboard is down? Heartbeat is failing")
	}

	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New("dashboard is down? Heartbeat non-200 response")
	}
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	//log.Debug("Heartbeat Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) DeRegister() error {
	req := h.newRequest(h.DeRegistrationEndpoint)

	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)

	c := initialiseClient(5 * time.Second)
	resp, err := c.Do(req)

	if err != nil {
		return fmt.Errorf("deregister request failed with error %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("deregister request failed with status %v", resp.StatusCode)
	}

	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Info("De-registered.")

	return nil
}
<|endoftext|>


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

// Package serverutils provides utilities to work with the cloudprober's external probe.
package serverutils

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/proto"
)

func readPayload(r *bufio.Reader) ([]byte, error) {
	// header format is: "\nContent-Length: %d\n\n"
	const prefix = "Content-Length: "
	var line string
	var length int
	var err error

	// Read lines until header line is found
	for {
		line, err = r.ReadString('\n')
		if err != nil {
			return nil, err
		}
		if strings.HasPrefix(line, prefix) {
			break
		}
	}

	// Parse content length from the header
	length, err = strconv.Atoi(line[len(prefix) : len(line)-1])
	if err != nil {
		return nil, err
	}
	// Consume the blank line following the header line
	if _, err = r.ReadSlice('\n'); err != nil {
		return nil, err
	}

	// Slurp in the payload
	buf := make([]byte, length)
	if _, err = io.ReadFull(r, buf); err != nil {
		return nil, err
	}
	return buf, nil
}

// ReadProbeReply reads ProbeReply from the supplied bufio.Reader and returns it to
// the caller.
func ReadProbeReply(r *bufio.Reader) (*ProbeReply, error) {
	buf, err := readPayload(r)
	if err != nil {
		return nil, err
	}
	rep := new(ProbeReply)
	return rep, proto.Unmarshal(buf, rep)
}

// ReadProbeRequest reads and parses ProbeRequest protocol buffers from the given
// bufio.Reader.
func ReadProbeRequest(r *bufio.Reader) (*ProbeRequest, error) {
	buf, err := readPayload(r)
	if err != nil {
		return nil, err
	}
	req := new(ProbeRequest)
	return req, proto.Unmarshal(buf, req)
}

func writeReplies(repliesChan chan *ProbeReply) {
	for r := range repliesChan {
		buf, err := proto.Marshal(r)
		if err != nil {
			log.Fatalf("Failed marshalling response: %v", err)
		}
		if _, err := fmt.Fprintf(os.Stdout, "\nContent-Length: %d\n\n%s", len(buf), buf); err != nil {
			log.Fatalf("Failed writing response: %v", err)
		}
	}
}

// Serve blocks indefinitely, servicing probe requests. Note that this function is
// provided mainly to help external probe server implementations. Cloudprober doesn't
// make use of it. Example usage:
//	import (
//		serverpb "github.com/google/cloudprober/probes/external/serverutils/server_proto"
//		"github.com/google/cloudprober/probes/external/serverutils/serverutils"
//	)
//	func runProbe(opts []*cppb.ProbeRequest_Option) {
//  	...
//	}
//	serverutils.Serve(func(req *ProbeRequest, reply *ProbeReply) {
// 		payload, errMsg, _ := runProbe(req.GetOptions())
//		reply.Payload = proto.String(payload)
//		if errMsg != "" {
//			reply.ErrorMessage = proto.String(errMsg)
//		}
//	})
func Serve(probeFunc func(*ProbeRequest, *ProbeReply)) {
	stdin := bufio.NewReader(os.Stdin)

	repliesChan := make(chan *ProbeReply)

	// Write replies to stdout. These are not required to be in-order.
	go writeReplies(repliesChan)

	// Read requests from stdin, and dispatch probes to service them.
	for {
		request, err := ReadProbeRequest(stdin)
		if err != nil {
			log.Fatalf("Failed reading request: %v", err)
		}
		log.Infof("Received request id: %d", request.RequestId)
		go func() {
			reply := &ProbeReply{
				RequestId: request.RequestId,
			}
			done := make(chan bool, 1)
			timeout := time.After(time.Duration(*request.TimeLimit) * time.Millisecond)
			go func() {
				probeFunc(request, reply)
				done <- true
			}()
			select {
			case <-done:
				repliesChan <- reply
			case <-timeout:
				// drop the request on the floor.
				log.Warningf("Timeout for request %v\n", *reply.RequestId)
				fmt.Fprintf(os.Stderr, "Timeout for request %v\n", *reply.RequestId)
			}
		}()
	}
}
<commit_msg>Remove unnecessary logging in the external prober.<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package serverutils provides utilities to work with the cloudprober's external probe.
package serverutils

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/golang/protobuf/proto"
)

func readPayload(r *bufio.Reader) ([]byte, error) {
	// header format is: "\nContent-Length: %d\n\n"
	const prefix = "Content-Length: "
	var line string
	var length int
	var err error

	// Read lines until header line is found
	for {
		line, err = r.ReadString('\n')
		if err != nil {
			return nil, err
		}
		if strings.HasPrefix(line, prefix) {
			break
		}
	}

	// Parse content length from the header
	length, err = strconv.Atoi(line[len(prefix) : len(line)-1])
	if err != nil {
		return nil, err
	}
	// Consume the blank line following the header line
	if _, err = r.ReadSlice('\n'); err != nil {
		return nil, err
	}

	// Slurp in the payload
	buf := make([]byte, length)
	if _, err = io.ReadFull(r, buf); err != nil {
		return nil, err
	}
	return buf, nil
}

// ReadProbeReply reads ProbeReply from the supplied bufio.Reader and returns it to
// the caller.
func ReadProbeReply(r *bufio.Reader) (*ProbeReply, error) {
	buf, err := readPayload(r)
	if err != nil {
		return nil, err
	}
	rep := new(ProbeReply)
	return rep, proto.Unmarshal(buf, rep)
}

// ReadProbeRequest reads and parses ProbeRequest protocol buffers from the given
// bufio.Reader.
func ReadProbeRequest(r *bufio.Reader) (*ProbeRequest, error) {
	buf, err := readPayload(r)
	if err != nil {
		return nil, err
	}
	req := new(ProbeRequest)
	return req, proto.Unmarshal(buf, req)
}

func writeReplies(repliesChan chan *ProbeReply) {
	for r := range repliesChan {
		buf, err := proto.Marshal(r)
		if err != nil {
			log.Fatalf("Failed marshalling response: %v", err)
		}
		if _, err := fmt.Fprintf(os.Stdout, "\nContent-Length: %d\n\n%s", len(buf), buf); err != nil {
			log.Fatalf("Failed writing response: %v", err)
		}
	}
}

// Serve blocks indefinitely, servicing probe requests. Note that this function is
// provided mainly to help external probe server implementations. Cloudprober doesn't
// make use of it. Example usage:
//	import (
//		serverpb "github.com/google/cloudprober/probes/external/serverutils/server_proto"
//		"github.com/google/cloudprober/probes/external/serverutils/serverutils"
//	)
//	func runProbe(opts []*cppb.ProbeRequest_Option) {
//  	...
//	}
//	serverutils.Serve(func(req *ProbeRequest, reply *ProbeReply) {
// 		payload, errMsg, _ := runProbe(req.GetOptions())
//		reply.Payload = proto.String(payload)
//		if errMsg != "" {
//			reply.ErrorMessage = proto.String(errMsg)
//		}
//	})
func Serve(probeFunc func(*ProbeRequest, *ProbeReply)) {
	stdin := bufio.NewReader(os.Stdin)

	repliesChan := make(chan *ProbeReply)

	// Write replies to stdout. These are not required to be in-order.
	go writeReplies(repliesChan)

	// Read requests from stdin, and dispatch probes to service them.
	for {
		request, err := ReadProbeRequest(stdin)
		if err != nil {
			log.Fatalf("Failed reading request: %v", err)
		}
		go func() {
			reply := &ProbeReply{
				RequestId: request.RequestId,
			}
			done := make(chan bool, 1)
			timeout := time.After(time.Duration(*request.TimeLimit) * time.Millisecond)
			go func() {
				probeFunc(request, reply)
				done <- true
			}()
			select {
			case <-done:
				repliesChan <- reply
			case <-timeout:
				// drop the request on the floor.
				fmt.Fprintf(os.Stderr, "Timeout for request %v\n", *reply.RequestId)
			}
		}()
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"testing"
)

func TestEditChange_Marshal_TitleChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: &EditTitle{
			From: String("TitleFrom"),
		},
		Body: nil,
		Base: nil,
	}

	want := `{
		"title": {
			"from": "TitleFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: nil,
		Body: &EditBody{
			From: String("BodyFrom"),
		},
		Base: nil,
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BaseChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	Base := EditBase{
		Ref: &EditRef{
			From: String("BaseRefFrom"),
		},
		SHA: &EditSHA{
			From: String("BaseSHAFrom"),
		},
	}

	u := &EditChange{
		Title: nil,
		Body:  nil,
		Base:  &Base,
	}

	want := `{
		"base": {
			"ref": {
				"from": "BaseRefFrom"
			},
			"sha": {
				"from": "BaseSHAFrom"
			}
		}
	}`

	testJSONMarshal(t, u, want)
}
<commit_msg>Add test cases for JSON resource marshaling (#1902)<commit_after>// Copyright 2020 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"testing"
)

func TestEditChange_Marshal_TitleChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: &EditTitle{
			From: String("TitleFrom"),
		},
		Body: nil,
		Base: nil,
	}

	want := `{
		"title": {
			"from": "TitleFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: nil,
		Body: &EditBody{
			From: String("BodyFrom"),
		},
		Base: nil,
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BaseChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	Base := EditBase{
		Ref: &EditRef{
			From: String("BaseRefFrom"),
		},
		SHA: &EditSHA{
			From: String("BaseSHAFrom"),
		},
	}

	u := &EditChange{
		Title: nil,
		Body:  nil,
		Base:  &Base,
	}

	want := `{
		"base": {
			"ref": {
				"from": "BaseRefFrom"
			},
			"sha": {
				"from": "BaseSHAFrom"
			}
		}
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectChange_Marshal_NameChange(t *testing.T) {
	testJSONMarshal(t, &ProjectChange{}, "{}")

	NameFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("NameFrom"),
	}

	u := &ProjectChange{
		Name: &NameFrom,
		Body: nil,
	}

	want := `{
		"name": {
			"from": "NameFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &ProjectChange{}, "{}")

	BodyFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("BodyFrom"),
	}

	u := &ProjectChange{
		Name: nil,
		Body: &BodyFrom,
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectCardChange_Marshal_NoteChange(t *testing.T) {
	testJSONMarshal(t, &ProjectCardChange{}, "{}")

	NoteFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("NoteFrom"),
	}

	u := &ProjectCardChange{
		Note: &NoteFrom,
	}

	want := `{
		"note": {
			"from": "NoteFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectColumnChange_Marshal_NameChange(t *testing.T) {
	testJSONMarshal(t, &ProjectColumnChange{}, "{}")

	NameFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("NameFrom"),
	}

	u := &ProjectColumnChange{
		Name: &NameFrom,
	}

	want := `{
		"name": {
			"from": "NameFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"net/http"
	"strconv"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/julienschmidt/httprouter"
)

type authenticateRequest struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

type authenticateResponse struct {
	Token string `json:"token"`
}

func (h apiHandler) postAuthenticate(resp http.ResponseWriter, req *http.Request, p httprouter.Params) {
	var authRequest authenticateRequest
	if err := readJSONFromRequest(req, &authRequest); err != nil {
		h.JSON(resp, http.StatusBadRequest, err.Error())
		return
	}

	user, err := h.model.Users.Authenticate(authRequest.UserName, authRequest.Password)
	if err != nil {
		h.JSON(resp, http.StatusUnauthorized, err.Error())
		return
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		ExpiresAt: time.Now().Add(time.Hour * 14 * 24).Unix(),
		IssuedAt:  time.Now().Unix(),
		Subject:   strconv.FormatInt(user.ID, 10),
	})
	// Always sign using the 0'th key
	tokenStr, err := token.SignedString([]byte(h.cfg.SecureKeys[0]))
	if err != nil {
		h.JSON(resp, http.StatusInternalServerError, err.Error())
	}

	h.JSON(resp, http.StatusOK, authenticateResponse{Token: tokenStr})
}

func (h apiHandler) requireAuthentication(handler httprouter.Handle) httprouter.Handle {
	return func(resp http.ResponseWriter, req *http.Request, p httprouter.Params) {
		authHeader := req.Header.Get("Authorization")
		if authHeader == "" {
			h.JSON(resp, http.StatusUnauthorized, "Authorization header missing")
			return
		}

		authHeaderParts := strings.Split(authHeader, " ")
		if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
			h.JSON(resp, http.StatusUnauthorized, "Authorization header must be in the form 'Bearer {token}'")
			return
		}

		tokenStr := authHeaderParts[1]

		// Try each key when validating the token
		var lastErr error
		for _, key := range h.cfg.SecureKeys {
			token, err := jwt.ParseWithClaims(tokenStr, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
				if token.Method != jwt.SigningMethodHS256 {
					return nil, errors.New("Incorrect signing method")
				}

				return []byte(key), nil
			})

			if err == nil && token.Valid {
				// TODO: Verify this is a valid user in the DB

				// Add the user's ID to the list of params
				claims := token.Claims.(*jwt.StandardClaims)
				p = append(p, httprouter.Param{Key: "CurrentUserID", Value: claims.Subject})

				handler(resp, req, p)
				return
			}
			lastErr = err
		}

		h.JSON(resp, http.StatusUnauthorized, lastErr.Error())
	}
}
<commit_msg>Make sure the user in the JWT token exists<commit_after>package api

import (
	"errors"
	"net/http"
	"strconv"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/julienschmidt/httprouter"
)

type authenticateRequest struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

type authenticateResponse struct {
	Token string `json:"token"`
}

func (h apiHandler) postAuthenticate(resp http.ResponseWriter, req *http.Request, p httprouter.Params) {
	var authRequest authenticateRequest
	if err := readJSONFromRequest(req, &authRequest); err != nil {
		h.JSON(resp, http.StatusBadRequest, err.Error())
		return
	}

	user, err := h.model.Users.Authenticate(authRequest.UserName, authRequest.Password)
	if err != nil {
		h.JSON(resp, http.StatusUnauthorized, err.Error())
		return
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		ExpiresAt: time.Now().Add(time.Hour * 14 * 24).Unix(),
		IssuedAt:  time.Now().Unix(),
		Subject:   strconv.FormatInt(user.ID, 10),
	})
	// Always sign using the 0'th key
	tokenStr, err := token.SignedString([]byte(h.cfg.SecureKeys[0]))
	if err != nil {
		h.JSON(resp, http.StatusInternalServerError, err.Error())
	}

	h.JSON(resp, http.StatusOK, authenticateResponse{Token: tokenStr})
}

func (h apiHandler) requireAuthentication(handler httprouter.Handle) httprouter.Handle {
	return func(resp http.ResponseWriter, req *http.Request, p httprouter.Params) {
		authHeader := req.Header.Get("Authorization")
		if authHeader == "" {
			h.JSON(resp, http.StatusUnauthorized, "Authorization header missing")
			return
		}

		authHeaderParts := strings.Split(authHeader, " ")
		if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
			h.JSON(resp, http.StatusUnauthorized, "Authorization header must be in the form 'Bearer {token}'")
			return
		}

		tokenStr := authHeaderParts[1]

		// Try each key when validating the token
		var lastErr error
		for _, key := range h.cfg.SecureKeys {
			token, err := jwt.ParseWithClaims(tokenStr, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
				if token.Method != jwt.SigningMethodHS256 {
					return nil, errors.New("Incorrect signing method")
				}

				return []byte(key), nil
			})

			if err == nil && token.Valid {
				claims := token.Claims.(*jwt.StandardClaims)
				if err = h.verifyUserExists(claims); err == nil {
					// Add the user's ID to the list of params
					p = append(p, httprouter.Param{Key: "CurrentUserID", Value: claims.Subject})

					handler(resp, req, p)
					return
				}
			}
			lastErr = err
		}

		h.JSON(resp, http.StatusUnauthorized, lastErr.Error())
	}
}

func (h apiHandler) verifyUserExists(claims *jwt.StandardClaims) error {
	userID, err := strconv.ParseInt(claims.Subject, 10, 64)
	if err != nil {
		return errors.New("invalid claims")
	}

	// Verify this is a valid user in the DB
	if _, err = h.model.Users.Read(userID); err != nil {
		return errors.New("invalid claims")
	}

	return nil
}
<|endoftext|>