Example #1
0
// lock sets set the 'open' property of an object to false. Also resets all fields used by
// all open methods
func (c *ObjectController) Lock(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) {

	// TODO: get from access token
	// authorizing wallet id
	authWalletID := "55c679145fe09c74ed000001"

	dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans()
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// get the object
	object, found, err := models.FindObjectByObjectID(dbTx, params["id"])
	if !found {
		dbTx.Rollback()
		services.Res(res).Error(404, "not_found", "object was not found")
		return
	} else if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure object belongs to authorizing wallet
	if object.Wallet.ObjectID != authWalletID {
		dbTx.Rollback()
		services.Res(res).Error(401, "unauthorized", "objects: object does not belong to authorizing wallet")
		return
	}

	// clear open related fields of object
	clearOpen(&object)

	// save update and commit
	dbTx.Save(&object).Commit()
	services.Res(res).Json(object)
}
Example #2
0
// create a new object by subtracting from a source object
func (c *ObjectController) Subtract(res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) {

	// TODO: get from access token
	// authorizing wallet id
	authWalletID := "55c679145fe09c74ed000001"

	// parse body
	var body objectSubtractBody
	if err := c.ParseJsonBody(req, &body); err != nil {
		services.Res(res).Error(400, "invalid_body", "request body is invalid or malformed. Expects valid json body")
		return
	}

	// object is required
	if c.validate.IsEmpty(body.Object) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: object")
		return
	}

	// amount to subtract is required
	if body.AmountToSubtract < MinimumObjectUnit {
		services.Res(res).Error(400, "invalid_parameter", "amount: amount must be equal or greater than the minimum object unit which is 0.00000001")
		return
	}

	dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans()
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// get the object
	object, found, err := models.FindObjectByObjectID(dbTx, body.Object)
	if !found {
		dbTx.Rollback()
		services.Res(res).Error(404, "not_found", "object was not found")
		return
	} else if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure object is a valuable type
	if object.Type == models.ObjectValueless {
		dbTx.Rollback()
		services.Res(res).Error(400, "invalid_parameter", "object: object must be a valuabe type (obj_value) ")
		return
	}

	// ensure object belongs to authorizing wallet
	if object.Wallet.ObjectID != authWalletID {
		dbTx.Rollback()
		services.Res(res).Error(401, "unauthorized", "objects: object does not belong to authorizing wallet")
		return
	}

	// ensure object's balance is sufficient
	if object.Balance < body.AmountToSubtract {
		dbTx.Rollback()
		services.Res(res).Error(400, "invalid_parameter", "amount: object's balance is insufficient")
		return
	}

	// if meta is provided, ensure it is not greater than the limit size
	if !body.InheritMeta && !c.validate.IsEmpty(body.Meta) && len([]byte(body.Meta)) > MaxMetaSize {
		services.Res(res).Error(400, "invalid_meta_size", fmt.Sprintf("Meta contains too much data. Max size is %d bytes", MaxMetaSize))
		return
	} else {
		if body.InheritMeta {
			body.Meta = object.Meta
		}
	}

	// subtract and update object's balance
	object.Balance = object.Balance - body.AmountToSubtract
	dbTx.Save(&object)

	// create new object
	// generate a pin
	countryCallCode := config.CurrencyCallCodes[strings.ToUpper(object.Service.Identity.BaseCurrency)]
	newPin, err := services.NewObjectPin(strconv.Itoa(countryCallCode))
	if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	newObj := NewObject(newPin, models.ObjectValue, object.Service, object.Wallet, body.AmountToSubtract, body.Meta)
	err = models.CreateObject(dbTx, &newObj)
	if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	dbTx.Commit()
	services.Res(res).Json(newObj)
}
Example #3
0
// open an object for charge/consumption. An object opened in this method
// will be consumable without restriction
func (c *ObjectController) Open(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) {

	// TODO: get from access token
	// authorizing wallet id
	authWalletID := "55c679145fe09c74ed000001"

	// parse body
	var body objectOpenBody
	if err := c.ParseJsonBody(req, &body); err != nil {
		services.Res(res).Error(400, "invalid_body", "request body is invalid or malformed. Expects valid json body")
		return
	}

	// ensure open method is provided
	if c.validate.IsEmpty(body.OpenMethod) {
		services.Res(res).Error(400, "invalid_parameter", "open_method: open method is required")
		return
	}

	// ensure a known open method is provided
	if body.OpenMethod != "open" && body.OpenMethod != "open_timed" && body.OpenMethod != "open_pin" {
		services.Res(res).Error(400, "invalid_parameter", "unknown open type method")
		return
	}

	dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans()
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// get the object
	object, found, err := models.FindObjectByObjectID(dbTx, params["id"])
	if !found {
		dbTx.Rollback()
		services.Res(res).Error(404, "not_found", "object was not found")
		return
	} else if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure object belongs to authorizing wallet
	if object.Wallet.ObjectID != authWalletID {
		dbTx.Rollback()
		services.Res(res).Error(401, "unauthorized", "objects: object does not belong to authorizing wallet")
		return
	}

	// set object's open property to true and open_method to `open`
	clearOpen(&object)
	object.Open = true
	object.OpenMethod = models.ObjectOpenDefault

	// for open_timed,
	// set 'open_time' field to indicate object open window
	if body.OpenMethod == "open_timed" {

		// ensure time field is provided
		if body.Time == 0 {
			dbTx.Rollback()
			services.Res(res).Error(400, "invalid_parameter", "time: open window time is required. use unix time")
			return
		}

		// time must be in the future
		now := time.Now().UTC()
		if !now.Before(services.UnixToTime(body.Time).UTC()) {
			dbTx.Rollback()
			services.Res(res).Error(400, "invalid_parameter", "time: use a unix time pointing to a period in the future")
			return
		}

		object.OpenMethod = models.ObjectOpenTimed
		object.OpenTime = body.Time
	}

	// for open_pin
	// open pin sets a pin for used by charge API
	if body.OpenMethod == "open_pin" {

		// ensure pin is provided
		if c.validate.IsEmpty(body.Pin) {
			dbTx.Rollback()
			services.Res(res).Error(400, "invalid_parameter", "pin: pin is required")
			return
		}

		// pin must be numeric
		if !validator.IsNumeric(body.Pin) {
			dbTx.Rollback()
			services.Res(res).Error(400, "invalid_parameter", "pin: pin must contain only numeric characters. e.g 4345")
			return
		}

		// pin length must be between 4 - 12 characters
		if len(body.Pin) < 4 || len(body.Pin) > 12 {
			dbTx.Rollback()
			services.Res(res).Error(400, "invalid_parameter", "pin: pin must have a minimum character length of 4 and maximum of 12")
			return
		}

		// hash pin using bcrypt
		pinHash, err := services.Bcrypt(body.Pin, 10)
		if err != nil {
			c.log.Error("unable to hash password. reason: " + err.Error())
			services.Res(res).Error(500, "", "server error")
			return
		}

		object.OpenMethod = models.ObjectOpenPin
		object.OpenPin = pinHash
	}

	dbTx.Save(&object).Commit()
	services.Res(res).Json(object)
}
Example #4
0
// divide an object into two or more equal parts.
// maxinum of 100 equal parts is allowed.
// object to be splitted must belong to authorizing wallet
func (c *ObjectController) Divide(res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) {

	// authorizing wallet id
	// todo: get from access token
	authWalletID := "55c679145fe09c74ed000001"

	// parse body
	var body objectDivideBody
	if err := c.ParseJsonBody(req, &body); err != nil {
		services.Res(res).Error(400, "invalid_body", "request body is invalid or malformed. Expects valid json body")
		return
	}

	// object is required
	if c.validate.IsEmpty(body.Object) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: object")
		return
	}

	// number of objects must be greater than 1
	if body.NumObjects < 2 {
		services.Res(res).Error(400, "invalid_parameter", "num_objects: must be greater than 1")
		return
	}

	// number of objects must not be greater than 100
	if body.NumObjects > 100 {
		services.Res(res).Error(400, "invalid_parameter", "num_objects: must be greater than 1")
		return
	}

	dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans()
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// get the object
	object, found, err := models.FindObjectByObjectID(dbTx, body.Object)
	if !found {
		dbTx.Rollback()
		services.Res(res).Error(404, "not_found", "object was not found")
		return
	} else if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure object is a valuable type
	if object.Type == models.ObjectValueless {
		dbTx.Rollback()
		services.Res(res).Error(400, "invalid_parameter", "object: object must be a valuabe type (obj_value) ")
		return
	}

	// ensure object has enough balance (minimum of 0.000001)
	if object.Balance < 0.000001 {
		dbTx.Rollback()
		services.Res(res).Error(400, "invalid_parameter", "object: object must have a minimum balance of 0.000001")
		return
	}

	// ensure object belongs to authorizing wallet
	if object.Wallet.ObjectID != authWalletID {
		dbTx.Rollback()
		services.Res(res).Error(401, "unauthorized", "object does not belong to authorizing wallet")
		return
	}

	// if meta is provided, ensure it is not greater than the limit size
	if !body.InheritMeta && !c.validate.IsEmpty(body.Meta) && len([]byte(body.Meta)) > MaxMetaSize {
		services.Res(res).Error(400, "invalid_meta_size", fmt.Sprintf("Meta contains too much data. Max size is %d bytes", MaxMetaSize))
		return
	} else {
		if body.InheritMeta {
			body.Meta = object.Meta
		}
	}

	// calculate new balance per object
	newBalance := object.Balance / float64(body.NumObjects)

	// delete object
	dbTx.Delete(&object)

	// create new objects
	newObjects := []models.Object{}
	for i := 0; i < body.NumObjects; i++ {

		// generate a pin
		countryCallCode := config.CurrencyCallCodes[strings.ToUpper(object.Service.Identity.BaseCurrency)]
		newPin, err := services.NewObjectPin(strconv.Itoa(countryCallCode))
		if err != nil {
			dbTx.Rollback()
			c.log.Error(err.Error())
			services.Res(res).Error(500, "", "server error")
			return
		}

		newObj := NewObject(newPin, models.ObjectValue, object.Service, object.Wallet, newBalance, body.Meta)
		err = models.CreateObject(dbTx, &newObj)
		if err != nil {
			dbTx.Rollback()
			c.log.Error(err.Error())
			services.Res(res).Error(500, "", "server error")
			return
		}

		newObjects = append(newObjects, newObj)
	}

	dbTx.Commit()
	services.Res(res).Json(newObjects)
}