<commit_before>package controllers

import (
	"database/sql"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"

	"github.com/michaelg9/ISOC/server/models"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"golang.org/x/crypto/bcrypt"
)

const (
	errNoPasswordOrEmail   = "No email and/or password specified."
	errWrongPasswordEmail  = "Wrong password/email combination."
	errNoDeviceID          = "No device ID specified."
	errNoAPIKey            = "No API key specified."
	errWrongAPIKey         = "Wrong API key."
	errNoSessionSet        = "No session set to log-out."
	errNoToken             = "No token given."
	errTokenInvalid        = "Token is invalid."
	errTokenAlreadyInvalid = "Token already invalid."
	errWrongUser           = "No such user."
	errDeviceIDNotInt      = "The device value is not an int."
	errUserIDNotInt        = "The user value is not an int."
	errWrongFeature        = "This device has no data for this feature."
	errWrongDeviceOrUser   = "The given user ID/device ID combination is not valid."

	hmacSecret = "secret"
)

// Env contains the environment information
type Env struct {
	DB           models.Datastore
	Tokens       models.TokenControl
	SessionStore *sessions.CookieStore
}

// SignUp handles /signup
func (env *Env) SignUp(w http.ResponseWriter, r *http.Request) {
	email := r.FormValue("email")
	password := r.FormValue("password")
	if email == "" || password == "" {
		http.Error(w, errNoPasswordOrEmail, http.StatusBadRequest)
		return
	}

	// Check if the user is already in the database
	user, err := env.DB.GetUser(models.User{Email: email})
	switch {
	// User does not exist
	case user == models.User{}:
		// Create new user with hashed password
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// Insert the credentials of the new user into the database
		err = env.DB.CreateUser(models.User{Email: email, PasswordHash: string(hashedPassword)})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		fmt.Fprintf(w, "Success")
	case err != nil:
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	default:
		fmt.Fprintf(w, "User already exists")
	}
}

// Upload handles /app/0.1/upload
func (env *Env) Upload(w http.ResponseWriter, r *http.Request) {
	decoder := xml.NewDecoder(r.Body)

	// Decode the given XML from the request body into the struct defined in models
	var d models.Upload
	if err := decoder.Decode(&d); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	deviceID := d.Meta.Device
	// Check if the device ID was specified in the input.
	// If no ID was specified it defaults to 0.
	if deviceID == 0 {
		http.Error(w, errNoDeviceID, http.StatusBadRequest)
		return
	}

	// If decoding was successfull input the data into the database
	for _, data := range d.TrackedData.GetContents() {
		err := env.DB.CreateData(models.AboutDevice{ID: deviceID}, data)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	fmt.Fprint(w, "Success")
}

// UpdateUser handles /update/user
// TODO: Use Token Auth
func (env *Env) UpdateUser(w http.ResponseWriter, r *http.Request) {
	// Because of the middleware we know that these values exist
	session, _ := env.SessionStore.Get(r, "log-in")
	oldEmail := session.Values["email"]

	// We need to get the user from the database to get its user ID
	user, err := env.DB.GetUser(models.User{Email: oldEmail.(string)})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Overwrite the stored values with the values given in the request.
	// If a value isn't given it defaults to the empty string, thus we don't update it.
	user.Email = r.FormValue("email")
	user.PasswordHash = r.FormValue("password")
	user.APIKey = r.FormValue("apiKey")

	if user.Email != "" {
		// Update the current session with the new email address
		session.Values["email"] = user.Email
		if err = session.Save(r, w); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	if user.PasswordHash != "" {
		// If password is non-empty create a new hash for the database.
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.PasswordHash), bcrypt.DefaultCost)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		user.PasswordHash = string(hashedPassword)
	}

	// For the API key we use 0/1 for false/true since the actual value of the key will be determined
	// by the MySQL database by calling the UUID() function. Hence if the value is not 1 we set the
	// value in the struct to be the empty string so that the apiKey won't be updated.
	if user.APIKey != "1" {
		user.APIKey = ""
	}

	if err = env.DB.UpdateUser(user); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprint(w, "Success")
}

// User handles /data/{user}. It gets all the devices and its data from
// the user and information about the user.
// TODO: Check if userID fits with token
func (env *Env) User(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	userID, err := strconv.Atoi(vars["user"])
	if err != nil {
		http.Error(w, errUserIDNotInt, http.StatusInternalServerError)
		return
	}

	user, err := env.DB.GetUser(models.User{ID: userID})
	if err == sql.ErrNoRows {
		http.Error(w, errWrongUser, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	devices, err := env.DB.GetDevicesFromUser(user)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	response := models.UserResponse{
		User:    user,
		Devices: devices,
	}

	err = writeResponse(w, r.FormValue("out"), response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Device handles /data/{user}/{device}. It gets all the info about the
// specified device of the given user. Device should be the device ID (for now).
func (env *Env) Device(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	userID, err := strconv.Atoi(vars["user"])
	if err != nil {
		http.Error(w, errUserIDNotInt, http.StatusInternalServerError)
		return
	}

	deviceID, err := strconv.Atoi(vars["device"])
	if err != nil {
		http.Error(w, errDeviceIDNotInt, http.StatusInternalServerError)
		return
	}

	device := models.Device{AboutDevice: models.AboutDevice{ID: deviceID}}
	response, err := env.DB.GetDeviceFromUser(models.User{ID: userID}, device)
	if err == sql.ErrNoRows {
		http.Error(w, errWrongDeviceOrUser, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = writeResponse(w, r.FormValue("out"), response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Feature handles /data/{user}/{device}/{feature}. It gets all the saved data from the
// specified feature from the given device of the user.
func (env *Env) Feature(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	userID, err := strconv.Atoi(vars["user"])
	if err != nil {
		http.Error(w, errUserIDNotInt, http.StatusInternalServerError)
		return
	}

	deviceID, err := strconv.Atoi(vars["device"])
	if err != nil {
		http.Error(w, errDeviceIDNotInt, http.StatusInternalServerError)
		return
	}

	// Check if device ID is registered with users
	deviceStruct := models.Device{AboutDevice: models.AboutDevice{ID: deviceID}}
	_, err = env.DB.GetDeviceFromUser(models.User{ID: userID}, deviceStruct)
	if err == sql.ErrNoRows {
		http.Error(w, errWrongDeviceOrUser, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// TODO: Refactor into new function and add comments
	var response models.TrackedData
	v := reflect.ValueOf(&response).Elem()
	f := vars["feature"]
	ptrToFeature := v.FieldByName(f).Addr().Interface()
	err = env.DB.GetData(models.AboutDevice{ID: deviceID}, ptrToFeature)
	if err == sql.ErrNoRows {
		http.Error(w, errWrongFeature, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = writeResponse(w, r.FormValue("out"), response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// writeResponse prints a given struct in the specified format to the response writer. If no
// format is specified it will be JSON.
// TODO: Add csv support
func writeResponse(w http.ResponseWriter, format string, response interface{}) (err error) {
	var out []byte
	switch format {
	case "", "json":
		w.Header().Set("Content-Type", "application/json")
		out, err = json.Marshal(response)
	case "xml":
		w.Header().Set("Content-Type", "application/xml")
		out, err = xml.Marshal(response)
	default:
		err = errors.New("Output type not supported.")
	}
	if err != nil {
		return
	}

	fmt.Fprint(w, string(out))
	return
}
<commit_msg>refactoring<commit_after>package controllers

import (
	"database/sql"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"

	"github.com/michaelg9/ISOC/server/models"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"golang.org/x/crypto/bcrypt"
)

const (
	errNoPasswordOrEmail   = "No email and/or password specified."
	errWrongPasswordEmail  = "Wrong password/email combination."
	errNoDeviceID          = "No device ID specified."
	errNoAPIKey            = "No API key specified."
	errWrongAPIKey         = "Wrong API key."
	errNoSessionSet        = "No session set to log-out."
	errNoToken             = "No token given."
	errTokenInvalid        = "Token is invalid."
	errTokenAlreadyInvalid = "Token already invalid."
	errWrongUser           = "No such user."
	errDeviceIDNotInt      = "The device value is not an int."
	errUserIDNotInt        = "The user value is not an int."
	errWrongFeature        = "This device has no data for this feature."
	errWrongDeviceOrUser   = "The given user ID/device ID combination is not valid."

	hmacSecret = "secret"
)

// Env contains the environment information
type Env struct {
	DB           models.Datastore
	Tokens       models.TokenControl
	SessionStore *sessions.CookieStore
}

// SignUp handles /signup
func (env *Env) SignUp(w http.ResponseWriter, r *http.Request) {
	email := r.FormValue("email")
	password := r.FormValue("password")
	if email == "" || password == "" {
		http.Error(w, errNoPasswordOrEmail, http.StatusBadRequest)
		return
	}

	// Check if the user is already in the database
	user, err := env.DB.GetUser(models.User{Email: email})
	switch {
	// User does not exist
	case user == models.User{}:
		// Create new user with hashed password
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// Insert the credentials of the new user into the database
		err = env.DB.CreateUser(models.User{Email: email, PasswordHash: string(hashedPassword)})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		fmt.Fprintf(w, "Success")
	case err != nil:
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	default:
		fmt.Fprintf(w, "User already exists")
	}
}

// Upload handles /app/0.1/upload
func (env *Env) Upload(w http.ResponseWriter, r *http.Request) {
	decoder := xml.NewDecoder(r.Body)

	// Decode the given XML from the request body into the struct defined in models
	var d models.Upload
	if err := decoder.Decode(&d); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	deviceID := d.Meta.Device
	// Check if the device ID was specified in the input.
	// If no ID was specified it defaults to 0.
	if deviceID == 0 {
		http.Error(w, errNoDeviceID, http.StatusBadRequest)
		return
	}

	// If decoding was successfull input the data into the database
	for _, data := range d.TrackedData.GetContents() {
		err := env.DB.CreateData(models.AboutDevice{ID: deviceID}, data)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	fmt.Fprint(w, "Success")
}

// UpdateUser handles /update/user
// TODO: Use Token Auth
func (env *Env) UpdateUser(w http.ResponseWriter, r *http.Request) {
	// Because of the middleware we know that these values exist
	session, _ := env.SessionStore.Get(r, "log-in")
	oldEmail := session.Values["email"]

	// We need to get the user from the database to get its user ID
	user, err := env.DB.GetUser(models.User{Email: oldEmail.(string)})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Overwrite the stored values with the values given in the request.
	// If a value isn't given it defaults to the empty string, thus we don't update it.
	user.Email = r.FormValue("email")
	user.PasswordHash = r.FormValue("password")
	user.APIKey = r.FormValue("apiKey")

	if user.Email != "" {
		// Update the current session with the new email address
		session.Values["email"] = user.Email
		if err = session.Save(r, w); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	if user.PasswordHash != "" {
		// If password is non-empty create a new hash for the database.
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.PasswordHash), bcrypt.DefaultCost)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		user.PasswordHash = string(hashedPassword)
	}

	// For the API key we use 0/1 for false/true since the actual value of the key will be determined
	// by the MySQL database by calling the UUID() function. Hence if the value is not 1 we set the
	// value in the struct to be the empty string so that the apiKey won't be updated.
	if user.APIKey != "1" {
		user.APIKey = ""
	}

	if err = env.DB.UpdateUser(user); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprint(w, "Success")
}

// User handles /data/{user}. It gets all the devices and its data from
// the user and information about the user.
// TODO: Check if userID fits with token
func (env *Env) User(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	userID, err := strconv.Atoi(vars["user"])
	if err != nil {
		http.Error(w, errUserIDNotInt, http.StatusInternalServerError)
		return
	}

	user, err := env.DB.GetUser(models.User{ID: userID})
	if err == sql.ErrNoRows {
		http.Error(w, errWrongUser, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	devices, err := env.DB.GetDevicesFromUser(user)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	response := models.UserResponse{
		User:    user,
		Devices: devices,
	}

	err = writeResponse(w, r.FormValue("out"), response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Device handles /data/{user}/{device}. It gets all the info about the
// specified device of the given user. Device should be the device ID (for now).
func (env *Env) Device(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	userID, err := strconv.Atoi(vars["user"])
	if err != nil {
		http.Error(w, errUserIDNotInt, http.StatusInternalServerError)
		return
	}

	deviceID, err := strconv.Atoi(vars["device"])
	if err != nil {
		http.Error(w, errDeviceIDNotInt, http.StatusInternalServerError)
		return
	}

	device := models.Device{AboutDevice: models.AboutDevice{ID: deviceID}}
	response, err := env.DB.GetDeviceFromUser(models.User{ID: userID}, device)
	if err == sql.ErrNoRows {
		http.Error(w, errWrongDeviceOrUser, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = writeResponse(w, r.FormValue("out"), response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Feature handles /data/{user}/{device}/{feature}. It gets all the saved data from the
// specified feature from the given device of the user.
func (env *Env) Feature(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	userID, err := strconv.Atoi(vars["user"])
	if err != nil {
		http.Error(w, errUserIDNotInt, http.StatusInternalServerError)
		return
	}

	deviceID, err := strconv.Atoi(vars["device"])
	if err != nil {
		http.Error(w, errDeviceIDNotInt, http.StatusInternalServerError)
		return
	}

	// Check if device ID is registered with users
	deviceStruct := models.Device{AboutDevice: models.AboutDevice{ID: deviceID}}
	_, err = env.DB.GetDeviceFromUser(models.User{ID: userID}, deviceStruct)
	if err == sql.ErrNoRows {
		http.Error(w, errWrongDeviceOrUser, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	response, err := getFeatureResponse(env, vars["feature"], deviceID)
	if err == sql.ErrNoRows {
		http.Error(w, errWrongFeature, http.StatusInternalServerError)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = writeResponse(w, r.FormValue("out"), response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func getFeatureResponse(env *Env, feature string, deviceID int) (response models.TrackedData, err error) {
	// Get the reflect value of the field with the name of the feature
	featureValue := reflect.ValueOf(&response).Elem().FieldByName(feature)
	// Get a pointer to the feature value
	featurePtr := featureValue.Addr().Interface()

	// Get the feature data from the specified device and save it to the response struct
	err = env.DB.GetData(models.AboutDevice{ID: deviceID}, featurePtr)
	return
}

// writeResponse prints a given struct in the specified format to the response writer. If no
// format is specified it will be JSON.
// TODO: Add csv support
func writeResponse(w http.ResponseWriter, format string, response interface{}) (err error) {
	var out []byte
	switch format {
	case "", "json":
		w.Header().Set("Content-Type", "application/json")
		out, err = json.Marshal(response)
	case "xml":
		w.Header().Set("Content-Type", "application/xml")
		out, err = xml.Marshal(response)
	default:
		err = errors.New("Output type not supported.")
	}
	if err != nil {
		return
	}

	fmt.Fprint(w, string(out))
	return
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 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 adreno

import (
	"context"
	"fmt"

	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/gapis/api/sync"
	"github.com/google/gapid/gapis/perfetto"
	"github.com/google/gapid/gapis/service"
	"github.com/google/gapid/gapis/trace/android/profile"
)

var (
	queueSubmitQuery = "" +
		"SELECT submission_id FROM gpu_slice s JOIN track t ON s.track_id = t.id WHERE s.name = 'vkQueueSubmit' AND t.name = 'Vulkan Events' ORDER BY submission_id"
	counterTracksQuery = "" +
		"SELECT id, name, unit, description FROM gpu_counter_track ORDER BY id"
	countersQueryFmt = "" +
		"SELECT ts, value FROM counter c WHERE c.track_id = %d ORDER BY ts"
	renderPassSliceName = "Surface"
)

func ProcessProfilingData(ctx context.Context, processor *perfetto.Processor,
	desc *device.GpuCounterDescriptor, handleMapping map[uint64][]service.VulkanHandleMappingItem,
	syncData *sync.Data, data *profile.ProfilingData) error {

	err := processGpuSlices(ctx, processor, handleMapping, syncData, data)
	if err != nil {
		log.Err(ctx, err, "Failed to get GPU slices")
	}
	data.Counters, err = processCounters(ctx, processor, desc)
	if err != nil {
		log.Err(ctx, err, "Failed to get GPU counters")
	}
	data.ComputeCounters(ctx)
	return nil
}

func fixContextIDs(data profile.SliceData) {
	// This is a workaround a QC bug(b/192546534)
	// that causes first deviceID to be zero after a
	// renderpass change in the same queue submit.
	// So, we fill the zero devices with the existing
	// device id, where there is only one device id.

	zeroIndices := make([]int, 0)
	contextID := int64(0)

	for i := range data {
		if data[i].Context == 0 {
			zeroIndices = append(zeroIndices, i)
			continue
		}

		if contextID == 0 {
			contextID = data[i].Context
			continue
		}

		if contextID != data[i].Context {
			// There are multiple devices
			// We cannot know which one to fill
			return
		}
	}

	for _, v := range zeroIndices {
		// If there is only one device in entire trace
		// We can assume that we possibly have only one device
		data[v].Context = contextID
	}
}

func processGpuSlices(ctx context.Context, processor *perfetto.Processor,
	handleMapping map[uint64][]service.VulkanHandleMappingItem, syncData *sync.Data,
	data *profile.ProfilingData) (err error) {

	data.Slices, err = profile.ExtractSliceData(ctx, processor)
	if err != nil {
		return log.Errf(ctx, err, "Extracting slice data failed")
	}

	queueSubmitQueryResult, err := processor.Query(queueSubmitQuery)
	if err != nil {
		return log.Errf(ctx, err, "SQL query failed: %v", queueSubmitQuery)
	}
	queueSubmitColumns := queueSubmitQueryResult.GetColumns()
	queueSubmitIds := queueSubmitColumns[0].GetLongValues()
	submissionOrdering := make(map[int64]int)

	for i, v := range queueSubmitIds {
		submissionOrdering[v] = i
	}

	fixContextIDs(data.Slices)
	data.Slices.MapIdentifiers(ctx, handleMapping)

	groupID := int32(-1)
	for i := range data.Slices {
		slice := &data.Slices[i]
		subOrder, ok := submissionOrdering[slice.Submission]
		if ok {
			cb := uint64(slice.CommandBuffer)
			key := sync.RenderPassKey{
				subOrder, cb, uint64(slice.Renderpass), uint64(slice.RenderTarget),
			}
			// Create a new group for each main renderPass slice.
			idx := syncData.RenderPassLookup.Lookup(ctx, key)
			if !idx.IsNil() && slice.Name == renderPassSliceName {
				slice.Name = fmt.Sprintf("%v-%v", idx.From, idx.To)
				groupID = data.Groups.GetOrCreateGroup(
					fmt.Sprintf("RenderPass %v, RenderTarget %v", uint64(slice.Renderpass), uint64(slice.RenderTarget)),
					idx,
				)
			}
		} else {
			log.W(ctx, "Encountered submission ID mismatch %v", slice.Submission)
		}

		if groupID < 0 {
			log.W(ctx, "Group missing for slice %v at submission %v, commandBuffer %v, renderPass %v, renderTarget %v",
				slice.Name, slice.Submission, slice.CommandBuffer, slice.Renderpass, slice.RenderTarget)
		}
		slice.GroupID = groupID
	}

	return nil
}

func processCounters(ctx context.Context, processor *perfetto.Processor, desc *device.GpuCounterDescriptor) ([]*service.ProfilingData_Counter, error) {
	counterTracksQueryResult, err := processor.Query(counterTracksQuery)
	if err != nil {
		return nil, log.Errf(ctx, err, "SQL query failed: %v", counterTracksQuery)
	}
	// t.id, name, unit, description, ts, value
	tracksColumns := counterTracksQueryResult.GetColumns()
	numTracksRows := counterTracksQueryResult.GetNumRecords()
	counters := make([]*service.ProfilingData_Counter, numTracksRows)
	// Grab all the column values. Depends on the order of columns selected in countersQuery
	trackIds := tracksColumns[0].GetLongValues()
	names := tracksColumns[1].GetStringValues()
	units := tracksColumns[2].GetStringValues()
	descriptions := tracksColumns[3].GetStringValues()

	nameToSpec := map[string]*device.GpuCounterDescriptor_GpuCounterSpec{}
	if desc != nil {
		for _, spec := range desc.Specs {
			nameToSpec[spec.Name] = spec
		}
	}

	for i := uint64(0); i < numTracksRows; i++ {
		countersQuery := fmt.Sprintf(countersQueryFmt, trackIds[i])
		countersQueryResult, err := processor.Query(countersQuery)
		countersColumns := countersQueryResult.GetColumns()
		if err != nil {
			return nil, log.Errf(ctx, err, "SQL query failed: %v", counterTracksQuery)
		}
		timestampsLong := countersColumns[0].GetLongValues()
		timestamps := make([]uint64, len(timestampsLong))
		for i, t := range timestampsLong {
			timestamps[i] = uint64(t)
		}
		values := countersColumns[1].GetDoubleValues()

		spec, _ := nameToSpec[names[i]]
		// TODO(apbodnar) Populate the `default` field once the trace processor supports it (b/147432390)
		counters[i] = &service.ProfilingData_Counter{
			Id:          uint32(trackIds[i]),
			Name:        names[i],
			Unit:        units[i],
			Description: descriptions[i],
			Spec:        spec,
			Timestamps:  timestamps,
			Values:      values,
		}
	}
	return counters, nil
}
<commit_msg>Counter grouping for Adreno GPUs.<commit_after>// Copyright (C) 2019 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 adreno

import (
	"context"
	"fmt"
	"strings"

	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/gapis/api/sync"
	"github.com/google/gapid/gapis/perfetto"
	"github.com/google/gapid/gapis/service"
	"github.com/google/gapid/gapis/trace/android/profile"
)

var (
	queueSubmitQuery = "" +
		"SELECT submission_id FROM gpu_slice s JOIN track t ON s.track_id = t.id WHERE s.name = 'vkQueueSubmit' AND t.name = 'Vulkan Events' ORDER BY submission_id"
	counterTracksQuery = "" +
		"SELECT id, name, unit, description FROM gpu_counter_track ORDER BY id"
	countersQueryFmt = "" +
		"SELECT ts, value FROM counter c WHERE c.track_id = %d ORDER BY ts"
	renderPassSliceName = "Surface"
)

func ProcessProfilingData(ctx context.Context, processor *perfetto.Processor,
	desc *device.GpuCounterDescriptor, handleMapping map[uint64][]service.VulkanHandleMappingItem,
	syncData *sync.Data, data *profile.ProfilingData) error {

	err := processGpuSlices(ctx, processor, handleMapping, syncData, data)
	if err != nil {
		log.Err(ctx, err, "Failed to get GPU slices")
	}
	data.Counters, err = processCounters(ctx, processor, desc)
	if err != nil {
		log.Err(ctx, err, "Failed to get GPU counters")
	}
	data.ComputeCounters(ctx)
	updateCounterGroups(ctx, data)
	return nil
}

func fixContextIDs(data profile.SliceData) {
	// This is a workaround a QC bug(b/192546534)
	// that causes first deviceID to be zero after a
	// renderpass change in the same queue submit.
	// So, we fill the zero devices with the existing
	// device id, where there is only one device id.

	zeroIndices := make([]int, 0)
	contextID := int64(0)

	for i := range data {
		if data[i].Context == 0 {
			zeroIndices = append(zeroIndices, i)
			continue
		}

		if contextID == 0 {
			contextID = data[i].Context
			continue
		}

		if contextID != data[i].Context {
			// There are multiple devices
			// We cannot know which one to fill
			return
		}
	}

	for _, v := range zeroIndices {
		// If there is only one device in entire trace
		// We can assume that we possibly have only one device
		data[v].Context = contextID
	}
}

func processGpuSlices(ctx context.Context, processor *perfetto.Processor,
	handleMapping map[uint64][]service.VulkanHandleMappingItem, syncData *sync.Data,
	data *profile.ProfilingData) (err error) {

	data.Slices, err = profile.ExtractSliceData(ctx, processor)
	if err != nil {
		return log.Errf(ctx, err, "Extracting slice data failed")
	}

	queueSubmitQueryResult, err := processor.Query(queueSubmitQuery)
	if err != nil {
		return log.Errf(ctx, err, "SQL query failed: %v", queueSubmitQuery)
	}
	queueSubmitColumns := queueSubmitQueryResult.GetColumns()
	queueSubmitIds := queueSubmitColumns[0].GetLongValues()
	submissionOrdering := make(map[int64]int)

	for i, v := range queueSubmitIds {
		submissionOrdering[v] = i
	}

	fixContextIDs(data.Slices)
	data.Slices.MapIdentifiers(ctx, handleMapping)

	groupID := int32(-1)
	for i := range data.Slices {
		slice := &data.Slices[i]
		subOrder, ok := submissionOrdering[slice.Submission]
		if ok {
			cb := uint64(slice.CommandBuffer)
			key := sync.RenderPassKey{
				subOrder, cb, uint64(slice.Renderpass), uint64(slice.RenderTarget),
			}
			// Create a new group for each main renderPass slice.
			idx := syncData.RenderPassLookup.Lookup(ctx, key)
			if !idx.IsNil() && slice.Name == renderPassSliceName {
				slice.Name = fmt.Sprintf("%v-%v", idx.From, idx.To)
				groupID = data.Groups.GetOrCreateGroup(
					fmt.Sprintf("RenderPass %v, RenderTarget %v", uint64(slice.Renderpass), uint64(slice.RenderTarget)),
					idx,
				)
			}
		} else {
			log.W(ctx, "Encountered submission ID mismatch %v", slice.Submission)
		}

		if groupID < 0 {
			log.W(ctx, "Group missing for slice %v at submission %v, commandBuffer %v, renderPass %v, renderTarget %v",
				slice.Name, slice.Submission, slice.CommandBuffer, slice.Renderpass, slice.RenderTarget)
		}
		slice.GroupID = groupID
	}

	return nil
}

func processCounters(ctx context.Context, processor *perfetto.Processor, desc *device.GpuCounterDescriptor) ([]*service.ProfilingData_Counter, error) {
	counterTracksQueryResult, err := processor.Query(counterTracksQuery)
	if err != nil {
		return nil, log.Errf(ctx, err, "SQL query failed: %v", counterTracksQuery)
	}
	// t.id, name, unit, description, ts, value
	tracksColumns := counterTracksQueryResult.GetColumns()
	numTracksRows := counterTracksQueryResult.GetNumRecords()
	counters := make([]*service.ProfilingData_Counter, numTracksRows)
	// Grab all the column values. Depends on the order of columns selected in countersQuery
	trackIds := tracksColumns[0].GetLongValues()
	names := tracksColumns[1].GetStringValues()
	units := tracksColumns[2].GetStringValues()
	descriptions := tracksColumns[3].GetStringValues()

	nameToSpec := map[string]*device.GpuCounterDescriptor_GpuCounterSpec{}
	if desc != nil {
		for _, spec := range desc.Specs {
			nameToSpec[spec.Name] = spec
		}
	}

	for i := uint64(0); i < numTracksRows; i++ {
		countersQuery := fmt.Sprintf(countersQueryFmt, trackIds[i])
		countersQueryResult, err := processor.Query(countersQuery)
		countersColumns := countersQueryResult.GetColumns()
		if err != nil {
			return nil, log.Errf(ctx, err, "SQL query failed: %v", counterTracksQuery)
		}
		timestampsLong := countersColumns[0].GetLongValues()
		timestamps := make([]uint64, len(timestampsLong))
		for i, t := range timestampsLong {
			timestamps[i] = uint64(t)
		}
		values := countersColumns[1].GetDoubleValues()

		spec, _ := nameToSpec[names[i]]
		// TODO(apbodnar) Populate the `default` field once the trace processor supports it (b/147432390)
		counters[i] = &service.ProfilingData_Counter{
			Id:          uint32(trackIds[i]),
			Name:        names[i],
			Unit:        units[i],
			Description: descriptions[i],
			Spec:        spec,
			Timestamps:  timestamps,
			Values:      values,
		}
	}
	return counters, nil
}

const (
	defaultCounterGroup  uint32 = 1
	veretexCounterGroup  uint32 = 2
	fragmentCounterGroup uint32 = 3
	textureCounterGroup  uint32 = 4
)

func updateCounterGroups(ctx context.Context, data *profile.ProfilingData) {
	data.CounterGroups = append(data.CounterGroups,
		&service.ProfilingData_CounterGroup{
			Id:    defaultCounterGroup,
			Label: "Performance",
		},
		&service.ProfilingData_CounterGroup{
			Id:    veretexCounterGroup,
			Label: "Vertex",
		},
		&service.ProfilingData_CounterGroup{
			Id:    fragmentCounterGroup,
			Label: "Fragment",
		},
		&service.ProfilingData_CounterGroup{
			Id:    textureCounterGroup,
			Label: "Texture",
		},
	)

	for _, counter := range data.GpuCounters.Metrics {
		name := strings.ToLower(counter.Name)
		if counter.SelectByDefault {
			counter.CounterGroupIds = append(counter.CounterGroupIds, defaultCounterGroup)
		}
		if strings.Index(name, "vertex") >= 0 {
			counter.CounterGroupIds = append(counter.CounterGroupIds, veretexCounterGroup)
		}
		if strings.Index(name, "fragment") >= 0 {
			counter.CounterGroupIds = append(counter.CounterGroupIds, fragmentCounterGroup)
		}
		if strings.Index(name, "texture") >= 0 {
			counter.CounterGroupIds = append(counter.CounterGroupIds, textureCounterGroup)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"github.com/juju/errors"

	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
)

// TODO(ericsnow) Fold this back into environ.go if neither ends up too big.

// AllocateAddress requests a specific address to be allocated for the
// given instance on the given network.
func (env *environ) AllocateAddress(instId instance.Id, netId network.Id, addr network.Address) error {
	return errors.Trace(errNotImplemented)
}

func (env *environ) ReleaseAddress(instId instance.Id, netId network.Id, addr network.Address) error {
	return errors.Trace(errNotImplemented)
}

func (env *environ) Subnets(inst instance.Id) ([]network.BasicInfo, error) {
	return nil, errors.Trace(errNotImplemented)
}

func (env *environ) ListNetworks(inst instance.Id) ([]network.BasicInfo, error) {
	return nil, errors.Trace(errNotImplemented)
}

// OpenPorts opens the given port ranges for the whole environment.
// Must only be used if the environment was setup with the
// FwGlobal firewall mode.
func (env *environ) OpenPorts(ports []network.PortRange) error {
	return errors.Trace(errNotImplemented)
}

// ClosePorts closes the given port ranges for the whole environment.
// Must only be used if the environment was setup with the
// FwGlobal firewall mode.
func (env *environ) ClosePorts(ports []network.PortRange) error {
	return errors.Trace(errNotImplemented)
}

// Ports returns the port ranges opened for the whole environment.
// Must only be used if the environment was setup with the
// FwGlobal firewall mode.
func (env *environ) Ports() ([]network.PortRange, error) {
	return nil, errors.Trace(errNotImplemented)
}

func (env *environ) openPorts(name string, ports []network.PortRange) error {
	// Compose the full set of open ports.
	currentPorts, err := env.ports(name)
	if err != nil {
		return errors.Trace(err)
	}
	inputPortsSet := network.NewPortSet(ports...)
	if inputPortsSet.IsEmpty() {
		return nil
	}
	currentPortsSet := network.NewPortSet(currentPorts...)

	// Send the request, depending on the current ports.
	if currentPortsSet.IsEmpty() {
		firewall := firewallSpec(name, inputPortsSet)
		if err := env.gce.setFirewall("", firewall); err != nil {
			return errors.Annotatef(err, "opening port(s) %+v", ports)
		}

	} else {
		newPortsSet := currentPortsSet.Union(inputPortsSet)
		firewall := firewallSpec(name, newPortsSet)
		if err := env.gce.setFirewall(name, firewall); err != nil {
			return errors.Annotatef(err, "opening port(s) %+v", ports)
		}
	}
	return nil
}

func (env *environ) closePorts(name string, ports []network.PortRange) error {
	// Compose the full set of open ports.
	currentPorts, err := env.ports(name)
	if err != nil {
		return errors.Trace(err)
	}
	inputPortsSet := network.NewPortSet(ports...)
	if inputPortsSet.IsEmpty() {
		return nil
	}
	currentPortsSet := network.NewPortSet(currentPorts...)
	newPortsSet := currentPortsSet.Difference(inputPortsSet)

	// Send the request, depending on the current ports.
	if newPortsSet.IsEmpty() {
		if err := env.gce.setFirewall(name, nil); err != nil {
			return errors.Annotatef(err, "closing port(s) %+v", ports)
		}
	} else {
		firewall := firewallSpec(name, newPortsSet)
		if err := env.gce.setFirewall(name, firewall); err != nil {
			return errors.Annotatef(err, "closing port(s) %+v", ports)
		}
	}
	return nil
}

func (env *environ) ports(name string) ([]network.PortRange, error) {
	firewall, err := env.gce.firewall(name)
	if err != nil {
		return nil, errors.Annotate(err, "while getting ports from GCE")
	}

	var ports []network.PortRange
	for _, allowed := range firewall.Allowed {
		for _, portRangeStr := range allowed.Ports {
			portRange, err := network.ParsePortRangePorts(portRangeStr, allowed.IPProtocol)
			if err != nil {
				return ports, errors.Annotate(err, "bad ports from GCE")
			}
			ports = append(ports, portRange)
		}
	}

	return ports, nil
}
<commit_msg>provider/gce: Implement the port-related environ methods.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"github.com/juju/errors"

	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	"github.com/juju/juju/provider/common"
)

// TODO(ericsnow) Fold this back into environ.go if neither ends up too big.

// AllocateAddress requests a specific address to be allocated for the
// given instance on the given network.
func (env *environ) AllocateAddress(instId instance.Id, netId network.Id, addr network.Address) error {
	return errors.Trace(errNotImplemented)
}

func (env *environ) ReleaseAddress(instId instance.Id, netId network.Id, addr network.Address) error {
	return errors.Trace(errNotImplemented)
}

func (env *environ) Subnets(inst instance.Id) ([]network.BasicInfo, error) {
	return nil, errors.Trace(errNotImplemented)
}

func (env *environ) ListNetworks(inst instance.Id) ([]network.BasicInfo, error) {
	return nil, errors.Trace(errNotImplemented)
}

func (env *environ) globalFirewallName() string {
	fwName := common.MachineFullName(env, "")
	return fwName[:len(fwName)-1]
}

// OpenPorts opens the given port ranges for the whole environment.
// Must only be used if the environment was setup with the
// FwGlobal firewall mode.
func (env *environ) OpenPorts(ports []network.PortRange) error {
	err := env.openPorts(env.globalFirewallName(), ports)
	return errors.Trace(err)
}

// ClosePorts closes the given port ranges for the whole environment.
// Must only be used if the environment was setup with the
// FwGlobal firewall mode.
func (env *environ) ClosePorts(ports []network.PortRange) error {
	err := env.closePorts(env.globalFirewallName(), ports)
	return errors.Trace(err)
}

// Ports returns the port ranges opened for the whole environment.
// Must only be used if the environment was setup with the
// FwGlobal firewall mode.
func (env *environ) Ports() ([]network.PortRange, error) {
	ports, err := env.ports(env.globalFirewallName())
	return ports, errors.Trace(err)
}

func (env *environ) openPorts(name string, ports []network.PortRange) error {
	// Compose the full set of open ports.
	currentPorts, err := env.ports(name)
	if err != nil {
		return errors.Trace(err)
	}
	inputPortsSet := network.NewPortSet(ports...)
	if inputPortsSet.IsEmpty() {
		return nil
	}
	currentPortsSet := network.NewPortSet(currentPorts...)

	// Send the request, depending on the current ports.
	if currentPortsSet.IsEmpty() {
		firewall := firewallSpec(name, inputPortsSet)
		if err := env.gce.setFirewall("", firewall); err != nil {
			return errors.Annotatef(err, "opening port(s) %+v", ports)
		}

	} else {
		newPortsSet := currentPortsSet.Union(inputPortsSet)
		firewall := firewallSpec(name, newPortsSet)
		if err := env.gce.setFirewall(name, firewall); err != nil {
			return errors.Annotatef(err, "opening port(s) %+v", ports)
		}
	}
	return nil
}

func (env *environ) closePorts(name string, ports []network.PortRange) error {
	// Compose the full set of open ports.
	currentPorts, err := env.ports(name)
	if err != nil {
		return errors.Trace(err)
	}
	inputPortsSet := network.NewPortSet(ports...)
	if inputPortsSet.IsEmpty() {
		return nil
	}
	currentPortsSet := network.NewPortSet(currentPorts...)
	newPortsSet := currentPortsSet.Difference(inputPortsSet)

	// Send the request, depending on the current ports.
	if newPortsSet.IsEmpty() {
		if err := env.gce.setFirewall(name, nil); err != nil {
			return errors.Annotatef(err, "closing port(s) %+v", ports)
		}
	} else {
		firewall := firewallSpec(name, newPortsSet)
		if err := env.gce.setFirewall(name, firewall); err != nil {
			return errors.Annotatef(err, "closing port(s) %+v", ports)
		}
	}
	return nil
}

func (env *environ) ports(name string) ([]network.PortRange, error) {
	firewall, err := env.gce.firewall(name)
	if err != nil {
		return nil, errors.Annotate(err, "while getting ports from GCE")
	}

	var ports []network.PortRange
	for _, allowed := range firewall.Allowed {
		for _, portRangeStr := range allowed.Ports {
			portRange, err := network.ParsePortRangePorts(portRangeStr, allowed.IPProtocol)
			if err != nil {
				return ports, errors.Annotate(err, "bad ports from GCE")
			}
			ports = append(ports, portRange)
		}
	}

	return ports, nil
}
<|endoftext|>


<commit_before>package oauth

import (
	"net/http"
	"strconv"
	"xorkevin.dev/governor"
	"xorkevin.dev/governor/service/cachecontrol"
	"xorkevin.dev/governor/service/image"
	"xorkevin.dev/governor/service/user/gate"
)

//go:generate forge validation -o validation_oauth_gen.go reqAppGet reqGetGroup reqAppPost reqAppPut

type (
	reqAppGet struct {
		ClientID string `valid:"clientID,has" json:"-"`
	}
)

func (m *router) getApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}
	res, err := m.s.GetApp(req.ClientID)
	if err != nil {
		c.WriteError(err)
		return
	}
	c.WriteJSON(http.StatusOK, res)
}

func (m *router) getAppLogo(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}
	img, contentType, err := m.s.GetLogo(req.ClientID)
	if err != nil {
		c.WriteError(err)
		return
	}
	defer func() {
		if err := img.Close(); err != nil {
			m.s.logger.Error("failed to close app logo", map[string]string{
				"actiontype": "getapplogo",
				"error":      err.Error(),
			})
		}
	}()
	c.WriteFile(http.StatusOK, contentType, img)
}

type (
	reqGetGroup struct {
		Amount int `valid:"amount" json:"-"`
		Offset int `valid:"offset" json:"-"`
	}
)

func (m *router) getAppGroup(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	amount, err := strconv.Atoi(c.Query("amount"))
	if err != nil {
		c.WriteError(governor.NewErrorUser("Amount invalid", http.StatusBadRequest, err))
		return
	}
	offset, err := strconv.Atoi(c.Query("offset"))
	if err != nil {
		c.WriteError(governor.NewErrorUser("Offset invalid", http.StatusBadRequest, err))
		return
	}

	req := reqGetGroup{
		Amount: amount,
		Offset: offset,
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	res, err := m.s.GetApps(amount, offset, c.Query("creatorid"))
	if err != nil {
		c.WriteError(err)
		return
	}
	c.WriteJSON(http.StatusOK, res)
}

type (
	reqAppPost struct {
		Name        string `valid:"name" json:"name"`
		URL         string `valid:"URL" json:"url"`
		RedirectURI string `valid:"redirect" json:"redirect_uri"`
		CreatorID   string `valid:"creatorID,has" json:"-"`
	}
)

func (m *router) createApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppPost{}
	if err := c.Bind(&req); err != nil {
		c.WriteError(err)
		return
	}
	req.CreatorID = gate.GetCtxUserid(c)
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	res, err := m.s.CreateApp(req.Name, req.URL, req.RedirectURI, req.CreatorID)
	if err != nil {
		c.WriteError(err)
		return
	}
	c.WriteJSON(http.StatusCreated, res)
}

type (
	reqAppPut struct {
		ClientID    string `valid:"clientID,has" json:"-"`
		Name        string `valid:"name" json:"name"`
		URL         string `valid:"URL" json:"url"`
		RedirectURI string `valid:"redirect" json:"redirect_uri"`
	}
)

func (m *router) updateApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppPut{}
	if err := c.Bind(&req); err != nil {
		c.WriteError(err)
		return
	}
	req.ClientID = c.Param("clientid")
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	if err := m.s.UpdateApp(req.ClientID, req.Name, req.URL, req.RedirectURI); err != nil {
		c.WriteError(err)
		return
	}
	c.WriteStatus(http.StatusNoContent)
}

func (m *router) updateAppLogo(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	img, err := image.LoadImage(m.s.logger, c, "image")
	if err != nil {
		c.WriteError(err)
		return
	}

	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	if err := m.s.UpdateLogo(req.ClientID, img); err != nil {
		c.WriteError(err)
		return
	}

	c.WriteStatus(http.StatusNoContent)
}

func (m *router) deleteApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	if err := m.s.Delete(req.ClientID); err != nil {
		c.WriteError(err)
		return
	}

	c.WriteStatus(http.StatusNoContent)
}

func (m *router) getAppLogoCC(c governor.Context) (string, error) {
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		return "", err
	}

	objinfo, err := m.s.StatLogo(req.ClientID)
	if err != nil {
		return "", err
	}

	return objinfo.ETag, nil
}

const (
	scopeAppRead  = "gov.user.oauth.app:read"
	scopeAppWrite = "gov.user.oauth.app:write"
)

func (m *router) mountRoutes(r governor.Router) {
	r.Get("/app/{clientid}", m.getApp)
	r.Get("/app/{clientid}/image", m.getAppLogo, cachecontrol.Control(m.s.logger, true, false, 60, m.getAppLogoCC))
	r.Get("/app", m.getAppGroup, gate.Member(m.s.gate, "oauth", scopeAppRead))
	r.Post("/app", m.getAppGroup, gate.Member(m.s.gate, "oauth", scopeAppWrite))
	r.Put("/app/{clientid}", m.updateApp, gate.Member(m.s.gate, "oauth", scopeAppWrite))
	r.Put("/app/{clientid}/image", m.updateAppLogo, gate.Member(m.s.gate, "oauth", scopeAppWrite))
	r.Delete("/app/{clientid}", m.deleteApp, gate.Member(m.s.gate, "oauth", scopeAppWrite))
}
<commit_msg>Fix oauth routes<commit_after>package oauth

import (
	"net/http"
	"strconv"
	"xorkevin.dev/governor"
	"xorkevin.dev/governor/service/cachecontrol"
	"xorkevin.dev/governor/service/image"
	"xorkevin.dev/governor/service/user/gate"
)

//go:generate forge validation -o validation_oauth_gen.go reqAppGet reqGetGroup reqAppPost reqAppPut

type (
	reqAppGet struct {
		ClientID string `valid:"clientID,has" json:"-"`
	}
)

func (m *router) getApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}
	res, err := m.s.GetApp(req.ClientID)
	if err != nil {
		c.WriteError(err)
		return
	}
	c.WriteJSON(http.StatusOK, res)
}

func (m *router) getAppLogo(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}
	img, contentType, err := m.s.GetLogo(req.ClientID)
	if err != nil {
		c.WriteError(err)
		return
	}
	defer func() {
		if err := img.Close(); err != nil {
			m.s.logger.Error("failed to close app logo", map[string]string{
				"actiontype": "getapplogo",
				"error":      err.Error(),
			})
		}
	}()
	c.WriteFile(http.StatusOK, contentType, img)
}

type (
	reqGetGroup struct {
		Amount int `valid:"amount" json:"-"`
		Offset int `valid:"offset" json:"-"`
	}
)

func (m *router) getAppGroup(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	amount, err := strconv.Atoi(c.Query("amount"))
	if err != nil {
		c.WriteError(governor.NewErrorUser("Amount invalid", http.StatusBadRequest, err))
		return
	}
	offset, err := strconv.Atoi(c.Query("offset"))
	if err != nil {
		c.WriteError(governor.NewErrorUser("Offset invalid", http.StatusBadRequest, err))
		return
	}

	req := reqGetGroup{
		Amount: amount,
		Offset: offset,
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	res, err := m.s.GetApps(amount, offset, c.Query("creatorid"))
	if err != nil {
		c.WriteError(err)
		return
	}
	c.WriteJSON(http.StatusOK, res)
}

type (
	reqAppPost struct {
		Name        string `valid:"name" json:"name"`
		URL         string `valid:"URL" json:"url"`
		RedirectURI string `valid:"redirect" json:"redirect_uri"`
		CreatorID   string `valid:"creatorID,has" json:"-"`
	}
)

func (m *router) createApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppPost{}
	if err := c.Bind(&req); err != nil {
		c.WriteError(err)
		return
	}
	req.CreatorID = gate.GetCtxUserid(c)
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	res, err := m.s.CreateApp(req.Name, req.URL, req.RedirectURI, req.CreatorID)
	if err != nil {
		c.WriteError(err)
		return
	}
	c.WriteJSON(http.StatusCreated, res)
}

type (
	reqAppPut struct {
		ClientID    string `valid:"clientID,has" json:"-"`
		Name        string `valid:"name" json:"name"`
		URL         string `valid:"URL" json:"url"`
		RedirectURI string `valid:"redirect" json:"redirect_uri"`
	}
)

func (m *router) updateApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppPut{}
	if err := c.Bind(&req); err != nil {
		c.WriteError(err)
		return
	}
	req.ClientID = c.Param("clientid")
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	if err := m.s.UpdateApp(req.ClientID, req.Name, req.URL, req.RedirectURI); err != nil {
		c.WriteError(err)
		return
	}
	c.WriteStatus(http.StatusNoContent)
}

func (m *router) updateAppLogo(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	img, err := image.LoadImage(m.s.logger, c, "image")
	if err != nil {
		c.WriteError(err)
		return
	}

	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	if err := m.s.UpdateLogo(req.ClientID, img); err != nil {
		c.WriteError(err)
		return
	}

	c.WriteStatus(http.StatusNoContent)
}

func (m *router) deleteApp(w http.ResponseWriter, r *http.Request) {
	c := governor.NewContext(w, r, m.s.logger)
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		c.WriteError(err)
		return
	}

	if err := m.s.Delete(req.ClientID); err != nil {
		c.WriteError(err)
		return
	}

	c.WriteStatus(http.StatusNoContent)
}

func (m *router) getAppLogoCC(c governor.Context) (string, error) {
	req := reqAppGet{
		ClientID: c.Param("clientid"),
	}
	if err := req.valid(); err != nil {
		return "", err
	}

	objinfo, err := m.s.StatLogo(req.ClientID)
	if err != nil {
		return "", err
	}

	return objinfo.ETag, nil
}

const (
	scopeAppRead  = "gov.user.oauth.app:read"
	scopeAppWrite = "gov.user.oauth.app:write"
)

func (m *router) mountRoutes(r governor.Router) {
	r.Get("/app/{clientid}", m.getApp)
	r.Get("/app/{clientid}/image", m.getAppLogo, cachecontrol.Control(m.s.logger, true, false, 60, m.getAppLogoCC))
	r.Get("/app", m.getAppGroup, gate.Member(m.s.gate, "oauth", scopeAppRead))
	r.Post("/app", m.createApp, gate.Member(m.s.gate, "oauth", scopeAppWrite))
	r.Put("/app/{clientid}", m.updateApp, gate.Member(m.s.gate, "oauth", scopeAppWrite))
	r.Post("/app/{clientid}/image", m.updateAppLogo, gate.Member(m.s.gate, "oauth", scopeAppWrite))
	r.Delete("/app/{clientid}", m.deleteApp, gate.Member(m.s.gate, "oauth", scopeAppWrite))
}
<|endoftext|>


<commit_before>// Copyright 2015, Klaus Post, see LICENSE for details.

// Wrapper for an SQL database backend
//
// This can be used to use an existing database for input
// output.
//
// There is constructors for
//
// See "mysql_test.go" and "postgres_test.go" for examples on
// how to create those.
//
// Note that passwords are truncated at 64 runes (not bytes).
package sqlpw

import (
	"database/sql"
)

// Sql can be used for adding and checking passwords.
// Insert and Query are generated for MySQL, and should very likely
// be changed for other databases. See the "postgres_test" for an example.
type Sql struct {
	TxBulk bool // Do bulk inserts with a transaction.
	db     *sql.DB
	query  string // Query string, used to get a count of hits
	insert string // Insert string,used to insert an item
	qStmt  *sql.Stmt
	iStmt  *sql.Stmt
}

// New returns a new database.
//
// You must give an query, that returns the number of
// rows matching the parameter given.
//
// You must give an insert statement that will insert
// the password given. It must be able to insert the
// same password multiple times without returning an error.
//
// You should manually enable bulk transactions
// modifying the TxBulk variable on the returned object
// if your database/driver supports it.
func New(db *sql.DB, query, insert string) *Sql {
	s := Sql{
		db:     db,
		query:  query,
		insert: insert,
	}
	return &s
}

// NewMysql returns a new database wrapper, set up for MySQL.
//
// You must supply a schema (that should already exist),
// as well as the column the passwords should be inserted into.
func NewMysql(db *sql.DB, schema, column string) *Sql {
	s := Sql{
		TxBulk: true,
		db:     db,
		query:  "SELECT COUNT(*) FROM `" + schema + "` WHERE `" + column + "`=?;",
		insert: "INSERT IGNORE INTO `" + schema + "` (`" + column + "`) VALUE (?);",
	}
	return &s
}

// NewPostgresql returns a new database wrapper, set up for PostgreSQL.
//
// You must supply a "schema.table" (that should already exist),
// as well as the column the passwords should be inserted into.
func NewPostgresql(db *sql.DB, table, column string) *Sql {
	s := Sql{
		TxBulk: true,
		db:     db,
		insert: `INSERT INTO ` + table + ` (` + column + `) VALUES ($1)`,
		query:  `SELECT COUNT(*) FROM  ` + table + ` WHERE ` + column + `=$1`,
	}
	return &s
}

// Add an entry to the password database
func (m *Sql) Add(s string) error {
	var err error
	if m.iStmt == nil {
		m.iStmt, err = m.db.Prepare(m.insert)
		if err != nil {
			return err
		}
	}
	_, err = m.iStmt.Exec(truncate(s))
	return err
}

// Add multiple entries to the password database
func (m *Sql) AddMultiple(s []string) error {
	var err error
	if !m.TxBulk {
		for _, pass := range s {
			err = m.Add(pass)
			if err != nil {
				return err
			}
		}
		return nil
	}
	tx, err := m.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(m.insert)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, pass := range s {
		_, err = stmt.Exec(truncate(pass))
		if err != nil {
			return err
		}
	}
	return tx.Commit()
}

// Has will return true if the database has the entry.
func (m *Sql) Has(s string) (bool, error) {
	var err error
	if m.qStmt == nil {
		m.qStmt, err = m.db.Prepare(m.query)
		if err != nil {
			return false, err
		}
	}
	var num int
	err = m.qStmt.QueryRow(truncate(s)).Scan(&num)
	if err != nil {
		return false, err
	}
	return num > 0, nil
}

func truncate(s string) string {
	r := []rune(s)
	if len(r) <= 64 {
		return s
	}
	return string(r[:64])
}
<commit_msg>Clarify documentation.<commit_after>// Copyright 2015, Klaus Post, see LICENSE for details.

// Wrapper for an SQL database backend
//
// This can be used to use an existing database for input
// output.
//
// There are constructors for MySQL and PostgreSQL, as well
// as a generic constructor that allow you to specify your
// own queries.
//
// See "mysql_test.go" and "postgres_test.go" for examples on
// how to create those.
//
// Note that passwords are truncated at 64 runes (not bytes),
// so your schema/table column must support that.
package sqlpw

import (
	"database/sql"
)

// Sql can be used for adding and checking passwords.
// Insert and Query are generated for MySQL, and should very likely
// be changed for other databases. See the "postgres_test" for an example.
type Sql struct {
	TxBulk bool // Do bulk inserts with a transaction.
	db     *sql.DB
	query  string // Query string, used to get a count of hits
	insert string // Insert string,used to insert an item
	qStmt  *sql.Stmt
	iStmt  *sql.Stmt
}

// New returns a new database.
//
// You must give an query, that returns the number of
// rows matching the parameter given.
//
// You must give an insert statement that will insert
// the password given. It must be able to insert the
// same password multiple times without returning an error.
//
// You should manually enable bulk transactions
// modifying the TxBulk variable on the returned object
// if your database/driver supports it.
func New(db *sql.DB, query, insert string) *Sql {
	s := Sql{
		db:     db,
		query:  query,
		insert: insert,
	}
	return &s
}

// NewMysql returns a new database wrapper, set up for MySQL.
//
// You must supply a schema (that should already exist),
// as well as the column the passwords should be inserted into.
func NewMysql(db *sql.DB, schema, column string) *Sql {
	s := Sql{
		TxBulk: true,
		db:     db,
		query:  "SELECT COUNT(*) FROM `" + schema + "` WHERE `" + column + "`=?;",
		insert: "INSERT IGNORE INTO `" + schema + "` (`" + column + "`) VALUE (?);",
	}
	return &s
}

// NewPostgresql returns a new database wrapper, set up for PostgreSQL.
//
// You must supply a "schema.table" (that should already exist),
// as well as the column the passwords should be inserted into.
func NewPostgresql(db *sql.DB, table, column string) *Sql {
	s := Sql{
		TxBulk: true,
		db:     db,
		insert: `INSERT INTO ` + table + ` (` + column + `) VALUES ($1)`,
		query:  `SELECT COUNT(*) FROM  ` + table + ` WHERE ` + column + `=$1`,
	}
	return &s
}

// Add an entry to the password database
func (m *Sql) Add(s string) error {
	var err error
	if m.iStmt == nil {
		m.iStmt, err = m.db.Prepare(m.insert)
		if err != nil {
			return err
		}
	}
	_, err = m.iStmt.Exec(truncate(s))
	return err
}

// Add multiple entries to the password database
func (m *Sql) AddMultiple(s []string) error {
	var err error
	if !m.TxBulk {
		for _, pass := range s {
			err = m.Add(pass)
			if err != nil {
				return err
			}
		}
		return nil
	}
	tx, err := m.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(m.insert)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, pass := range s {
		_, err = stmt.Exec(truncate(pass))
		if err != nil {
			return err
		}
	}
	return tx.Commit()
}

// Has will return true if the database has the entry.
func (m *Sql) Has(s string) (bool, error) {
	var err error
	if m.qStmt == nil {
		m.qStmt, err = m.db.Prepare(m.query)
		if err != nil {
			return false, err
		}
	}
	var num int
	err = m.qStmt.QueryRow(truncate(s)).Scan(&num)
	if err != nil {
		return false, err
	}
	return num > 0, nil
}

func truncate(s string) string {
	r := []rune(s)
	if len(r) <= 64 {
		return s
	}
	return string(r[:64])
}
<|endoftext|>


<commit_before>package main

import (
	"../../dynago"

	"code.google.com/p/gcfg"
        "github.com/gobs/cmd"
        "github.com/gobs/pretty"

	"fmt"
	"log"
	"os"
)

const (
    CONFIG_FILE = ".dynagorc"
    HISTORY_FILE = ".dynago_history"
    )

// the configuration should look like the following
// (with multiple profiles and a selected one)
//
// [dynago]
// profile=xxx
//
// [profile "xxx"]
// region=us-west-1
// accessKey=XXXXXXXX
// secretKey=YYYYYYYY

type Config struct {
	Dynago struct {
		// define default profile
		Profile string
	}

	// list of named profiles
	Profile map[string]*struct {
		Region    string
		AccessKey string
		SecretKey string
	}
}

func CompletionFunction(text string, line string, start, stop int) []string {
    return nil
}

func main() {
	var config Config

	err := gcfg.ReadFileInto(&config, CONFIG_FILE)
	if err != nil {
		log.Fatal(err)
	}

	selected := config.Dynago.Profile

	if len(os.Args) > 1 {
		// there is at least one parameter:
		// override the selected profile
		selected = os.Args[1]
	}

	profile := config.Profile[selected]

	db := dynago.NewDBClient()

	if len(profile.Region) > 0 {
		db.WithRegion(profile.Region)
	}

	if len(profile.AccessKey) > 0 {
		db.WithCredentials(profile.AccessKey, profile.SecretKey)
	}

	commander := &cmd.Cmd{HistoryFile: HISTORY_FILE, Complete: CompletionFunction, EnableShell: true}
        commander.Prompt = "dynagosh> "
	commander.Init()

	commander.Add(cmd.Command{"config",
		`
		config : display current configuration
		`,
		func(string) (stop bool) {
			pretty.PrettyPrint(config)
			return
		}})

        commander.Add(cmd.Command{"list",
                `
                list : display list of available tables
                `,
                func(string) (stop bool) {
	            tables, err := db.ListTables()

	            if err != nil {
		        fmt.Println(err)
                        return
                    }

                    fmt.Println("Available tables")

                    for _, tableName := range tables {
                        fmt.Println("  ", tableName)
                    }

                    return
                }})

        commander.Add(cmd.Command{"describe",
                `
                describe {table} : display table configuration
                `,
                func(line string) (stop bool) {
                    tableName := line
		    table, err := db.DescribeTable(tableName)
		    if err != nil {
			fmt.Println(err)
		    } else {
		        pretty.PrettyPrint(table)
                    }

                    return
                }})

	commander.Commands["ls"] = commander.Commands["list"]

	commander.CmdLoop()
}
<commit_msg>- Added ReadConfig, that look in current working directory or home   directory. - Reformatted code<commit_after>package main

import (
	"../../dynago"

	"code.google.com/p/gcfg"
	"github.com/gobs/cmd"
	"github.com/gobs/pretty"

	"fmt"
	"log"
	"os"
	"path"
	"strings"
)

const (
	CONFIG_FILE  = ".dynagorc"
	HISTORY_FILE = ".dynago_history"
)

// the configuration should look like the following
// (with multiple profiles and a selected one)
//
// [dynago]
// profile=xxx
//
// [profile "xxx"]
// region=us-west-1
// accessKey=XXXXXXXX
// secretKey=YYYYYYYY

type Config struct {
	Dynago struct {
		// define default profile
		Profile string
	}

	// list of named profiles
	Profile map[string]*struct {
		Region    string
		AccessKey string
		SecretKey string
	}
}

func ReadConfig(configFile string, config *Config) {
	// configFile in current directory or full path
	if _, err := os.Stat(configFile); err != nil {
		if strings.Contains(configFile, "/") {
			return
		}

		// configFile in home directory
		configFile = path.Join(os.Getenv("HOME"), configFile)
		if _, err := os.Stat(configFile); err != nil {
			return
		}
	}

	err := gcfg.ReadFileInto(config, configFile)
	if err != nil {
		log.Fatal(err)
	}
}

func CompletionFunction(text string, line string, start, stop int) []string {
	return nil
}

func main() {
	var config Config
	ReadConfig(CONFIG_FILE, &config)

	selected := config.Dynago.Profile

	if len(os.Args) > 1 {
		// there is at least one parameter:
		// override the selected profile
		selected = os.Args[1]
	}

	profile := config.Profile[selected]
	if profile == nil {
		log.Fatal("no profile selected")
	}

	db := dynago.NewDBClient()

	if len(profile.Region) > 0 {
		db.WithRegion(profile.Region)
	}

	if len(profile.AccessKey) > 0 {
		db.WithCredentials(profile.AccessKey, profile.SecretKey)
	}

	commander := &cmd.Cmd{HistoryFile: HISTORY_FILE, Complete: CompletionFunction, EnableShell: true}
	commander.Prompt = "dynagosh> "
	commander.Init()

	commander.Add(cmd.Command{"config",
		`
		config : display current configuration
		`,
		func(string) (stop bool) {
			pretty.PrettyPrint(config)
			return
		}})

	commander.Add(cmd.Command{"list",
		`
                list : display list of available tables
                `,
		func(string) (stop bool) {
			tables, err := db.ListTables()

			if err != nil {
				fmt.Println(err)
				return
			}

			fmt.Println("Available tables")

			for _, tableName := range tables {
				fmt.Println("  ", tableName)
			}

			return
		}})

	commander.Add(cmd.Command{"describe",
		`
                describe {table} : display table configuration
                `,
		func(line string) (stop bool) {
			tableName := line
			table, err := db.DescribeTable(tableName)
			if err != nil {
				fmt.Println(err)
			} else {
				pretty.PrettyPrint(table)
			}

			return
		}})

	commander.Commands["ls"] = commander.Commands["list"]

	commander.CmdLoop()
}
<|endoftext|>


<commit_before>/* A simmple example of how to use the panel ncurses library */

package main

import . "goncurses.googlecode.com/hg/goncurses"

func main() {
    stdscr, _ := Init()
    defer End()

    StartColor()
    CBreak(true)
    Echo(true)
    stdscr.Keypad(true)
    stdscr.Print("Hit 'tab' to cycle through windows, 'q' to quit")

    InitPair(1, C_RED, C_BLACK)
    InitPair(2, C_GREEN, C_BLACK)
    InitPair(3, C_BLUE, C_BLACK)
    InitPair(4, C_CYAN, C_BLACK)

    var panels [3]*Panel
    y, x := 4, 10

    for i := 0; i < 3; i++ {
        h, w := 10, 40
        title := "Window Number %d"

        window, _ := NewWindow(h, w , y+(i*4), x+(i*10))
        window.Box(0, 0)
        window.AddChar(2, 0, ACS_LTEE)
        window.HLine(2, 1, ACS_HLINE, w - 2)
        window.AddChar(2, w-1, ACS_RTEE)
        window.ColorOn(byte(i+1))
        window.Print(1, (w/2)-(len(title)/2), title, i+1)
        window.ColorOff(byte(i+1))
        panels[i] = NewPanel(window)

    }

    active := 2

    for {
        UpdatePanels()
        Update()

        switch stdscr.GetChar() {
        case 'q':
            return
        case KEY_TAB:
            active += 1
            if active > 2 {
                active = 0
            }
            panels[active].Top()
        }
    }
}
<commit_msg>Update panel2 example<commit_after>/* A simmple example of how to use the panel ncurses library */

package main

import gc "code.google.com/p/goncurses"

func main() {
	stdscr, _ := gc.Init()
	defer gc.End()

	gc.StartColor()
	gc.CBreak(true)
	gc.Echo(true)
	stdscr.Keypad(true)
	stdscr.Print("Hit 'tab' to cycle through windows, 'q' to quit")

	gc.InitPair(1, gc.C_RED, gc.C_BLACK)
	gc.InitPair(2, gc.C_GREEN, gc.C_BLACK)
	gc.InitPair(3, gc.C_BLUE, gc.C_BLACK)
	gc.InitPair(4, gc.C_CYAN, gc.C_BLACK)

	var panels [3]*gc.Panel
	y, x := 4, 10

	for i := 0; i < 3; i++ {
		h, w := 10, 40
		title := "Window Number %d"

		window, _ := gc.NewWindow(h, w, y+(i*4), x+(i*10))
		window.Box(0, 0)
		window.MoveAddChar(2, 0, gc.ACS_LTEE)
		window.HLine(2, 1, gc.ACS_HLINE, w-2)
		window.MoveAddChar(2, w-1, gc.ACS_RTEE)
		window.ColorOn(byte(i + 1))
		window.Print(1, (w/2)-(len(title)/2), title, i+1)
		window.ColorOff(byte(i + 1))
		panels[i] = gc.NewPanel(&window)

	}

	active := 2

	for {
		gc.UpdatePanels()
		gc.Update()

		switch stdscr.GetChar() {
		case 'q':
			return
		case gc.KEY_TAB:
			active += 1
			if active > 2 {
				active = 0
			}
			panels[active].Top()
		}
	}
}
<|endoftext|>


<commit_before>package payment

import (
	"errors"
	"fmt"
	"koding/db/models"
	"koding/db/mongodb/modelhelper"
	"koding/helpers"
	"sync"
	"time"

	"gopkg.in/fatih/set.v0"
	"gopkg.in/mgo.v2/bson"

	"github.com/stripe/stripe-go"
	"github.com/stripe/stripe-go/customer"
	stripeplan "github.com/stripe/stripe-go/plan"
	"github.com/stripe/stripe-go/sub"
)

var (
	// ErrCustomerNotSubscribedToAnyPlans error for not subscribed users
	ErrCustomerNotSubscribedToAnyPlans = errors.New("user is not subscribed to any plans")
	// ErrCustomerNotExists error for not created users
	ErrCustomerNotExists = errors.New("user is not created for subscription")
)

type Usage struct {
	User            *UserInfo
	ExpectedPlan    *stripe.Plan
	Due             uint64
	NextBillingDate time.Time
	Subscription    *stripe.Sub
}

type UserInfo struct {
	Total   int
	Active  int
	Deleted int
}

// DeleteCustomerForGroup deletes the customer for a given group. If customer is
// not registered, returns error. If customer is already deleted, returns success.
func DeleteCustomerForGroup(groupName string) error {
	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return err
	}

	if group.Payment.Customer.ID == "" {
		return ErrCustomerNotExists
	}

	if err := deleteCustomer(group.Payment.Customer.ID); err != nil {
		return err
	}

	return modelhelper.UpdateGroupPartial(
		modelhelper.Selector{"_id": group.Id},
		modelhelper.Selector{
			// deleting customer deletes everything belong to that customer in stripe,
			// so say we all
			"$unset": modelhelper.Selector{"payment": ""},
		},
	)
}

// UpdateCustomerForGroup updates customer data of a group`
func UpdateCustomerForGroup(username, groupName string, params *stripe.CustomerParams) (*stripe.Customer, error) {
	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return nil, err
	}

	if group.Payment.Customer.ID == "" {
		return nil, ErrCustomerNotExists
	}

	params, err = populateCustomerParams(username, groupName, params)
	if err != nil {
		return nil, err
	}

	return customer.Update(group.Payment.Customer.ID, params)
}

// GetCustomerForGroup get the registered customer info of a group if exists
func GetCustomerForGroup(groupName string) (*stripe.Customer, error) {
	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return nil, err
	}

	if group.Payment.Customer.ID == "" {
		return nil, ErrCustomerNotExists
	}

	return customer.Get(group.Payment.Customer.ID, nil)
}

// GetInfoForGroup get the current usage info of a group
func GetInfoForGroup(group *models.Group) (*Usage, error) {
	usage, err := fetchParallelizableeUsageItems(group)
	if err != nil {
		return nil, err
	}

	plan, err := getPlan(usage.Subscription, usage.User.Total)
	if err != nil {
		return nil, err
	}

	usage.ExpectedPlan = plan
	usage.Due = uint64(usage.User.Total) * plan.Amount

	return usage, nil

}

func fetchParallelizableeUsageItems(group *models.Group) (*Usage, error) {
	var infoErr error
	var errMu sync.Mutex
	var wg sync.WaitGroup

	withCheck := func(f func() error) {
		errMu.Lock()
		if infoErr != nil {
			errMu.Unlock()
			return
		}
		errMu.Unlock()

		err := f()
		if err != nil {
			errMu.Lock()
			infoErr = err
			errMu.Unlock()
		}
		wg.Done()
	}

	var cus *stripe.Customer
	wg.Add(1)
	go withCheck(func() (err error) {
		if group.Payment.Customer.ID == "" {
			return ErrCustomerNotExists
		}

		cus, err = customer.Get(group.Payment.Customer.ID, nil)
		return err
	})

	var subscription *stripe.Sub
	wg.Add(1)
	go withCheck(func() (err error) {
		if group.Payment.Subscription.ID == "" {
			return ErrCustomerNotSubscribedToAnyPlans
		}

		subscription, err = sub.Get(group.Payment.Subscription.ID, nil)
		return err
	})

	var activeCount int
	wg.Add(1)
	go withCheck(func() (err error) {
		activeCount, err = calculateActiveUserCount(group.Id)
		return err
	})

	var deletedCount int
	wg.Add(1)
	go withCheck(func() (err error) {
		deletedCount, err = modelhelper.GetDeletedMemberCountByGroupId(group.Id)
		return err
	})

	wg.Wait()

	if infoErr != nil {
		return nil, infoErr
	}

	totalCount := activeCount + deletedCount

	// if the team is in trialing period, and when we want to charge them, do
	// not include the deleted members
	if subscription.Status == "trialing" {
		totalCount = activeCount
	}

	usage := &Usage{
		User: &UserInfo{
			Total:   totalCount,
			Active:  activeCount,
			Deleted: deletedCount,
		},
		ExpectedPlan:    nil,
		Due:             0,
		NextBillingDate: time.Unix(subscription.PeriodEnd, 0),
		Subscription:    subscription,
	}

	return usage, nil
}

func getPlan(subscription *stripe.Sub, totalCount int) (*stripe.Plan, error) {
	plan := subscription.Plan

	if plan.Amount == 0 { // we are on trial period
		return plan, nil
	}

	// in the cases where the active subscription and the have-to-be
	// subscription is different, fetch the real plan from system. This can only
	// happen if the team got more members than the previous subscription's user
	// count in the current month. The subscription will be automatically fixed
	// on the next billing date. We do not change the subscription on each user
	// addition or deletion becasue Stripe charges the user whenever a
	// subscription change happens, so we only change the subscription on the
	// billing date with cancelling the previous subscription & invoice and
	// creating a new subscription with new requirement
	if plan.ID == GetPlanID(totalCount) {
		return plan, nil
	}

	return stripeplan.Get(GetPlanID(totalCount), nil)
}

func createFilter(groupID bson.ObjectId) modelhelper.Selector {
	return modelhelper.Selector{
		"as":         modelhelper.Selector{"$in": []string{"owner", "admin", "member"}},
		"targetName": "JAccount",
		"sourceName": "JGroup",
		"sourceId":   groupID,
	}
}

// calculateActiveUserCount calculates the active user count from the
// relationship collection. I tried using map-reduce first but that was so
// magical from  engineering perspective and we dont have any other usage of it
// in our system. Then i implemented it using "aggregate" framework, that worked
// pretty well indeed but it is fetching all the records from database at once,
// so decided to use our battle tested iter support, it handles iterations, bulk
// operations, timeouts. We are actively using it for iterating over millions of
// records without hardening on the database.
//
//
// Sample aggregate function
//
// db.relationships.aggregate([
//     { "$match": { as: { $in : [ "owner", "admin", "member" ] }, targetName:"JAccount", sourceName:"JGroup", sourceId: ObjectId("") }},
//     // Count all occurrences
//     { "$group": {
//         "_id": {
//             "targetId": "$targetId"
//         },
//         "count": { "$sum": 1 }
//     }},
//     // Sum all occurrences and count distinct
//     { "$group": {
//         "_id": {
//             "targetId": "$_id.targetId"
//         },
//         "totalCount": { "$sum": "$count" },
//         "distinctCount": { "$sum": 1 }
//     }}
// ])
//
func calculateActiveUserCount(groupID bson.ObjectId) (int, error) {
	accounts := set.New()

	iterOptions := helpers.NewIterOptions()
	iterOptions.F = func(rel interface{}) error {
		result, ok := rel.(*models.Relationship)
		if !ok {
			return errors.New("not a relationship")
		}
		accounts.Add(result.TargetId)
		return nil
	}
	iterOptions.CollectionName = modelhelper.RelationshipColl
	iterOptions.Filter = createFilter(groupID)
	iterOptions.Result = &models.Relationship{}
	iterOptions.Limit = 0
	iterOptions.Skip = 0
	// iterOptions.Log = log

	err := helpers.Iter(modelhelper.Mongo, iterOptions)
	if err != nil {
		return 0, err
	}

	return accounts.Size(), nil
}

// CreateCustomerForGroup registers a customer for a group
func CreateCustomerForGroup(username, groupName string, req *stripe.CustomerParams) (*stripe.Customer, error) {
	req, err := populateCustomerParams(username, groupName, req)
	if err != nil {
		return nil, err
	}

	cus, err := customer.New(req)
	if err != nil {
		return nil, err
	}

	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return nil, err
	}

	if err := modelhelper.UpdateGroupPartial(
		modelhelper.Selector{"_id": group.Id},
		modelhelper.Selector{
			"$set": modelhelper.Selector{
				"payment.customer.id": cus.ID,
			},
		},
	); err != nil {
		return nil, err
	}

	return cus, nil
}

// deleteCustomer tries to make customer deletion idempotent
func deleteCustomer(customerID string) error {
	cus, err := customer.Del(customerID)
	if cus != nil && cus.Deleted { // if customer is already deleted previously
		return nil
	}

	return err
}

func populateCustomerParams(username, groupName string, req *stripe.CustomerParams) (*stripe.CustomerParams, error) {
	if req == nil {
		req = &stripe.CustomerParams{}
	}

	user, err := modelhelper.GetUser(username)
	if err != nil {
		return nil, err
	}

	if req.Desc == "" {
		req.Desc = fmt.Sprintf("%s team", groupName)
	}
	if req.Email == "" {
		req.Email = user.Email
	}

	if req.Params.Meta == nil {
		req.Params.Meta = make(map[string]string)
	}
	req.Params.Meta["groupName"] = groupName
	req.Params.Meta["username"] = username

	return req, nil
}
<commit_msg>go/payment: whitelist to be updated customer params<commit_after>package payment

import (
	"errors"
	"fmt"
	"koding/db/models"
	"koding/db/mongodb/modelhelper"
	"koding/helpers"
	"sync"
	"time"

	"gopkg.in/fatih/set.v0"
	"gopkg.in/mgo.v2/bson"

	"github.com/stripe/stripe-go"
	"github.com/stripe/stripe-go/customer"
	stripeplan "github.com/stripe/stripe-go/plan"
	"github.com/stripe/stripe-go/sub"
)

var (
	// ErrCustomerNotSubscribedToAnyPlans error for not subscribed users
	ErrCustomerNotSubscribedToAnyPlans = errors.New("user is not subscribed to any plans")
	// ErrCustomerNotExists error for not created users
	ErrCustomerNotExists = errors.New("user is not created for subscription")
)

type Usage struct {
	User            *UserInfo
	ExpectedPlan    *stripe.Plan
	Due             uint64
	NextBillingDate time.Time
	Subscription    *stripe.Sub
}

type UserInfo struct {
	Total   int
	Active  int
	Deleted int
}

// DeleteCustomerForGroup deletes the customer for a given group. If customer is
// not registered, returns error. If customer is already deleted, returns success.
func DeleteCustomerForGroup(groupName string) error {
	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return err
	}

	if group.Payment.Customer.ID == "" {
		return ErrCustomerNotExists
	}

	if err := deleteCustomer(group.Payment.Customer.ID); err != nil {
		return err
	}

	return modelhelper.UpdateGroupPartial(
		modelhelper.Selector{"_id": group.Id},
		modelhelper.Selector{
			// deleting customer deletes everything belong to that customer in stripe,
			// so say we all
			"$unset": modelhelper.Selector{"payment": ""},
		},
	)
}

// UpdateCustomerForGroup updates customer data of a group`
func UpdateCustomerForGroup(username, groupName string, params *stripe.CustomerParams) (*stripe.Customer, error) {
	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return nil, err
	}

	if group.Payment.Customer.ID == "" {
		return nil, ErrCustomerNotExists
	}

	params, err = populateCustomerParams(username, groupName, params)
	if err != nil {
		return nil, err
	}

	return customer.Update(group.Payment.Customer.ID, params)
}

// GetCustomerForGroup get the registered customer info of a group if exists
func GetCustomerForGroup(groupName string) (*stripe.Customer, error) {
	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return nil, err
	}

	if group.Payment.Customer.ID == "" {
		return nil, ErrCustomerNotExists
	}

	return customer.Get(group.Payment.Customer.ID, nil)
}

// GetInfoForGroup get the current usage info of a group
func GetInfoForGroup(group *models.Group) (*Usage, error) {
	usage, err := fetchParallelizableeUsageItems(group)
	if err != nil {
		return nil, err
	}

	plan, err := getPlan(usage.Subscription, usage.User.Total)
	if err != nil {
		return nil, err
	}

	usage.ExpectedPlan = plan
	usage.Due = uint64(usage.User.Total) * plan.Amount

	return usage, nil

}

func fetchParallelizableeUsageItems(group *models.Group) (*Usage, error) {
	var infoErr error
	var errMu sync.Mutex
	var wg sync.WaitGroup

	withCheck := func(f func() error) {
		errMu.Lock()
		if infoErr != nil {
			errMu.Unlock()
			return
		}
		errMu.Unlock()

		err := f()
		if err != nil {
			errMu.Lock()
			infoErr = err
			errMu.Unlock()
		}
		wg.Done()
	}

	var cus *stripe.Customer
	wg.Add(1)
	go withCheck(func() (err error) {
		if group.Payment.Customer.ID == "" {
			return ErrCustomerNotExists
		}

		cus, err = customer.Get(group.Payment.Customer.ID, nil)
		return err
	})

	var subscription *stripe.Sub
	wg.Add(1)
	go withCheck(func() (err error) {
		if group.Payment.Subscription.ID == "" {
			return ErrCustomerNotSubscribedToAnyPlans
		}

		subscription, err = sub.Get(group.Payment.Subscription.ID, nil)
		return err
	})

	var activeCount int
	wg.Add(1)
	go withCheck(func() (err error) {
		activeCount, err = calculateActiveUserCount(group.Id)
		return err
	})

	var deletedCount int
	wg.Add(1)
	go withCheck(func() (err error) {
		deletedCount, err = modelhelper.GetDeletedMemberCountByGroupId(group.Id)
		return err
	})

	wg.Wait()

	if infoErr != nil {
		return nil, infoErr
	}

	totalCount := activeCount + deletedCount

	// if the team is in trialing period, and when we want to charge them, do
	// not include the deleted members
	if subscription.Status == "trialing" {
		totalCount = activeCount
	}

	usage := &Usage{
		User: &UserInfo{
			Total:   totalCount,
			Active:  activeCount,
			Deleted: deletedCount,
		},
		ExpectedPlan:    nil,
		Due:             0,
		NextBillingDate: time.Unix(subscription.PeriodEnd, 0),
		Subscription:    subscription,
	}

	return usage, nil
}

func getPlan(subscription *stripe.Sub, totalCount int) (*stripe.Plan, error) {
	plan := subscription.Plan

	if plan.Amount == 0 { // we are on trial period
		return plan, nil
	}

	// in the cases where the active subscription and the have-to-be
	// subscription is different, fetch the real plan from system. This can only
	// happen if the team got more members than the previous subscription's user
	// count in the current month. The subscription will be automatically fixed
	// on the next billing date. We do not change the subscription on each user
	// addition or deletion becasue Stripe charges the user whenever a
	// subscription change happens, so we only change the subscription on the
	// billing date with cancelling the previous subscription & invoice and
	// creating a new subscription with new requirement
	if plan.ID == GetPlanID(totalCount) {
		return plan, nil
	}

	return stripeplan.Get(GetPlanID(totalCount), nil)
}

func createFilter(groupID bson.ObjectId) modelhelper.Selector {
	return modelhelper.Selector{
		"as":         modelhelper.Selector{"$in": []string{"owner", "admin", "member"}},
		"targetName": "JAccount",
		"sourceName": "JGroup",
		"sourceId":   groupID,
	}
}

// calculateActiveUserCount calculates the active user count from the
// relationship collection. I tried using map-reduce first but that was so
// magical from  engineering perspective and we dont have any other usage of it
// in our system. Then i implemented it using "aggregate" framework, that worked
// pretty well indeed but it is fetching all the records from database at once,
// so decided to use our battle tested iter support, it handles iterations, bulk
// operations, timeouts. We are actively using it for iterating over millions of
// records without hardening on the database.
//
//
// Sample aggregate function
//
// db.relationships.aggregate([
//     { "$match": { as: { $in : [ "owner", "admin", "member" ] }, targetName:"JAccount", sourceName:"JGroup", sourceId: ObjectId("") }},
//     // Count all occurrences
//     { "$group": {
//         "_id": {
//             "targetId": "$targetId"
//         },
//         "count": { "$sum": 1 }
//     }},
//     // Sum all occurrences and count distinct
//     { "$group": {
//         "_id": {
//             "targetId": "$_id.targetId"
//         },
//         "totalCount": { "$sum": "$count" },
//         "distinctCount": { "$sum": 1 }
//     }}
// ])
//
func calculateActiveUserCount(groupID bson.ObjectId) (int, error) {
	accounts := set.New()

	iterOptions := helpers.NewIterOptions()
	iterOptions.F = func(rel interface{}) error {
		result, ok := rel.(*models.Relationship)
		if !ok {
			return errors.New("not a relationship")
		}
		accounts.Add(result.TargetId)
		return nil
	}
	iterOptions.CollectionName = modelhelper.RelationshipColl
	iterOptions.Filter = createFilter(groupID)
	iterOptions.Result = &models.Relationship{}
	iterOptions.Limit = 0
	iterOptions.Skip = 0
	// iterOptions.Log = log

	err := helpers.Iter(modelhelper.Mongo, iterOptions)
	if err != nil {
		return 0, err
	}

	return accounts.Size(), nil
}

// CreateCustomerForGroup registers a customer for a group
func CreateCustomerForGroup(username, groupName string, req *stripe.CustomerParams) (*stripe.Customer, error) {
	req, err := populateCustomerParams(username, groupName, req)
	if err != nil {
		return nil, err
	}

	cus, err := customer.New(req)
	if err != nil {
		return nil, err
	}

	group, err := modelhelper.GetGroup(groupName)
	if err != nil {
		return nil, err
	}

	if err := modelhelper.UpdateGroupPartial(
		modelhelper.Selector{"_id": group.Id},
		modelhelper.Selector{
			"$set": modelhelper.Selector{
				"payment.customer.id": cus.ID,
			},
		},
	); err != nil {
		return nil, err
	}

	return cus, nil
}

// deleteCustomer tries to make customer deletion idempotent
func deleteCustomer(customerID string) error {
	cus, err := customer.Del(customerID)
	if cus != nil && cus.Deleted { // if customer is already deleted previously
		return nil
	}

	return err
}

func populateCustomerParams(username, groupName string, initial *stripe.CustomerParams) (*stripe.CustomerParams, error) {
	if initial == nil {
		initial = &stripe.CustomerParams{}
	}

	// whitelisted parameters
	req := &stripe.CustomerParams{
		Token:  initial.Token,
		Coupon: initial.Coupon,
		Source: initial.Source,
		Desc:   initial.Desc,
		Email:  initial.Email,
		Params: initial.Params,
		// plan can not be updated by hand, do not add it to whilelist. It should
		// only be updated automatically on invoice applications
		// Plan: initial.Plan,
	}

	user, err := modelhelper.GetUser(username)
	if err != nil {
		return nil, err
	}

	if req.Desc == "" {
		req.Desc = fmt.Sprintf("%s team", groupName)
	}
	if req.Email == "" {
		req.Email = user.Email
	}

	if req.Params.Meta == nil {
		req.Params.Meta = make(map[string]string)
	}
	req.Params.Meta["groupName"] = groupName
	req.Params.Meta["username"] = username

	return req, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	ssdp "github.com/pulento/go-ssdp"
)

func main() {
	t := flag.String("t", ssdp.All, "search type")
	w := flag.Int("w", 1, "wait time")
	l := flag.String("l", "", "local address to listen")
	m := flag.String("m", "", "multicast address to send")
	v := flag.Bool("v", false, "verbose mode")
	h := flag.Bool("h", false, "show help")
	flag.Parse()
	if *h {
		flag.Usage()
		return
	}
	if *v {
		ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
	}
	list, err := ssdp.Search(*t, *w, *l, *m)
	if err != nil {
		log.Fatal(err)
	}
	for i, srv := range list {
		//fmt.Printf("%d: %#v\n", i, srv)
		fmt.Printf("%d: %s %s\n", i, srv.Type, srv.Location)
	}
}
<commit_msg>Typo introduced by VSCode autocomplete<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/koron/go-ssdp"
)

func main() {
	t := flag.String("t", ssdp.All, "search type")
	w := flag.Int("w", 1, "wait time")
	l := flag.String("l", "", "local address to listen")
	m := flag.String("m", "", "multicast address to send")
	v := flag.Bool("v", false, "verbose mode")
	h := flag.Bool("h", false, "show help")
	flag.Parse()
	if *h {
		flag.Usage()
		return
	}
	if *v {
		ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
	}
	list, err := ssdp.Search(*t, *w, *l, *m)
	if err != nil {
		log.Fatal(err)
	}
	for i, srv := range list {
		//fmt.Printf("%d: %#v\n", i, srv)
		fmt.Printf("%d: %s %s\n", i, srv.Type, srv.Location)
	}
}
<|endoftext|>