<commit_before>package masterapi

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"github.com/mattn/go-sqlite3"
	"net/http"
	"strconv"
)

type RiskGroup struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

func (m *MasterAPI) RiskGroupIndex(w http.ResponseWriter, r *http.Request) {
	tx := m.DB.Begin()
	defer tx.Rollback()

	var riskGroups []*model.RiskGroup
	err := tx.Order("id", false).Find(&riskGroups).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	out := make([]*RiskGroup, len(riskGroups))
	for i, v := range riskGroups {
		out[i] = ProjectModelRiskGroupToRiskGroup(v)
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) RiskGroupById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	if id == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "id may not be 0")
		return
	}

	tx := m.DB.Begin()
	defer tx.Rollback()

	var riskgroup model.RiskGroup
	res := tx.First(&riskgroup, id)

	if res.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err = res.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	json.NewEncoder(w).Encode(ProjectModelRiskGroupToRiskGroup(&riskgroup))
	return
}

func (m *MasterAPI) RiskGroupPut(w http.ResponseWriter, r *http.Request) {
	var postRiskGroup RiskGroup
	err := json.NewDecoder(r.Body).Decode(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postRiskGroup.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not specify the risk group ID in PUT request")
		return
	}

	modelRiskGroup, err := ProjectRiskGroupToModelRiskGroup(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Persist to database

	tx := m.DB.Begin()

	err = tx.Create(&modelRiskGroup).Error

	//Check db specific errors
	if model.IsIntegrityConstraintViolation(err) {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	} else if err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

	// Return created risk group

	json.NewEncoder(w).Encode(ProjectModelRiskGroupToRiskGroup(modelRiskGroup))

	return
}

func (m *MasterAPI) RiskGroupUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	var postRiskGroup RiskGroup
	err = json.NewDecoder(r.Body).Decode(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postRiskGroup.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	// Check if risk group with id exists

	tx := m.DB.Begin()

	var modelRiskGroup model.RiskGroup
	findRes := tx.First(&modelRiskGroup, id)
	if findRes.RecordNotFound() {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		return
	} else if err = findRes.Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	// Allow update

	save, err := ProjectRiskGroupToModelRiskGroup(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Persist to database

	err = tx.Save(&save).Error

	// Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) RiskGroupDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	tx := m.DB.Begin()

	// Can only delete risk groups without slaves
	var currentRiskGroup model.RiskGroup
	findRes := tx.First(&currentRiskGroup, id)
	if findRes.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	} else if err = findRes.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		tx.Rollback()
		fmt.Fprint(w, err.Error())
		return
	}

	if err = tx.First(&currentRiskGroup, id).Related(&currentRiskGroup.Slaves, "Slaves").Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		tx.Rollback()
		fmt.Fprint(w, err.Error())
		return
	}

	if len(currentRiskGroup.Slaves) != 0 {
		w.WriteHeader(http.StatusForbidden)
		tx.Rollback()
		fmt.Fprintf(w, "riskgroup with id %d has assigned slaves", currentRiskGroup.ID)
		return
	}

	// Allow delete

	s := tx.Delete(&model.RiskGroup{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}
	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) RiskGroupGetSlaves(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]

	id := sql.NullInt64{}

	// Check if risk group exists
	// Special case: id == 0 => Get unassigned slaves
	if idStr != "null" {
		idInt64, err := strconv.ParseInt(idStr, 10, 0)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		id.Valid = true
		id.Int64 = idInt64
	}
	// else id is NULL

	tx := m.DB.Begin()
	defer tx.Rollback()

	// Check if risk group exists
	// Special case: id == 0 => Get unassigned slaves
	if id.Valid {
		var riskgroup model.RiskGroup
		riskgroupRes := tx.First(&riskgroup, id)
		if riskgroupRes.RecordNotFound() {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "Riskgroup not found")
			return
		} else if err := riskgroupRes.Error; err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, err.Error())
			return
		}
	}

	var slaves []*model.Slave
	var err error
	if id.Valid { // gorm/gosql does not query for IS NULL automatically
		err = tx.Where("risk_group_id = ?", id).Find(&slaves).Error
	} else {
		err = tx.Where("risk_group_id IS NULL").Find(&slaves).Error
	}
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}

	out := make([]*Slave, len(slaves))
	for i, v := range slaves {
		out[i], err = ProjectModelSlaveToSlave(tx, v)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err.Error())
			return
		}
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) RiskGroupAssignSlave(w http.ResponseWriter, r *http.Request) {
	riskgroupIdStr := mux.Vars(r)["riskgroupId"]
	riskgroupId, err := strconv.ParseInt(riskgroupIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	slaveIdStr := mux.Vars(r)["slaveId"]
	slaveId, err := strconv.ParseInt(slaveIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if riskgroupId == 0 || slaveId == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Id may not be 0")
		return
	}

	tx := m.DB.Begin()

	var riskgroup model.RiskGroup
	riskgroupRes := tx.First(&riskgroup, riskgroupId)
	if riskgroupRes.RecordNotFound() {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Riskgroup not found")
		return
	} else if err = riskgroupRes.Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	var modelSlave model.Slave
	if err = tx.First(&modelSlave, slaveId).Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	updatedSlave := modelSlave
	updatedSlave.RiskGroupID = model.NullIntValue(riskgroupId)

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, &updatedSlave)
	if dbError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		return
	}
	if permissionError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		return
	}

	// Persist to database

	err = tx.Save(&updatedSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
	}

	tx.Commit()
}

func (m *MasterAPI) RiskGroupRemoveSlave(w http.ResponseWriter, r *http.Request) {
	riskgroupIdStr := mux.Vars(r)["riskgroupId"]
	riskgroupId, err := strconv.ParseInt(riskgroupIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	slaveIdStr := mux.Vars(r)["slaveId"]
	slaveId, err := strconv.ParseInt(slaveIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if riskgroupId == 0 || slaveId == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Id may not be 0")
		return
	}

	tx := m.DB.Begin()

	var riskgroup model.RiskGroup
	riskgroupRes := tx.First(&riskgroup, riskgroupId)
	if riskgroupRes.RecordNotFound() {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Riskgroup not found")
		return
	} else if err = riskgroupRes.Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	var modelSlave model.Slave
	if err = tx.First(&modelSlave, slaveId).Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	if !modelSlave.RiskGroupID.Valid || modelSlave.RiskGroupID.Int64 != riskgroupId {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Slave not found in this riskgroup. (Slave is in other riskgroup)")
		return
	}

	updatedSlave := modelSlave
	updatedSlave.RiskGroupID = model.NullInt()

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, &updatedSlave)
	if dbError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		return
	}
	if permissionError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		return
	}

	// Persist to database

	tx.Model(&modelSlave).Update("RiskGroupID", model.NullInt())

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()
}
<commit_msg>UPD: masterapi: use refactored DB error check method in riskGroups endpoints<commit_after>package masterapi

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"net/http"
	"strconv"
)

type RiskGroup struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

func (m *MasterAPI) RiskGroupIndex(w http.ResponseWriter, r *http.Request) {
	tx := m.DB.Begin()
	defer tx.Rollback()

	var riskGroups []*model.RiskGroup
	err := tx.Order("id", false).Find(&riskGroups).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	out := make([]*RiskGroup, len(riskGroups))
	for i, v := range riskGroups {
		out[i] = ProjectModelRiskGroupToRiskGroup(v)
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) RiskGroupById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	if id == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "id may not be 0")
		return
	}

	tx := m.DB.Begin()
	defer tx.Rollback()

	var riskgroup model.RiskGroup
	res := tx.First(&riskgroup, id)

	if res.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err = res.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	json.NewEncoder(w).Encode(ProjectModelRiskGroupToRiskGroup(&riskgroup))
	return
}

func (m *MasterAPI) RiskGroupPut(w http.ResponseWriter, r *http.Request) {
	var postRiskGroup RiskGroup
	err := json.NewDecoder(r.Body).Decode(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postRiskGroup.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not specify the risk group ID in PUT request")
		return
	}

	modelRiskGroup, err := ProjectRiskGroupToModelRiskGroup(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Persist to database

	tx := m.DB.Begin()

	err = tx.Create(&modelRiskGroup).Error

	//Check db specific errors
	if model.IsIntegrityConstraintViolation(err) {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	} else if err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

	// Return created risk group

	json.NewEncoder(w).Encode(ProjectModelRiskGroupToRiskGroup(modelRiskGroup))

	return
}

func (m *MasterAPI) RiskGroupUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	var postRiskGroup RiskGroup
	err = json.NewDecoder(r.Body).Decode(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postRiskGroup.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	// Check if risk group with id exists

	tx := m.DB.Begin()

	var modelRiskGroup model.RiskGroup
	findRes := tx.First(&modelRiskGroup, id)
	if findRes.RecordNotFound() {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		return
	} else if err = findRes.Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	// Allow update

	save, err := ProjectRiskGroupToModelRiskGroup(&postRiskGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Persist to database

	err = tx.Save(&save).Error

	// Check db specific errors
	if model.IsIntegrityConstraintViolation(err) {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) RiskGroupDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	tx := m.DB.Begin()

	// Can only delete risk groups without slaves
	var currentRiskGroup model.RiskGroup
	findRes := tx.First(&currentRiskGroup, id)
	if findRes.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	} else if err = findRes.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		tx.Rollback()
		fmt.Fprint(w, err.Error())
		return
	}

	if err = tx.First(&currentRiskGroup, id).Related(&currentRiskGroup.Slaves, "Slaves").Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		tx.Rollback()
		fmt.Fprint(w, err.Error())
		return
	}

	if len(currentRiskGroup.Slaves) != 0 {
		w.WriteHeader(http.StatusForbidden)
		tx.Rollback()
		fmt.Fprintf(w, "riskgroup with id %d has assigned slaves", currentRiskGroup.ID)
		return
	}

	// Allow delete

	s := tx.Delete(&model.RiskGroup{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}
	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) RiskGroupGetSlaves(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["riskgroupId"]

	id := sql.NullInt64{}

	// Check if risk group exists
	// Special case: id == 0 => Get unassigned slaves
	if idStr != "null" {
		idInt64, err := strconv.ParseInt(idStr, 10, 0)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		id.Valid = true
		id.Int64 = idInt64
	}
	// else id is NULL

	tx := m.DB.Begin()
	defer tx.Rollback()

	// Check if risk group exists
	// Special case: id == 0 => Get unassigned slaves
	if id.Valid {
		var riskgroup model.RiskGroup
		riskgroupRes := tx.First(&riskgroup, id)
		if riskgroupRes.RecordNotFound() {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "Riskgroup not found")
			return
		} else if err := riskgroupRes.Error; err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, err.Error())
			return
		}
	}

	var slaves []*model.Slave
	var err error
	if id.Valid { // gorm/gosql does not query for IS NULL automatically
		err = tx.Where("risk_group_id = ?", id).Find(&slaves).Error
	} else {
		err = tx.Where("risk_group_id IS NULL").Find(&slaves).Error
	}
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}

	out := make([]*Slave, len(slaves))
	for i, v := range slaves {
		out[i], err = ProjectModelSlaveToSlave(tx, v)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err.Error())
			return
		}
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) RiskGroupAssignSlave(w http.ResponseWriter, r *http.Request) {
	riskgroupIdStr := mux.Vars(r)["riskgroupId"]
	riskgroupId, err := strconv.ParseInt(riskgroupIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	slaveIdStr := mux.Vars(r)["slaveId"]
	slaveId, err := strconv.ParseInt(slaveIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if riskgroupId == 0 || slaveId == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Id may not be 0")
		return
	}

	tx := m.DB.Begin()

	var riskgroup model.RiskGroup
	riskgroupRes := tx.First(&riskgroup, riskgroupId)
	if riskgroupRes.RecordNotFound() {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Riskgroup not found")
		return
	} else if err = riskgroupRes.Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	var modelSlave model.Slave
	if err = tx.First(&modelSlave, slaveId).Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	updatedSlave := modelSlave
	updatedSlave.RiskGroupID = model.NullIntValue(riskgroupId)

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, &updatedSlave)
	if dbError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		return
	}
	if permissionError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		return
	}

	// Persist to database

	err = tx.Save(&updatedSlave).Error

	//Check db specific errors
	if model.IsIntegrityConstraintViolation(err) {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
	}

	tx.Commit()
}

func (m *MasterAPI) RiskGroupRemoveSlave(w http.ResponseWriter, r *http.Request) {
	riskgroupIdStr := mux.Vars(r)["riskgroupId"]
	riskgroupId, err := strconv.ParseInt(riskgroupIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	slaveIdStr := mux.Vars(r)["slaveId"]
	slaveId, err := strconv.ParseInt(slaveIdStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if riskgroupId == 0 || slaveId == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Id may not be 0")
		return
	}

	tx := m.DB.Begin()

	var riskgroup model.RiskGroup
	riskgroupRes := tx.First(&riskgroup, riskgroupId)
	if riskgroupRes.RecordNotFound() {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Riskgroup not found")
		return
	} else if err = riskgroupRes.Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	var modelSlave model.Slave
	if err = tx.First(&modelSlave, slaveId).Error; err != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	if !modelSlave.RiskGroupID.Valid || modelSlave.RiskGroupID.Int64 != riskgroupId {
		tx.Rollback()
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Slave not found in this riskgroup. (Slave is in other riskgroup)")
		return
	}

	updatedSlave := modelSlave
	updatedSlave.RiskGroupID = model.NullInt()

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, &updatedSlave)
	if dbError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		return
	}
	if permissionError != nil {
		tx.Rollback()
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		return
	}

	// Persist to database

	tx.Model(&modelSlave).Update("RiskGroupID", model.NullInt())

	//Check db specific errors
	if model.IsIntegrityConstraintViolation(err) {
		tx.Rollback()
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()
}
<|endoftext|>


<commit_before>// Copyright 2017 HenryLee. All Rights Reserved.
//
// 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 socket

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net/url"
	"sync"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/teleport/codec"
	"github.com/henrylee2cn/teleport/utils"
	"github.com/henrylee2cn/teleport/xfer"
)

type (
	// Packet a socket data packet.
	Packet struct {
		// packet sequence
		seq uint64
		// packet type, such as PULL, PUSH, REPLY
		ptype byte
		// URI string
		uri string
		// URI object
		uriObject *url.URL
		// metadata
		meta *utils.Args
		// body codec type
		bodyCodec byte
		// body object
		body interface{}
		// newBodyFunc creates a new body by packet type and URI.
		// Note:
		//  only for writing packet;
		//  should be nil when reading packet.
		newBodyFunc NewBodyFunc
		// XferPipe transfer filter pipe, handlers from outer-most to inner-most.
		// Note: the length can not be bigger than 255!
		xferPipe *xfer.XferPipe
		// packet size
		size uint32
		// ctx is the packet handling context,
		// carries a deadline, a cancelation signal,
		// and other values across API boundaries.
		ctx context.Context
		// stack
		next *Packet
	}
	// Header packet header interface
	Header interface {
		// Ptype returns the packet sequence
		Seq() uint64
		// SetSeq sets the packet sequence
		SetSeq(uint64)
		// Ptype returns the packet type, such as PULL, PUSH, REPLY
		Ptype() byte
		// Ptype sets the packet type
		SetPtype(byte)
		// Uri returns the URI string
		Uri() string
		// UriObject returns the URI object
		UriObject() *url.URL
		// SetUri sets the packet URI
		SetUri(string)
		// SetUriObject sets the packet URI
		SetUriObject(uriObject *url.URL)
		// Meta returns the metadata
		Meta() *utils.Args
	}
	// Body packet body interface
	Body interface {
		// BodyCodec returns the body codec type id
		BodyCodec() byte
		// SetBodyCodec sets the body codec type id
		SetBodyCodec(bodyCodec byte)
		// Body returns the body object
		Body() interface{}
		// SetBody sets the body object
		SetBody(body interface{})
		// SetNewBody resets the function of geting body.
		SetNewBody(newBodyFunc NewBodyFunc)
		// MarshalBody returns the encoding of body.
		// Note: when the body is a stream of bytes, no marshalling is done.
		MarshalBody() ([]byte, error)
		// UnmarshalBody unmarshals the encoded data to the body.
		// Note:
		//  seq, ptype, uri must be setted already;
		//  if body=nil, try to use newBodyFunc to create a new one;
		//  when the body is a stream of bytes, no unmarshalling is done.
		UnmarshalBody(bodyBytes []byte) error
	}

	// NewBodyFunc creates a new body by header.
	NewBodyFunc func(Header) interface{}
)

var (
	_ Header = new(Packet)
	_ Body   = new(Packet)
)

var packetStack = new(struct {
	freePacket *Packet
	mu         sync.Mutex
})

// GetPacket gets a *Packet form packet stack.
// Note:
//  newBodyFunc is only for reading form connection;
//  settings are only for writing to connection.
func GetPacket(settings ...PacketSetting) *Packet {
	packetStack.mu.Lock()
	p := packetStack.freePacket
	if p == nil {
		p = NewPacket(settings...)
	} else {
		packetStack.freePacket = p.next
		p.doSetting(settings...)
	}
	packetStack.mu.Unlock()
	return p
}

// PutPacket puts a *Packet to packet stack.
func PutPacket(p *Packet) {
	packetStack.mu.Lock()
	p.Reset()
	p.next = packetStack.freePacket
	packetStack.freePacket = p
	packetStack.mu.Unlock()
}

// NewPacket creates a new *Packet.
// Note:
//  NewBody is only for reading form connection;
//  settings are only for writing to connection.
func NewPacket(settings ...PacketSetting) *Packet {
	var p = &Packet{
		meta:     new(utils.Args),
		xferPipe: new(xfer.XferPipe),
	}
	p.doSetting(settings...)
	return p
}

// Reset resets itself.
// Note:
//  newBodyFunc is only for reading form connection;
//  settings are only for writing to connection.
func (p *Packet) Reset(settings ...PacketSetting) {
	p.next = nil
	p.body = nil
	p.meta.Reset()
	p.xferPipe.Reset()
	p.newBodyFunc = nil
	p.seq = 0
	p.ptype = 0
	p.uri = ""
	p.uriObject = nil
	p.size = 0
	p.ctx = nil
	p.bodyCodec = codec.NilCodecId
	p.doSetting(settings...)
}

func (p *Packet) doSetting(settings ...PacketSetting) {
	for _, fn := range settings {
		if fn != nil {
			fn(p)
		}
	}
}

// Context returns the packet handling context.
func (p *Packet) Context() context.Context {
	if p.ctx == nil {
		return context.Background()
	}
	return p.ctx
}

// Seq returns the packet sequence
func (p *Packet) Seq() uint64 {
	return p.seq
}

// SetSeq sets the packet sequence
func (p *Packet) SetSeq(seq uint64) {
	p.seq = seq
}

// Ptype returns the packet type, such as PULL, PUSH, REPLY
func (p *Packet) Ptype() byte {
	return p.ptype
}

// SetPtype sets the packet type
func (p *Packet) SetPtype(ptype byte) {
	p.ptype = ptype
}

// Uri returns the URI string
func (p *Packet) Uri() string {
	if p.uriObject != nil {
		return p.uriObject.String()
	}
	return p.uri
}

// UriObject returns the URI object
func (p *Packet) UriObject() *url.URL {
	if p.uriObject == nil {
		p.uriObject, _ = url.Parse(p.uri)
		if p.uriObject == nil {
			p.uriObject = new(url.URL)
		}
		p.uri = ""
	}
	return p.uriObject
}

// SetUri sets the packet URI
func (p *Packet) SetUri(uri string) {
	p.uri = uri
	p.uriObject = nil
}

// SetUriObject sets the packet URI
func (p *Packet) SetUriObject(uriObject *url.URL) {
	p.uriObject = uriObject
	p.uri = ""
}

// Meta returns the metadata.
// When the package is reset, it will be reset.
func (p *Packet) Meta() *utils.Args {
	return p.meta
}

// BodyCodec returns the body codec type id
func (p *Packet) BodyCodec() byte {
	return p.bodyCodec
}

// SetBodyCodec sets the body codec type id
func (p *Packet) SetBodyCodec(bodyCodec byte) {
	p.bodyCodec = bodyCodec
}

// Body returns the body object
func (p *Packet) Body() interface{} {
	return p.body
}

// SetBody sets the body object
func (p *Packet) SetBody(body interface{}) {
	p.body = body
}

// SetNewBody resets the function of geting body.
func (p *Packet) SetNewBody(newBodyFunc NewBodyFunc) {
	p.newBodyFunc = newBodyFunc
}

// MarshalBody returns the encoding of body.
// Note: when the body is a stream of bytes, no marshalling is done.
func (p *Packet) MarshalBody() ([]byte, error) {
	switch body := p.body.(type) {
	default:
		c, err := codec.Get(p.bodyCodec)
		if err != nil {
			return []byte{}, err
		}
		return c.Marshal(body)
	case nil:
		return []byte{}, nil
	case *[]byte:
		if body == nil {
			return []byte{}, nil
		}
		return *body, nil
	case []byte:
		return body, nil
	}
}

// UnmarshalBody unmarshals the encoded data to the body.
// Note:
//  seq, ptype, uri must be setted already;
//  if body=nil, try to use newBodyFunc to create a new one;
//  when the body is a stream of bytes, no unmarshalling is done.
func (p *Packet) UnmarshalBody(bodyBytes []byte) error {
	if p.body == nil && p.newBodyFunc != nil {
		p.body = p.newBodyFunc(p)
	}
	if len(bodyBytes) == 0 {
		return nil
	}
	switch body := p.body.(type) {
	default:
		c, err := codec.Get(p.bodyCodec)
		if err != nil {
			return err
		}
		return c.Unmarshal(bodyBytes, p.body)
	case nil:
		return nil
	case *[]byte:
		if body != nil {
			*body = make([]byte, len(bodyBytes))
			copy(*body, bodyBytes)
		}
		return nil
	}
}

// XferPipe returns transfer filter pipe, handlers from outer-most to inner-most.
// Note: the length can not be bigger than 255!
func (p *Packet) XferPipe() *xfer.XferPipe {
	return p.xferPipe
}

// Size returns the size of packet.
func (p *Packet) Size() uint32 {
	return p.size
}

// SetSize sets the size of packet.
// If the size is too big, returns error.
func (p *Packet) SetSize(size uint32) error {
	err := checkPacketSize(size)
	if err != nil {
		return err
	}
	p.size = size
	return nil
}

const packetFormat = `
{
  "seq": %d,
  "ptype": %d,
  "uri": %q,
  "meta": %q,
  "body_codec": %d,
  "body": %s,
  "xfer_pipe": %s,
  "size": %d
}`

// String returns printing text.
func (p *Packet) String() string {
	var xferPipeIds = make([]int, p.xferPipe.Len())
	for i, id := range p.xferPipe.Ids() {
		xferPipeIds[i] = int(id)
	}
	idsBytes, _ := json.Marshal(xferPipeIds)
	b, _ := json.Marshal(p.body)
	dst := bytes.NewBuffer(make([]byte, 0, len(b)*2))
	json.Indent(dst, goutil.StringToBytes(
		fmt.Sprintf(packetFormat,
			p.seq,
			p.ptype,
			p.uri,
			p.meta.QueryString(),
			p.bodyCodec,
			b,
			idsBytes,
			p.size,
		),
	), "", "  ")
	return goutil.BytesToString(dst.Bytes())
}

// PacketSetting sets Header field.
type PacketSetting func(*Packet)

// WithContext sets the packet handling context.
func WithContext(ctx context.Context) PacketSetting {
	return func(p *Packet) {
		p.ctx = ctx
	}
}

// WithSeq sets the packet sequence.
func WithSeq(seq uint64) PacketSetting {
	return func(p *Packet) {
		p.seq = seq
	}
}

// WithPtype sets the packet type.
func WithPtype(ptype byte) PacketSetting {
	return func(p *Packet) {
		p.ptype = ptype
	}
}

// WithUri sets the packet URI string.
func WithUri(uri string) PacketSetting {
	return func(p *Packet) {
		p.SetUri(uri)
	}
}

// WithUriObject sets the packet URI object.
func WithUriObject(uriObject *url.URL) PacketSetting {
	return func(p *Packet) {
		p.SetUriObject(uriObject)
	}
}

// WithQuery sets the packet URI query parameter.
func WithQuery(key, value string) PacketSetting {
	return func(p *Packet) {
		u := p.UriObject()
		v := u.Query()
		v.Add(key, value)
		u.RawQuery = v.Encode()
	}
}

// WithAddMeta adds 'key=value' metadata argument.
// Multiple values for the same key may be added.
func WithAddMeta(key, value string) PacketSetting {
	return func(p *Packet) {
		p.meta.Add(key, value)
	}
}

// WithSetMeta sets 'key=value' metadata argument.
func WithSetMeta(key, value string) PacketSetting {
	return func(p *Packet) {
		p.meta.Set(key, value)
	}
}

// WithBodyCodec sets the body codec.
func WithBodyCodec(bodyCodec byte) PacketSetting {
	return func(p *Packet) {
		p.bodyCodec = bodyCodec
	}
}

// WithBody sets the body object.
func WithBody(body interface{}) PacketSetting {
	return func(p *Packet) {
		p.body = body
	}
}

// WithNewBody resets the function of geting body.
func WithNewBody(newBodyFunc NewBodyFunc) PacketSetting {
	return func(p *Packet) {
		p.newBodyFunc = newBodyFunc
	}
}

// WithXferPipe sets transfer filter pipe.
func WithXferPipe(filterId ...byte) PacketSetting {
	return func(p *Packet) {
		p.xferPipe.Append(filterId...)
	}
}

var (
	packetSizeLimit uint32 = math.MaxUint32
	// ErrExceedPacketSizeLimit error
	ErrExceedPacketSizeLimit = errors.New("Size of package exceeds limit.")
)

// PacketSizeLimit gets the packet size upper limit of reading.
func PacketSizeLimit() uint32 {
	return packetSizeLimit
}

// SetPacketSizeLimit sets max packet size.
// If maxSize<=0, set it to max uint32.
func SetPacketSizeLimit(maxPacketSize uint32) {
	if maxPacketSize <= 0 {
		packetSizeLimit = math.MaxUint32
	} else {
		packetSizeLimit = maxPacketSize
	}
}

func checkPacketSize(packetSize uint32) error {
	if packetSize > packetSizeLimit {
		return ErrExceedPacketSizeLimit
	}
	return nil
}
<commit_msg>socket: update PacketSetting’s comment<commit_after>// Copyright 2017 HenryLee. All Rights Reserved.
//
// 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 socket

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net/url"
	"sync"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/teleport/codec"
	"github.com/henrylee2cn/teleport/utils"
	"github.com/henrylee2cn/teleport/xfer"
)

type (
	// Packet a socket data packet.
	Packet struct {
		// packet sequence
		seq uint64
		// packet type, such as PULL, PUSH, REPLY
		ptype byte
		// URI string
		uri string
		// URI object
		uriObject *url.URL
		// metadata
		meta *utils.Args
		// body codec type
		bodyCodec byte
		// body object
		body interface{}
		// newBodyFunc creates a new body by packet type and URI.
		// Note:
		//  only for writing packet;
		//  should be nil when reading packet.
		newBodyFunc NewBodyFunc
		// XferPipe transfer filter pipe, handlers from outer-most to inner-most.
		// Note: the length can not be bigger than 255!
		xferPipe *xfer.XferPipe
		// packet size
		size uint32
		// ctx is the packet handling context,
		// carries a deadline, a cancelation signal,
		// and other values across API boundaries.
		ctx context.Context
		// stack
		next *Packet
	}
	// Header packet header interface
	Header interface {
		// Ptype returns the packet sequence
		Seq() uint64
		// SetSeq sets the packet sequence
		SetSeq(uint64)
		// Ptype returns the packet type, such as PULL, PUSH, REPLY
		Ptype() byte
		// Ptype sets the packet type
		SetPtype(byte)
		// Uri returns the URI string
		Uri() string
		// UriObject returns the URI object
		UriObject() *url.URL
		// SetUri sets the packet URI
		SetUri(string)
		// SetUriObject sets the packet URI
		SetUriObject(uriObject *url.URL)
		// Meta returns the metadata
		Meta() *utils.Args
	}
	// Body packet body interface
	Body interface {
		// BodyCodec returns the body codec type id
		BodyCodec() byte
		// SetBodyCodec sets the body codec type id
		SetBodyCodec(bodyCodec byte)
		// Body returns the body object
		Body() interface{}
		// SetBody sets the body object
		SetBody(body interface{})
		// SetNewBody resets the function of geting body.
		SetNewBody(newBodyFunc NewBodyFunc)
		// MarshalBody returns the encoding of body.
		// Note: when the body is a stream of bytes, no marshalling is done.
		MarshalBody() ([]byte, error)
		// UnmarshalBody unmarshals the encoded data to the body.
		// Note:
		//  seq, ptype, uri must be setted already;
		//  if body=nil, try to use newBodyFunc to create a new one;
		//  when the body is a stream of bytes, no unmarshalling is done.
		UnmarshalBody(bodyBytes []byte) error
	}

	// NewBodyFunc creates a new body by header.
	NewBodyFunc func(Header) interface{}
)

var (
	_ Header = new(Packet)
	_ Body   = new(Packet)
)

var packetStack = new(struct {
	freePacket *Packet
	mu         sync.Mutex
})

// GetPacket gets a *Packet form packet stack.
// Note:
//  newBodyFunc is only for reading form connection;
//  settings are only for writing to connection.
func GetPacket(settings ...PacketSetting) *Packet {
	packetStack.mu.Lock()
	p := packetStack.freePacket
	if p == nil {
		p = NewPacket(settings...)
	} else {
		packetStack.freePacket = p.next
		p.doSetting(settings...)
	}
	packetStack.mu.Unlock()
	return p
}

// PutPacket puts a *Packet to packet stack.
func PutPacket(p *Packet) {
	packetStack.mu.Lock()
	p.Reset()
	p.next = packetStack.freePacket
	packetStack.freePacket = p
	packetStack.mu.Unlock()
}

// NewPacket creates a new *Packet.
// Note:
//  NewBody is only for reading form connection;
//  settings are only for writing to connection.
func NewPacket(settings ...PacketSetting) *Packet {
	var p = &Packet{
		meta:     new(utils.Args),
		xferPipe: new(xfer.XferPipe),
	}
	p.doSetting(settings...)
	return p
}

// Reset resets itself.
// Note:
//  newBodyFunc is only for reading form connection;
//  settings are only for writing to connection.
func (p *Packet) Reset(settings ...PacketSetting) {
	p.next = nil
	p.body = nil
	p.meta.Reset()
	p.xferPipe.Reset()
	p.newBodyFunc = nil
	p.seq = 0
	p.ptype = 0
	p.uri = ""
	p.uriObject = nil
	p.size = 0
	p.ctx = nil
	p.bodyCodec = codec.NilCodecId
	p.doSetting(settings...)
}

func (p *Packet) doSetting(settings ...PacketSetting) {
	for _, fn := range settings {
		if fn != nil {
			fn(p)
		}
	}
}

// Context returns the packet handling context.
func (p *Packet) Context() context.Context {
	if p.ctx == nil {
		return context.Background()
	}
	return p.ctx
}

// Seq returns the packet sequence
func (p *Packet) Seq() uint64 {
	return p.seq
}

// SetSeq sets the packet sequence
func (p *Packet) SetSeq(seq uint64) {
	p.seq = seq
}

// Ptype returns the packet type, such as PULL, PUSH, REPLY
func (p *Packet) Ptype() byte {
	return p.ptype
}

// SetPtype sets the packet type
func (p *Packet) SetPtype(ptype byte) {
	p.ptype = ptype
}

// Uri returns the URI string
func (p *Packet) Uri() string {
	if p.uriObject != nil {
		return p.uriObject.String()
	}
	return p.uri
}

// UriObject returns the URI object
func (p *Packet) UriObject() *url.URL {
	if p.uriObject == nil {
		p.uriObject, _ = url.Parse(p.uri)
		if p.uriObject == nil {
			p.uriObject = new(url.URL)
		}
		p.uri = ""
	}
	return p.uriObject
}

// SetUri sets the packet URI
func (p *Packet) SetUri(uri string) {
	p.uri = uri
	p.uriObject = nil
}

// SetUriObject sets the packet URI
func (p *Packet) SetUriObject(uriObject *url.URL) {
	p.uriObject = uriObject
	p.uri = ""
}

// Meta returns the metadata.
// When the package is reset, it will be reset.
func (p *Packet) Meta() *utils.Args {
	return p.meta
}

// BodyCodec returns the body codec type id
func (p *Packet) BodyCodec() byte {
	return p.bodyCodec
}

// SetBodyCodec sets the body codec type id
func (p *Packet) SetBodyCodec(bodyCodec byte) {
	p.bodyCodec = bodyCodec
}

// Body returns the body object
func (p *Packet) Body() interface{} {
	return p.body
}

// SetBody sets the body object
func (p *Packet) SetBody(body interface{}) {
	p.body = body
}

// SetNewBody resets the function of geting body.
func (p *Packet) SetNewBody(newBodyFunc NewBodyFunc) {
	p.newBodyFunc = newBodyFunc
}

// MarshalBody returns the encoding of body.
// Note: when the body is a stream of bytes, no marshalling is done.
func (p *Packet) MarshalBody() ([]byte, error) {
	switch body := p.body.(type) {
	default:
		c, err := codec.Get(p.bodyCodec)
		if err != nil {
			return []byte{}, err
		}
		return c.Marshal(body)
	case nil:
		return []byte{}, nil
	case *[]byte:
		if body == nil {
			return []byte{}, nil
		}
		return *body, nil
	case []byte:
		return body, nil
	}
}

// UnmarshalBody unmarshals the encoded data to the body.
// Note:
//  seq, ptype, uri must be setted already;
//  if body=nil, try to use newBodyFunc to create a new one;
//  when the body is a stream of bytes, no unmarshalling is done.
func (p *Packet) UnmarshalBody(bodyBytes []byte) error {
	if p.body == nil && p.newBodyFunc != nil {
		p.body = p.newBodyFunc(p)
	}
	if len(bodyBytes) == 0 {
		return nil
	}
	switch body := p.body.(type) {
	default:
		c, err := codec.Get(p.bodyCodec)
		if err != nil {
			return err
		}
		return c.Unmarshal(bodyBytes, p.body)
	case nil:
		return nil
	case *[]byte:
		if body != nil {
			*body = make([]byte, len(bodyBytes))
			copy(*body, bodyBytes)
		}
		return nil
	}
}

// XferPipe returns transfer filter pipe, handlers from outer-most to inner-most.
// Note: the length can not be bigger than 255!
func (p *Packet) XferPipe() *xfer.XferPipe {
	return p.xferPipe
}

// Size returns the size of packet.
func (p *Packet) Size() uint32 {
	return p.size
}

// SetSize sets the size of packet.
// If the size is too big, returns error.
func (p *Packet) SetSize(size uint32) error {
	err := checkPacketSize(size)
	if err != nil {
		return err
	}
	p.size = size
	return nil
}

const packetFormat = `
{
  "seq": %d,
  "ptype": %d,
  "uri": %q,
  "meta": %q,
  "body_codec": %d,
  "body": %s,
  "xfer_pipe": %s,
  "size": %d
}`

// String returns printing text.
func (p *Packet) String() string {
	var xferPipeIds = make([]int, p.xferPipe.Len())
	for i, id := range p.xferPipe.Ids() {
		xferPipeIds[i] = int(id)
	}
	idsBytes, _ := json.Marshal(xferPipeIds)
	b, _ := json.Marshal(p.body)
	dst := bytes.NewBuffer(make([]byte, 0, len(b)*2))
	json.Indent(dst, goutil.StringToBytes(
		fmt.Sprintf(packetFormat,
			p.seq,
			p.ptype,
			p.uri,
			p.meta.QueryString(),
			p.bodyCodec,
			b,
			idsBytes,
			p.size,
		),
	), "", "  ")
	return goutil.BytesToString(dst.Bytes())
}

// PacketSetting is a pipe function type for setting socket package.
type PacketSetting func(*Packet)

// WithContext sets the packet handling context.
func WithContext(ctx context.Context) PacketSetting {
	return func(p *Packet) {
		p.ctx = ctx
	}
}

// WithSeq sets the packet sequence.
func WithSeq(seq uint64) PacketSetting {
	return func(p *Packet) {
		p.seq = seq
	}
}

// WithPtype sets the packet type.
func WithPtype(ptype byte) PacketSetting {
	return func(p *Packet) {
		p.ptype = ptype
	}
}

// WithUri sets the packet URI string.
func WithUri(uri string) PacketSetting {
	return func(p *Packet) {
		p.SetUri(uri)
	}
}

// WithUriObject sets the packet URI object.
func WithUriObject(uriObject *url.URL) PacketSetting {
	return func(p *Packet) {
		p.SetUriObject(uriObject)
	}
}

// WithQuery sets the packet URI query parameter.
func WithQuery(key, value string) PacketSetting {
	return func(p *Packet) {
		u := p.UriObject()
		v := u.Query()
		v.Add(key, value)
		u.RawQuery = v.Encode()
	}
}

// WithAddMeta adds 'key=value' metadata argument.
// Multiple values for the same key may be added.
func WithAddMeta(key, value string) PacketSetting {
	return func(p *Packet) {
		p.meta.Add(key, value)
	}
}

// WithSetMeta sets 'key=value' metadata argument.
func WithSetMeta(key, value string) PacketSetting {
	return func(p *Packet) {
		p.meta.Set(key, value)
	}
}

// WithBodyCodec sets the body codec.
func WithBodyCodec(bodyCodec byte) PacketSetting {
	return func(p *Packet) {
		p.bodyCodec = bodyCodec
	}
}

// WithBody sets the body object.
func WithBody(body interface{}) PacketSetting {
	return func(p *Packet) {
		p.body = body
	}
}

// WithNewBody resets the function of geting body.
func WithNewBody(newBodyFunc NewBodyFunc) PacketSetting {
	return func(p *Packet) {
		p.newBodyFunc = newBodyFunc
	}
}

// WithXferPipe sets transfer filter pipe.
func WithXferPipe(filterId ...byte) PacketSetting {
	return func(p *Packet) {
		p.xferPipe.Append(filterId...)
	}
}

var (
	packetSizeLimit uint32 = math.MaxUint32
	// ErrExceedPacketSizeLimit error
	ErrExceedPacketSizeLimit = errors.New("Size of package exceeds limit.")
)

// PacketSizeLimit gets the packet size upper limit of reading.
func PacketSizeLimit() uint32 {
	return packetSizeLimit
}

// SetPacketSizeLimit sets max packet size.
// If maxSize<=0, set it to max uint32.
func SetPacketSizeLimit(maxPacketSize uint32) {
	if maxPacketSize <= 0 {
		packetSizeLimit = math.MaxUint32
	} else {
		packetSizeLimit = maxPacketSize
	}
}

func checkPacketSize(packetSize uint32) error {
	if packetSize > packetSizeLimit {
		return ErrExceedPacketSizeLimit
	}
	return nil
}
<|endoftext|>


<commit_before>package betwixt

import (
	"errors"
	"github.com/zubairhamed/canopus"
	"github.com/zubairhamed/go-commons/network"
	"github.com/zubairhamed/go-commons/typeval"
	"time"
	"github.com/zubairhamed/sugoi"
)

type TestDeviceObject struct {
	Model       ObjectDefinition
	currentTime time.Time
	utcOffset   string
	timeZone    string
}

func (o *TestDeviceObject) OnExecute(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	return Changed()
}

func (o *TestDeviceObject) OnCreate(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	return Created()
}

func (o *TestDeviceObject) OnDelete(instanceId int, req Lwm2mRequest) Lwm2mResponse {
	return Deleted()
}

func (o *TestDeviceObject) OnRead(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	if resourceId == -1 {
		// Read Object Instance
	} else {
		// Read Resource Instance
		var val typeval.Value

		// resource := o.Model.GetResource(resourceId)
		switch resourceId {
		case 0:
			val = typeval.String("Open Mobile Alliance")
			break

		case 1:
			val = typeval.String("Lightweight M2M Client")
			break

		case 2:
			val = typeval.String("345000123")
			break

		case 3:
			val = typeval.String("1.0")
			break

		case 6:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{1, 5})
			break

		case 7:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{3800, 5000})
			break

		case 8:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{125, 900})
			break

		case 9:
			val = typeval.Integer(100)
			break

		case 10:
			val = typeval.Integer(15)
			break

		case 11:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{0})
			break

		case 13:
			val = typeval.Time(o.currentTime)
			break

		case 14:
			val = typeval.String(o.utcOffset)
			break

		case 15:
			val = typeval.String(o.timeZone)
			break

		case 16:
			val = typeval.String(string(BINDINGMODE_UDP))
			break

		default:
			break
		}
		return Content(val)
	}
	return NotFound()
}

func (o *TestDeviceObject) OnWrite(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	val := req.GetMessage().Payload

	switch resourceId {
	case 13:
		break

	case 14:
		o.utcOffset = val.String()
		break

	case 15:
		o.timeZone = val.String()
		break

	default:
		return NotFound()
	}
	return Changed()
}

func (o *TestDeviceObject) Reboot() typeval.Value {
	return typeval.Empty()
}

func (o *TestDeviceObject) FactoryReset() typeval.Value {
	return typeval.Empty()
}

func (o *TestDeviceObject) ResetErrorCode() string {
	return ""
}

func NewTestDeviceObject(def ObjectDefinition) *TestDeviceObject {
	return &TestDeviceObject{
		Model:       def,
		currentTime: time.Unix(1367491215, 0),
		utcOffset:   "+02:00",
		timeZone:    "+02:00",
	}
}

type MockServerStatistics struct {
}

func (s *MockServerStatistics) IncrementCoapRequestsCount() {

}

func (s *MockServerStatistics) GetRequestsCount() int {
	return 0
}

func NewMockServer() Server {
	return &MockServer{
		stats:      &MockServerStatistics{},
		httpServer: network.NewDefaultHttpServer("8080"),
	}
}

type MockServer struct {
	stats      ServerStatistics
	httpServer *sugoi.SugoiServer
	coapServer *canopus.CoapServer
}

func (server *MockServer) Start() {

}

func (server *MockServer) UseRegistry(reg Registry) {

}

func (server *MockServer) On(e EventType, fn FnEvent) {

}

func (server *MockServer) GetClients() map[string]RegisteredClient {
	return make(map[string]RegisteredClient)
}

func (server *MockServer) GetStats() ServerStatistics {
	return server.stats
}

func (server *MockServer) GetHttpServer() *sugoi.SugoiServer {
	return server.httpServer
}

func (server *MockServer) GetCoapServer() *canopus.CoapServer {
	return server.coapServer
}

func (server *MockServer) GetClient(id string) RegisteredClient {
	return nil
}

func NewMockRegistry(s ...ObjectSource) Registry {
	reg := &MockRegistry{}

	reg.sources = []ObjectSource{}

	for _, o := range s {
		reg.Register(o)
	}
	return reg
}

type MockRegistry struct {
	sources []ObjectSource
}

func (r *MockRegistry) GetDefinition(t LWM2MObjectType) ObjectDefinition {
	return nil
}

func (m *MockRegistry) Register(s ObjectSource) {
	s.Initialize()
	m.sources = append(m.sources, s)
}

func (r *MockRegistry) GetMandatory() []ObjectDefinition {
	mandatory := []ObjectDefinition{}

	for _, s := range r.sources {
		objs := s.GetObjects()
		for _, o := range objs {
			if o.IsMandatory() {
				mandatory = append(mandatory, o)
			}
		}
	}
	return mandatory
}

func (m *MockRegistry) GetDefinitions() []ObjectDefinition {
	defs := []ObjectDefinition{}

	for _, s := range m.sources {
		if s != nil {
			for _, v := range s.GetObjects() {
				defs = append(defs, v)
			}
		}
	}
	return defs
}

func NewMockObject(t LWM2MObjectType, enabler ObjectEnabler, reg Registry) Object {
	def := reg.GetDefinition(t)

	return &MockObject{
		definition: def,
		typeId:     t,
		enabler:    enabler,
		instances:  make(map[int]bool),
	}
}

type MockObject struct {
	typeId     LWM2MObjectType
	definition ObjectDefinition
	enabler    ObjectEnabler
	instances  map[int]bool
}

func (o *MockObject) AddInstance(int)    {}
func (o *MockObject) RemoveInstance(int) {}
func (o *MockObject) GetInstances() []int {
	return nil
}
func (o *MockObject) GetEnabler() ObjectEnabler {
	return o.enabler
}
func (o *MockObject) GetType() LWM2MObjectType {
	return LWM2MObjectType(0)
}
func (o *MockObject) GetDefinition() ObjectDefinition {
	return nil
}
func (o *MockObject) SetEnabler(ObjectEnabler) {}

func NewMockClient() LWM2MClient {
	return &MockClient{
		enabledObjects: make(map[LWM2MObjectType]Object),
	}
}

type MockClient struct {
	enabledObjects map[LWM2MObjectType]Object
	registry       Registry
}

func (c *MockClient) AddObjectInstance(LWM2MObjectType, int) error {
	return nil
}
func (c *MockClient) Register(string) (string, error) {
	return "", nil
}
func (c *MockClient) UseRegistry(r Registry) {
	c.registry = r
}

func (c *MockClient) EnableObject(t LWM2MObjectType, e ObjectEnabler) error {
	_, ok := c.enabledObjects[t]
	if !ok {
		c.enabledObjects[t] = NewMockObject(t, e, c.GetRegistry())

		return nil
	} else {
		return errors.New("Object already enabled")
	}
}

func (c *MockClient) GetRegistry() Registry {
	return c.registry
}
func (c *MockClient) GetEnabledObjects() map[LWM2MObjectType]Object {
	return c.enabledObjects
}

func (c *MockClient) AddObjectInstances(LWM2MObjectType, ...int) {}
func (c *MockClient) AddResource()                               {}
func (c *MockClient) AddObject()                                 {}
func (c *MockClient) Deregister()                                {}
func (c *MockClient) Update()                                    {}
func (c *MockClient) SetEnabler(LWM2MObjectType, ObjectEnabler)  {}
func (c *MockClient) Start()                                     {}
func (c *MockClient) OnStartup(FnOnStartup)                      {}
func (c *MockClient) OnRead(FnOnRead)                            {}
func (c *MockClient) OnWrite(FnOnWrite)                          {}
func (c *MockClient) OnExecute(FnOnExecute)                      {}
func (c *MockClient) OnRegistered(FnOnRegistered)                {}
func (c *MockClient) OnDeregistered(FnOnDeregistered)            {}
func (c *MockClient) OnError(FnOnError)                          {}
<commit_msg>test fixes<commit_after>package betwixt

import (
	"errors"
	"github.com/zubairhamed/canopus"
	"github.com/zubairhamed/go-commons/typeval"
	"time"
	"github.com/zubairhamed/sugoi"
)

type TestDeviceObject struct {
	Model       ObjectDefinition
	currentTime time.Time
	utcOffset   string
	timeZone    string
}

func (o *TestDeviceObject) OnExecute(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	return Changed()
}

func (o *TestDeviceObject) OnCreate(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	return Created()
}

func (o *TestDeviceObject) OnDelete(instanceId int, req Lwm2mRequest) Lwm2mResponse {
	return Deleted()
}

func (o *TestDeviceObject) OnRead(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	if resourceId == -1 {
		// Read Object Instance
	} else {
		// Read Resource Instance
		var val typeval.Value

		// resource := o.Model.GetResource(resourceId)
		switch resourceId {
		case 0:
			val = typeval.String("Open Mobile Alliance")
			break

		case 1:
			val = typeval.String("Lightweight M2M Client")
			break

		case 2:
			val = typeval.String("345000123")
			break

		case 3:
			val = typeval.String("1.0")
			break

		case 6:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{1, 5})
			break

		case 7:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{3800, 5000})
			break

		case 8:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{125, 900})
			break

		case 9:
			val = typeval.Integer(100)
			break

		case 10:
			val = typeval.Integer(15)
			break

		case 11:
			// val, _ = values.TlvPayloadFromIntResource(resource, []int{0})
			break

		case 13:
			val = typeval.Time(o.currentTime)
			break

		case 14:
			val = typeval.String(o.utcOffset)
			break

		case 15:
			val = typeval.String(o.timeZone)
			break

		case 16:
			val = typeval.String(string(BINDINGMODE_UDP))
			break

		default:
			break
		}
		return Content(val)
	}
	return NotFound()
}

func (o *TestDeviceObject) OnWrite(instanceId int, resourceId int, req Lwm2mRequest) Lwm2mResponse {
	val := req.GetMessage().Payload

	switch resourceId {
	case 13:
		break

	case 14:
		o.utcOffset = val.String()
		break

	case 15:
		o.timeZone = val.String()
		break

	default:
		return NotFound()
	}
	return Changed()
}

func (o *TestDeviceObject) Reboot() typeval.Value {
	return typeval.Empty()
}

func (o *TestDeviceObject) FactoryReset() typeval.Value {
	return typeval.Empty()
}

func (o *TestDeviceObject) ResetErrorCode() string {
	return ""
}

func NewTestDeviceObject(def ObjectDefinition) *TestDeviceObject {
	return &TestDeviceObject{
		Model:       def,
		currentTime: time.Unix(1367491215, 0),
		utcOffset:   "+02:00",
		timeZone:    "+02:00",
	}
}

type MockServerStatistics struct {
}

func (s *MockServerStatistics) IncrementCoapRequestsCount() {

}

func (s *MockServerStatistics) GetRequestsCount() int {
	return 0
}

func NewMockServer() Server {
	return &MockServer{
		stats:      &MockServerStatistics{},
		httpServer: sugoi.NewSugoi("8080"),
	}
}

type MockServer struct {
	stats      ServerStatistics
	httpServer *sugoi.SugoiServer
	coapServer *canopus.CoapServer
}

func (server *MockServer) Start() {

}

func (server *MockServer) UseRegistry(reg Registry) {

}

func (server *MockServer) On(e EventType, fn FnEvent) {

}

func (server *MockServer) GetClients() map[string]RegisteredClient {
	return make(map[string]RegisteredClient)
}

func (server *MockServer) GetStats() ServerStatistics {
	return server.stats
}

func (server *MockServer) GetHttpServer() *sugoi.SugoiServer {
	return server.httpServer
}

func (server *MockServer) GetCoapServer() *canopus.CoapServer {
	return server.coapServer
}

func (server *MockServer) GetClient(id string) RegisteredClient {
	return nil
}

func NewMockRegistry(s ...ObjectSource) Registry {
	reg := &MockRegistry{}

	reg.sources = []ObjectSource{}

	for _, o := range s {
		reg.Register(o)
	}
	return reg
}

type MockRegistry struct {
	sources []ObjectSource
}

func (r *MockRegistry) GetDefinition(t LWM2MObjectType) ObjectDefinition {
	return nil
}

func (m *MockRegistry) Register(s ObjectSource) {
	s.Initialize()
	m.sources = append(m.sources, s)
}

func (r *MockRegistry) GetMandatory() []ObjectDefinition {
	mandatory := []ObjectDefinition{}

	for _, s := range r.sources {
		objs := s.GetObjects()
		for _, o := range objs {
			if o.IsMandatory() {
				mandatory = append(mandatory, o)
			}
		}
	}
	return mandatory
}

func (m *MockRegistry) GetDefinitions() []ObjectDefinition {
	defs := []ObjectDefinition{}

	for _, s := range m.sources {
		if s != nil {
			for _, v := range s.GetObjects() {
				defs = append(defs, v)
			}
		}
	}
	return defs
}

func NewMockObject(t LWM2MObjectType, enabler ObjectEnabler, reg Registry) Object {
	def := reg.GetDefinition(t)

	return &MockObject{
		definition: def,
		typeId:     t,
		enabler:    enabler,
		instances:  make(map[int]bool),
	}
}

type MockObject struct {
	typeId     LWM2MObjectType
	definition ObjectDefinition
	enabler    ObjectEnabler
	instances  map[int]bool
}

func (o *MockObject) AddInstance(int)    {}
func (o *MockObject) RemoveInstance(int) {}
func (o *MockObject) GetInstances() []int {
	return nil
}
func (o *MockObject) GetEnabler() ObjectEnabler {
	return o.enabler
}
func (o *MockObject) GetType() LWM2MObjectType {
	return LWM2MObjectType(0)
}
func (o *MockObject) GetDefinition() ObjectDefinition {
	return nil
}
func (o *MockObject) SetEnabler(ObjectEnabler) {}

func NewMockClient() LWM2MClient {
	return &MockClient{
		enabledObjects: make(map[LWM2MObjectType]Object),
	}
}

type MockClient struct {
	enabledObjects map[LWM2MObjectType]Object
	registry       Registry
}

func (c *MockClient) AddObjectInstance(LWM2MObjectType, int) error {
	return nil
}
func (c *MockClient) Register(string) (string, error) {
	return "", nil
}
func (c *MockClient) UseRegistry(r Registry) {
	c.registry = r
}

func (c *MockClient) EnableObject(t LWM2MObjectType, e ObjectEnabler) error {
	_, ok := c.enabledObjects[t]
	if !ok {
		c.enabledObjects[t] = NewMockObject(t, e, c.GetRegistry())

		return nil
	} else {
		return errors.New("Object already enabled")
	}
}

func (c *MockClient) GetRegistry() Registry {
	return c.registry
}
func (c *MockClient) GetEnabledObjects() map[LWM2MObjectType]Object {
	return c.enabledObjects
}

func (c *MockClient) AddObjectInstances(LWM2MObjectType, ...int) {}
func (c *MockClient) AddResource()                               {}
func (c *MockClient) AddObject()                                 {}
func (c *MockClient) Deregister()                                {}
func (c *MockClient) Update()                                    {}
func (c *MockClient) SetEnabler(LWM2MObjectType, ObjectEnabler)  {}
func (c *MockClient) Start()                                     {}
func (c *MockClient) OnStartup(FnOnStartup)                      {}
func (c *MockClient) OnRead(FnOnRead)                            {}
func (c *MockClient) OnWrite(FnOnWrite)                          {}
func (c *MockClient) OnExecute(FnOnExecute)                      {}
func (c *MockClient) OnRegistered(FnOnRegistered)                {}
func (c *MockClient) OnDeregistered(FnOnDeregistered)            {}
func (c *MockClient) OnError(FnOnError)                          {}
<|endoftext|>


<commit_before>package zerolog

import (
	"net"
	"sync"
	"time"
)

var arrayPool = &sync.Pool{
	New: func() interface{} {
		return &Array{
			buf: make([]byte, 0, 500),
		}
	},
}

// Array is used to prepopulate an array of items
// which can be re-used to add to log messages.
type Array struct {
	buf []byte
}

// Arr creates an array to be added to an Event or Context.
func Arr() *Array {
	a := arrayPool.Get().(*Array)
	a.buf = a.buf[:0]
	return a
}

// MarshalZerologArray method here is no-op - since data is
// already in the needed format.
func (*Array) MarshalZerologArray(*Array) {
}

func (a *Array) write(dst []byte) []byte {
	dst = enc.AppendArrayStart(dst)
	if len(a.buf) > 0 {
		dst = append(append(dst, a.buf...))
	}
	dst = enc.AppendArrayEnd(dst)
	arrayPool.Put(a)
	return dst
}

// Object marshals an object that implement the LogObjectMarshaler
// interface and enc.Append it to the array.
func (a *Array) Object(obj LogObjectMarshaler) *Array {
	e := Dict()
	obj.MarshalZerologObject(e)
	e.buf = enc.AppendEndMarker(e.buf)
	a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
	eventPool.Put(e)
	return a
}

// Str enc.Append the val as a string to the array.
func (a *Array) Str(val string) *Array {
	a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), val)
	return a
}

// Bytes enc.Append the val as a string to the array.
func (a *Array) Bytes(val []byte) *Array {
	a.buf = enc.AppendBytes(enc.AppendArrayDelim(a.buf), val)
	return a
}

// Hex enc.Append the val as a hex string to the array.
func (a *Array) Hex(val []byte) *Array {
	a.buf = enc.AppendHex(enc.AppendArrayDelim(a.buf), val)
	return a
}

// Err enc.Append the err as a string to the array.
func (a *Array) Err(err error) *Array {
	a.buf = enc.AppendError(enc.AppendArrayDelim(a.buf), err)
	return a
}

// Bool enc.Append the val as a bool to the array.
func (a *Array) Bool(b bool) *Array {
	a.buf = enc.AppendBool(enc.AppendArrayDelim(a.buf), b)
	return a
}

// Int enc.Append i as a int to the array.
func (a *Array) Int(i int) *Array {
	a.buf = enc.AppendInt(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int8 enc.Append i as a int8 to the array.
func (a *Array) Int8(i int8) *Array {
	a.buf = enc.AppendInt8(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int16 enc.Append i as a int16 to the array.
func (a *Array) Int16(i int16) *Array {
	a.buf = enc.AppendInt16(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int32 enc.Append i as a int32 to the array.
func (a *Array) Int32(i int32) *Array {
	a.buf = enc.AppendInt32(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int64 enc.Append i as a int64 to the array.
func (a *Array) Int64(i int64) *Array {
	a.buf = enc.AppendInt64(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint enc.Append i as a uint to the array.
func (a *Array) Uint(i uint) *Array {
	a.buf = enc.AppendUint(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint8 enc.Append i as a uint8 to the array.
func (a *Array) Uint8(i uint8) *Array {
	a.buf = enc.AppendUint8(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint16 enc.Append i as a uint16 to the array.
func (a *Array) Uint16(i uint16) *Array {
	a.buf = enc.AppendUint16(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint32 enc.Append i as a uint32 to the array.
func (a *Array) Uint32(i uint32) *Array {
	a.buf = enc.AppendUint32(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint64 enc.Append i as a uint64 to the array.
func (a *Array) Uint64(i uint64) *Array {
	a.buf = enc.AppendUint64(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Float32 enc.Append f as a float32 to the array.
func (a *Array) Float32(f float32) *Array {
	a.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f)
	return a
}

// Float64 enc.Append f as a float64 to the array.
func (a *Array) Float64(f float64) *Array {
	a.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f)
	return a
}

// Time enc.Append t formated as string using zerolog.TimeFieldFormat.
func (a *Array) Time(t time.Time) *Array {
	a.buf = enc.AppendTime(enc.AppendArrayDelim(a.buf), t, TimeFieldFormat)
	return a
}

// Dur enc.Append d to the array.
func (a *Array) Dur(d time.Duration) *Array {
	a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger)
	return a
}

// Interface enc.Append i marshaled using reflection.
func (a *Array) Interface(i interface{}) *Array {
	if obj, ok := i.(LogObjectMarshaler); ok {
		return a.Object(obj)
	}
	a.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), i)
	return a
}

// IPAddr adds IPv4 or IPv6 address to the array
func (a *Array) IPAddr(ip net.IP) *Array {
	a.buf = enc.AppendIPAddr(enc.AppendArrayDelim(a.buf), ip)
	return a
}

// IPPrefix adds IPv4 or IPv6 Prefix (IP + mask) to the array
func (a *Array) IPPrefix(pfx net.IPNet) *Array {
	a.buf = enc.AppendIPPrefix(enc.AppendArrayDelim(a.buf), pfx)
	return a
}

// MACAddr adds a MAC (Ethernet) address to the array
func (a *Array) MACAddr(ha net.HardwareAddr) *Array {
	a.buf = enc.AppendMACAddr(enc.AppendArrayDelim(a.buf), ha)
	return a
}
<commit_msg>Fix comments<commit_after>package zerolog

import (
	"net"
	"sync"
	"time"
)

var arrayPool = &sync.Pool{
	New: func() interface{} {
		return &Array{
			buf: make([]byte, 0, 500),
		}
	},
}

// Array is used to prepopulate an array of items
// which can be re-used to add to log messages.
type Array struct {
	buf []byte
}

// Arr creates an array to be added to an Event or Context.
func Arr() *Array {
	a := arrayPool.Get().(*Array)
	a.buf = a.buf[:0]
	return a
}

// MarshalZerologArray method here is no-op - since data is
// already in the needed format.
func (*Array) MarshalZerologArray(*Array) {
}

func (a *Array) write(dst []byte) []byte {
	dst = enc.AppendArrayStart(dst)
	if len(a.buf) > 0 {
		dst = append(append(dst, a.buf...))
	}
	dst = enc.AppendArrayEnd(dst)
	arrayPool.Put(a)
	return dst
}

// Object marshals an object that implement the LogObjectMarshaler
// interface and Append it to the array.
func (a *Array) Object(obj LogObjectMarshaler) *Array {
	e := Dict()
	obj.MarshalZerologObject(e)
	e.buf = enc.AppendEndMarker(e.buf)
	a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
	eventPool.Put(e)
	return a
}

// Str Append the val as a string to the array.
func (a *Array) Str(val string) *Array {
	a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), val)
	return a
}

// Bytes Append the val as a string to the array.
func (a *Array) Bytes(val []byte) *Array {
	a.buf = enc.AppendBytes(enc.AppendArrayDelim(a.buf), val)
	return a
}

// Hex Append the val as a hex string to the array.
func (a *Array) Hex(val []byte) *Array {
	a.buf = enc.AppendHex(enc.AppendArrayDelim(a.buf), val)
	return a
}

// Err Append the err as a string to the array.
func (a *Array) Err(err error) *Array {
	a.buf = enc.AppendError(enc.AppendArrayDelim(a.buf), err)
	return a
}

// Bool Append the val as a bool to the array.
func (a *Array) Bool(b bool) *Array {
	a.buf = enc.AppendBool(enc.AppendArrayDelim(a.buf), b)
	return a
}

// Int Append i as a int to the array.
func (a *Array) Int(i int) *Array {
	a.buf = enc.AppendInt(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int8 Append i as a int8 to the array.
func (a *Array) Int8(i int8) *Array {
	a.buf = enc.AppendInt8(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int16 Append i as a int16 to the array.
func (a *Array) Int16(i int16) *Array {
	a.buf = enc.AppendInt16(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int32 Append i as a int32 to the array.
func (a *Array) Int32(i int32) *Array {
	a.buf = enc.AppendInt32(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Int64 Append i as a int64 to the array.
func (a *Array) Int64(i int64) *Array {
	a.buf = enc.AppendInt64(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint Append i as a uint to the array.
func (a *Array) Uint(i uint) *Array {
	a.buf = enc.AppendUint(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint8 Append i as a uint8 to the array.
func (a *Array) Uint8(i uint8) *Array {
	a.buf = enc.AppendUint8(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint16 Append i as a uint16 to the array.
func (a *Array) Uint16(i uint16) *Array {
	a.buf = enc.AppendUint16(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint32 Append i as a uint32 to the array.
func (a *Array) Uint32(i uint32) *Array {
	a.buf = enc.AppendUint32(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Uint64 Append i as a uint64 to the array.
func (a *Array) Uint64(i uint64) *Array {
	a.buf = enc.AppendUint64(enc.AppendArrayDelim(a.buf), i)
	return a
}

// Float32 Append f as a float32 to the array.
func (a *Array) Float32(f float32) *Array {
	a.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f)
	return a
}

// Float64 Append f as a float64 to the array.
func (a *Array) Float64(f float64) *Array {
	a.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f)
	return a
}

// Time Append t formated as string using zerolog.TimeFieldFormat.
func (a *Array) Time(t time.Time) *Array {
	a.buf = enc.AppendTime(enc.AppendArrayDelim(a.buf), t, TimeFieldFormat)
	return a
}

// Dur Append d to the array.
func (a *Array) Dur(d time.Duration) *Array {
	a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger)
	return a
}

// Interface Append i marshaled using reflection.
func (a *Array) Interface(i interface{}) *Array {
	if obj, ok := i.(LogObjectMarshaler); ok {
		return a.Object(obj)
	}
	a.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), i)
	return a
}

// IPAddr adds IPv4 or IPv6 address to the array
func (a *Array) IPAddr(ip net.IP) *Array {
	a.buf = enc.AppendIPAddr(enc.AppendArrayDelim(a.buf), ip)
	return a
}

// IPPrefix adds IPv4 or IPv6 Prefix (IP + mask) to the array
func (a *Array) IPPrefix(pfx net.IPNet) *Array {
	a.buf = enc.AppendIPPrefix(enc.AppendArrayDelim(a.buf), pfx)
	return a
}

// MACAddr adds a MAC (Ethernet) address to the array
func (a *Array) MACAddr(ha net.HardwareAddr) *Array {
	a.buf = enc.AppendMACAddr(enc.AppendArrayDelim(a.buf), ha)
	return a
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/bowery/prompt"
	"golang.org/x/crypto/bcrypt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

func main() {
	if len(os.Args) < 2 {
		log.Fatal("No file given.")
	}

	clearfile := filepath.Clean(os.Args[1])
	encryptedfile := strings.Join([]string{clearfile, "aes"}, ".")

	cleartext, err := ioutil.ReadFile(clearfile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Password: ")
	password, err := prompt.Password("Password")
	if err != nil {
		panic(err)
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		panic(err)
	}

	hasher := sha256.New()
	hasher.Write(hash)
	key := hasher.Sum(nil)

	fmt.Printf("%s\n", cleartext)

	encryptedtext, err := encrypt(key, cleartext)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%0x\n", encryptedtext)

	if err := ioutil.WriteFile(encryptedfile, encryptedtext, 0644); err != nil {
		panic(err)
	}

	result, err := decrypt(key, encryptedtext)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", result)
}

func encrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	b := base64.StdEncoding.EncodeToString(text)
	encryptedtext := make([]byte, aes.BlockSize+len(b))
	iv := encryptedtext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(encryptedtext[aes.BlockSize:], []byte(b))
	return encryptedtext, nil
}

func decrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("encryptedtext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	data, err := base64.StdEncoding.DecodeString(string(text))
	if err != nil {
		return nil, err
	}
	return data, nil
}

func openFile() {

}
<commit_msg>update<commit_after>package main

import (
	"strings"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/bowery/prompt"
	"golang.org/x/crypto/bcrypt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

func main() {
	if len(os.Args) < 2 {
		log.Fatal("No file given.")
	}

	path := filepath.Clean(os.Args[1])
	for _, file := range listFiles(path) {
		encryptFile(file)
	}
}

func encryptFile(clearfile string) {

	encryptedfile := strings.Join([]string{clearfile, "aes"}, ".")

	cleartext, err := ioutil.ReadFile(clearfile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Password: ")
	password, err := prompt.Password("Password")
	if err != nil {
		panic(err)
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		panic(err)
	}

	hasher := sha256.New()
	hasher.Write(hash)
	key := hasher.Sum(nil)

	fmt.Printf("%s\n", cleartext)

	encryptedtext, err := encrypt(key, cleartext)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%0x\n", encryptedtext)

	if err := ioutil.WriteFile(encryptedfile, encryptedtext, 0644); err != nil {
		panic(err)
	}

	result, err := decrypt(key, encryptedtext)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", result)
}

func listFiles(path string) []string {
	return []string{path}
}

func encrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	b := base64.StdEncoding.EncodeToString(text)
	encryptedtext := make([]byte, aes.BlockSize+len(b))
	iv := encryptedtext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(encryptedtext[aes.BlockSize:], []byte(b))
	return encryptedtext, nil
}

func decrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("encryptedtext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	data, err := base64.StdEncoding.DecodeString(string(text))
	if err != nil {
		return nil, err
	}
	return data, nil
}

func openFile() {

}
<|endoftext|>


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

// Tags helper functions

package circonusgometrics

import (
	"encoding/base64"
	"fmt"
	"sort"
	"strings"
	"unicode"
)

// Tag defines an individual tag
type Tag struct {
	Category string
	Value    string
}

// Tags defines a list of tags
type Tags []Tag

// SetMetricTags sets the tags for the named metric and flags a check update is needed
func (m *CirconusMetrics) SetMetricTags(name string, tags []string) bool {
	return m.check.AddMetricTags(name, tags, false)
}

// AddMetricTags appends tags to any existing tags for the named metric and flags a check update is needed
func (m *CirconusMetrics) AddMetricTags(name string, tags []string) bool {
	return m.check.AddMetricTags(name, tags, true)
}

// EncodeMetricStreamTags encodes Tags into a string suitable for use with
// stream tags. Tags directly embedded into metric names using the
// `metric_name|ST[<tags>]` syntax.
func EncodeMetricStreamTags(tags *Tags) string {
	if len(*tags) == 0 {
		return ""
	}

	tagEnc := make(map[string]bool)
	// additional deduplication step for stream tags since spaces are removed prior to encoding
	for _, tag := range prepTags(tags) {
		c := base64.StdEncoding.EncodeToString([]byte(strings.Map(removeSpaces, tag.Category)))
		v := base64.StdEncoding.EncodeToString([]byte(strings.Map(removeSpaces, tag.Value)))
		tagEnc[fmt.Sprintf(`b"%s":b"%s"`, c, v)] = true
	}
	i := 0
	tagList := make([]string, len(tagEnc))
	for t := range tagEnc {
		tagList[i] = t
		i++
	}
	return strings.Join(tagList, ",")
}

// EncodeMetricTags encodes Tags into an array of strings. The format check_bundle.metircs.metric.tags needs.
// This helper is intended to work with legacy check bundle metrics. Tags directly on named metrics are being
// deprecated in favor of stream tags.
func EncodeMetricTags(tags *Tags) []string {
	if len(*tags) == 0 {
		return []string{}
	}

	tagList := []string{}
	for _, tag := range prepTags(tags) {
		tagList = append(tagList, fmt.Sprintf(`%s:%s`, tag.Category, tag.Value))
	}
	return tagList
}

func removeSpaces(r rune) rune {
	if unicode.IsSpace(r) {
		return -1
	}
	return r
}

type compareFunc func(t1, t2 *Tag) int

type multiSorter struct {
	tags    Tags
	compare []compareFunc
}

func orderBy(compare ...compareFunc) *multiSorter {
	return &multiSorter{
		compare: compare,
	}
}

func (ms *multiSorter) Sort(tags Tags) {
	ms.tags = tags
	sort.Sort(ms)
}

func (ms *multiSorter) Len() int {
	return len(ms.tags)
}

func (ms *multiSorter) Swap(i, j int) {
	ms.tags[i], ms.tags[j] = ms.tags[j], ms.tags[i]
}

func (ms *multiSorter) Less(i, j int) bool {
	t1, t2 := &ms.tags[i], &ms.tags[j]
	var k int
	for k = 0; k < len(ms.compare)-1; k++ {
		if ms.compare[k](t1, t2) == -1 {
			return true
		}
	}
	return ms.compare[k](t1, t2) == -1
}

func prepTags(tags *Tags) Tags {
	if len(*tags) == 0 {
		return Tags{}
	}

	ltags := make(Tags, len(*tags))
	copy(ltags, *tags)

	// multisort
	category := func(t1, t2 *Tag) int {
		if t1.Category == t2.Category {
			return 0
		} else if t1.Category < t2.Category {
			return -1
		}
		return 1
	}
	value := func(t1, t2 *Tag) int {
		if t1.Value == t2.Value {
			return 0
		} else if t1.Value < t2.Value {
			return -1
		}
		return 1
	}

	orderBy(category, value).Sort(ltags)

	// deduplicate
	unique := make(map[string]map[string]bool)
	for _, tag := range ltags {
		if _, exists := unique[tag.Category]; !exists {
			unique[tag.Category] = make(map[string]bool)
		}
		unique[tag.Category][tag.Value] = true
	}

	if len(unique) == 0 {
		return Tags{}
	}

	tagList := Tags{}
	for tcat, tvals := range unique {
		for tval := range tvals {
			tagList = append(tagList, Tag{tcat, tval})
		}
	}

	return tagList
}
<commit_msg>fix: EncodeMetricTags, return sorted tags<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Tags helper functions

package circonusgometrics

import (
	"encoding/base64"
	"fmt"
	"sort"
	"strings"
	"unicode"
)

// Tag defines an individual tag
type Tag struct {
	Category string
	Value    string
}

// Tags defines a list of tags
type Tags []Tag

// SetMetricTags sets the tags for the named metric and flags a check update is needed
func (m *CirconusMetrics) SetMetricTags(name string, tags []string) bool {
	return m.check.AddMetricTags(name, tags, false)
}

// AddMetricTags appends tags to any existing tags for the named metric and flags a check update is needed
func (m *CirconusMetrics) AddMetricTags(name string, tags []string) bool {
	return m.check.AddMetricTags(name, tags, true)
}

// EncodeMetricStreamTags encodes Tags into a string suitable for use with
// stream tags. Tags directly embedded into metric names using the
// `metric_name|ST[<tags>]` syntax.
func EncodeMetricStreamTags(tags *Tags) string {
	if len(*tags) == 0 {
		return ""
	}

	tagEnc := make(map[string]bool)
	// additional deduplication step for stream tags since spaces are removed prior to encoding
	for _, tag := range prepTags(tags) {
		c := base64.StdEncoding.EncodeToString([]byte(strings.Map(removeSpaces, tag.Category)))
		v := base64.StdEncoding.EncodeToString([]byte(strings.Map(removeSpaces, tag.Value)))
		tagEnc[fmt.Sprintf(`b"%s":b"%s"`, c, v)] = true
	}
	i := 0
	tagList := make([]string, len(tagEnc))
	for t := range tagEnc {
		tagList[i] = t
		i++
	}
	return strings.Join(tagList, ",")
}

// EncodeMetricTags encodes Tags into an array of strings. The format check_bundle.metircs.metric.tags needs.
// This helper is intended to work with legacy check bundle metrics. Tags directly on named metrics are being
// deprecated in favor of stream tags.
func EncodeMetricTags(tags *Tags) []string {
	if len(*tags) == 0 {
		return []string{}
	}

	tagList := []string{}
	for _, tag := range prepTags(tags) {
		tagList = append(tagList, fmt.Sprintf(`%s:%s`, tag.Category, tag.Value))
	}
	sort.Strings(tagList)
	return tagList
}

func removeSpaces(r rune) rune {
	if unicode.IsSpace(r) {
		return -1
	}
	return r
}

type compareFunc func(t1, t2 *Tag) int

type multiSorter struct {
	tags    Tags
	compare []compareFunc
}

func orderBy(compare ...compareFunc) *multiSorter {
	return &multiSorter{
		compare: compare,
	}
}

func (ms *multiSorter) Sort(tags Tags) {
	ms.tags = tags
	sort.Sort(ms)
}

func (ms *multiSorter) Len() int {
	return len(ms.tags)
}

func (ms *multiSorter) Swap(i, j int) {
	ms.tags[i], ms.tags[j] = ms.tags[j], ms.tags[i]
}

func (ms *multiSorter) Less(i, j int) bool {
	t1, t2 := &ms.tags[i], &ms.tags[j]
	var k int
	for k = 0; k < len(ms.compare)-1; k++ {
		if ms.compare[k](t1, t2) == -1 {
			return true
		}
	}
	return ms.compare[k](t1, t2) == -1
}

func prepTags(tags *Tags) Tags {
	if len(*tags) == 0 {
		return Tags{}
	}

	ltags := make(Tags, len(*tags))
	copy(ltags, *tags)

	// multisort
	category := func(t1, t2 *Tag) int {
		if t1.Category == t2.Category {
			return 0
		} else if t1.Category < t2.Category {
			return -1
		}
		return 1
	}
	value := func(t1, t2 *Tag) int {
		if t1.Value == t2.Value {
			return 0
		} else if t1.Value < t2.Value {
			return -1
		}
		return 1
	}

	orderBy(category, value).Sort(ltags)

	// deduplicate
	unique := make(map[string]map[string]bool)
	for _, tag := range ltags {
		if _, exists := unique[tag.Category]; !exists {
			unique[tag.Category] = make(map[string]bool)
		}
		unique[tag.Category][tag.Value] = true
	}

	if len(unique) == 0 {
		return Tags{}
	}

	tagList := Tags{}
	for tcat, tvals := range unique {
		for tval := range tvals {
			tagList = append(tagList, Tag{tcat, tval})
		}
	}

	return tagList
}
<|endoftext|>


<commit_before>package finch

import (
	"crypto/sha1"
	"encoding/base64"

	"github.com/vmihailenco/msgpack"

	"io"
	"time"
)

type Task struct {
	Id          string
	Description string
	Added       time.Time
	Selected    bool
	Pending     bool
}

func NewTask(description string, added time.Time) *Task {
	t := new(Task)
	t.Description = description
	t.Added = added
	t.Selected = false
	t.Pending = true

	hash := sha1.New()
	io.WriteString(hash, description)
	descPart := base64.StdEncoding.EncodeToString(hash.Sum(nil))

	timePart := added.Format(time.RFC3339)

	t.Id = timePart + "/" + descPart

	return t
}

func DeserializeTask(szd []byte) (*Task, error) {
	t := new(Task)
	err := msgpack.Unmarshal(szd, t)

	return t, err
}

func (t *Task) Serialize() ([]byte, error) {
	b, err := msgpack.Marshal(t)
	return b, err
}

func (t *Task) Key(idx string) *Key {
	return KeyForTask(idx, t)
}
<commit_msg>get rid of duplicate time parts<commit_after>package finch

import (
	"crypto/sha1"
	"encoding/base64"

	"github.com/vmihailenco/msgpack"

	"io"
	"time"
)

type Task struct {
	Id          string
	Description string
	Added       time.Time
	Selected    bool
	Pending     bool
}

func NewTask(description string, added time.Time) *Task {
	t := new(Task)
	t.Description = description
	t.Added = added
	t.Selected = false
	t.Pending = true

	hash := sha1.New()
	io.WriteString(hash, description)
	t.Id = base64.StdEncoding.EncodeToString(hash.Sum(nil))

	return t
}

func DeserializeTask(szd []byte) (*Task, error) {
	t := new(Task)
	err := msgpack.Unmarshal(szd, t)

	return t, err
}

func (t *Task) Serialize() ([]byte, error) {
	b, err := msgpack.Marshal(t)
	return b, err
}

func (t *Task) Key(idx string) *Key {
	return KeyForTask(idx, t)
}
<|endoftext|>