Пример #1
0
// get a service
func (c *ServiceController) Get(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

	service, found, err := models.FindServiceByObjectID(db.GetPostgresHandle(), params["id"])
	if !found {
		services.Res(res).Error(404, "not_found", "service was not found")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	respObj, _ := services.StructToJsonToMap(service)
	services.Res(res).Json(respObj)
}
Пример #2
0
// get an identity
func (c *IdentityController) Get(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

	identity, found, err := models.FindIdentityByObjectID(db.GetPostgresHandle(), params["id"])
	if !found {
		services.Res(res).Error(404, "not_found", "identity was not found")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// create response
	respObj, _ := services.StructToJsonToMap(identity)

	if identity.Issuer {
		respObj["soul_balance"] = identity.SoulBalance
	}

	services.Res(res).Json(respObj)
}
Пример #3
0
// get an object by its id or pin
func (c *ObjectController) Get(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) {

	dbObj := db.GetPostgresHandle()
	object, found, err := models.FindObjectByObjectIDOrPin(dbObj, params["id"])
	if !found {
		services.Res(res).Error(404, "not_found", "object was not found")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// construct response. remove private fields
	respObj, _ := services.StructToJsonToMap(object)
	delete(respObj["wallet"].(map[string]interface{})["identity"].(map[string]interface{}), "soul_balance")
	delete(respObj["wallet"].(map[string]interface{})["identity"].(map[string]interface{}), "email")
	delete(respObj["service"].(map[string]interface{})["identity"].(map[string]interface{}), "soul_balance")
	delete(respObj["service"].(map[string]interface{})["identity"].(map[string]interface{}), "email")

	services.Res(res).Json(respObj)
}
Пример #4
0
// generate and return client_credentials token
func (c *AuthController) GetClientCredentialToken(res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) {

	// get base64 encoded credentials
	base64Credential := services.StringSplit(req.Header.Get("Authorization"), " ")[1]
	base64CredentialDecoded := services.DecodeB64(base64Credential)
	credentials := services.StringSplit(base64CredentialDecoded, ":")

	// check if requesting client is a back service id
	if credentials[0] == BackOfficeId && credentials[1] == BackOfficeSecret {

		exp := int64(0)
		token, err := createJWTToken("", true, exp)
		if err != nil {
			log.Error(err)
			services.Res(res).Error(500, "", "server error")
			return
		}

		// create and save new token
		newToken := models.Token{
			Token:     token,
			Type:      "bearer",
			ExpiresIn: time.Time{},
			CreatedAt: time.Now().UTC(),
			UpdatedAt: time.Now().UTC(),
		}

		// persist token
		err = models.CreateToken(db.GetPostgresHandle(), &newToken)
		if err != nil {
			c.log.Error(err.Error())
			services.Res(res).Error(500, "", "server error")
			return
		}

		services.Res(res).Json(newToken)
		return
	}

	// find service by client id
	service, found, err := models.FindServiceByClientId(db.GetPostgresHandle(), credentials[0])
	if !found && err == nil {
		log.Error(err)
		services.Res(res).Error(404, "", "service not found")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// compare secret
	if credentials[1] != service.ClientSecret {
		log.Error(err)
		services.Res(res).Error(401, "", "service credentials are invalid. ensure client id and secret are valid")
		return
	}

	// create access token
	exp := time.Now().Add(time.Hour * 1)
	token, err := createJWTToken(service.ObjectID, false, exp.UTC().Unix())
	if err != nil {
		log.Error(err)
		services.Res(res).Error(500, "", "server error")
		return
	}

	// create and save new token
	newToken := models.Token{
		Service:   service,
		Token:     token,
		Type:      "bearer",
		ExpiresIn: exp.UTC(),
		CreatedAt: time.Now().UTC(),
		UpdatedAt: time.Now().UTC(),
	}

	// persist token
	err = models.CreateToken(db.GetPostgresHandle(), &newToken)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	respObj, _ := services.StructToJsonToMap(newToken)
	respObj["service"] = services.DeleteKeys(respObj["service"].(map[string]interface{}), "client_id", "client_secret")
	services.Res(res).Json(respObj)
}
Пример #5
0
// create a service
func (c *ServiceController) Create(res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

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

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

	// full name must have max of 60 characters
	if !validator.StringLength(body.FullName, "1", "60") {
		services.Res(res).Error(400, "invalid_full_name", "full_name character limit is 60")
		return
	}

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

	// service name must have max of 30 characters
	if !validator.StringLength(body.ServiceName, "1", "60") {
		services.Res(res).Error(400, "invalid_service_name", "service_name character limit is 60")
		return
	}

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

	// description must have max of 140 characters
	if !validator.StringLength(body.Description, "1", "140") {
		services.Res(res).Error(400, "invalid_description", "description character limit is 140")
		return
	}

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

	// email must be valid
	if !validator.IsEmail(body.Email) {
		services.Res(res).Error(400, "invalid_email", "email is invalid")
		return
	}

	// create identity
	newIdentity := &models.Identity{
		ObjectID: bson.NewObjectId().Hex(),
		FullName: body.FullName,
		Email:    body.Email,
	}

	// start db transaction
	dbTx := db.GetPostgresHandle().Begin()

	if err := models.CreateIdentity(dbTx, newIdentity); err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// create client credentials
	clientId := services.GetRandString(services.GetRandNumRange(32, 42))
	clientSecret := services.GetRandString(services.GetRandNumRange(32, 42))

	// create new service object
	newService := models.Service{
		ObjectID:     bson.NewObjectId().Hex(),
		Name:         body.ServiceName,
		Description:  body.Description,
		ClientID:     clientId,
		ClientSecret: clientSecret,
		Identity:     newIdentity,
	}

	// create service
	err := models.CreateService(dbTx, &newService)
	if err != nil {
		dbTx.Rollback()
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// commit db transaction
	dbTx.Commit()

	// send response
	respObj, _ := services.StructToJsonToMap(newService)
	services.Res(res).Json(respObj)
}
Пример #6
0
// enable a service to issuer status
func (c *ServiceController) EnableIssuer(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

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

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

	// ensure service exists
	service, found, err := models.FindServiceByObjectID(db.GetPostgresHandle(), body.ServiceID)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	} else if !found {
		services.Res(res).Error(404, "not_found", "service was not found")
		return
	}

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

	// ensure no other service has used the object name
	identity, found, err := models.FindIdentityByObjectName(db.GetPostgresHandle(), body.ObjectName)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	} else if found && identity.ObjectID != service.Identity.ObjectID {
		services.Res(res).Error(400, "invalid_object_name", "object name is not available, try a unique name")
		return
	}

	// base currency is required
	if validator.IsNull(body.BaseCurrency) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: base_currency")
		return
	}

	// base currency must be supported
	if !services.StringInStringSlice(config.SupportedBaseCurrencies, body.BaseCurrency) {
		services.Res(res).Error(400, "invalid_base_currency", "base currency is unknown")
		return
	}

	// set issuer to true
	service.Identity.Issuer = true
	service.Identity.ObjectName = strings.ToLower(body.ObjectName)
	service.Identity.BaseCurrency = body.BaseCurrency
	db.GetPostgresHandle().Save(&service)

	respObj, _ := services.StructToJsonToMap(service)
	respObj["identity"].(map[string]interface{})["soul_balance"] = service.Identity.SoulBalance
	services.Res(res).Json(respObj)
}
Пример #7
0
func PostgresAutoMigration(db *services.DB) {
	db.GetPostgresHandle().AutoMigrate(&models.Token{}, &models.Service{}, &models.Identity{}, &models.Wallet{}, &models.Object{})
	services.Println("Migration complete!")
}
Пример #8
0
// create an identity
func (c *IdentityController) Create(res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

	// parse request body
	var body identityCreateBody
	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
	}

	// full name is required
	if validator.IsNull(body.FullName) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: full_name")
		return
	}

	// email is required
	if validator.IsNull(body.Email) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: email")
		return
	}

	// email is required
	if !validator.IsEmail(body.Email) {
		services.Res(res).Error(400, "invalid_email", "email is invalid")
		return
	}

	// create identity
	newIdentity := models.Identity{
		ObjectID: bson.NewObjectId().Hex(),
		FullName: body.FullName,
		Email:    body.Email,
	}

	// if request is from Issuer controller, set issuer field to true
	if d := req.GetData("isIssuer"); d != nil && d.(bool) {

		// object is required
		if validator.IsNull(body.ObjectName) {
			services.Res(res).Error(400, "missing_parameter", "Missing required field: object_name")
			return
		}

		// base currency is required
		if validator.IsNull(body.BaseCurrency) {
			services.Res(res).Error(400, "missing_parameter", "Missing required field: base_currency")
			return
		}

		// base currency must be supported
		if !services.StringInStringSlice(config.SupportedBaseCurrencies, body.BaseCurrency) {
			services.Res(res).Error(400, "invalid_base_currency", "base currency is unknown")
			return
		}

		newIdentity.Issuer = true
		newIdentity.ObjectName = body.ObjectName
		newIdentity.BaseCurrency = body.BaseCurrency
	}

	err := models.CreateIdentity(db.GetPostgresHandle(), &newIdentity)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// create response
	respObj, _ := services.StructToJsonToMap(newIdentity)

	if newIdentity.Issuer {
		respObj["soul_balance"] = newIdentity.SoulBalance
	}

	services.Res(res).Json(respObj)

}
Пример #9
0
// renew an issuer identity soul
func (c *IdentityController) RenewSoul(res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

	// parse request body
	var body soulRenewBody
	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
	}

	// identity id is required
	if validator.IsNull(body.IdentityId) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: identity_id")
		return
	}

	// soul balance is required
	if body.SoulBalance == 0 {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: soul_balance")
		return
	}

	// soul balance must be greater than zero
	if body.SoulBalance < MinimumObjectUnit {
		services.Res(res).Error(400, "invalid_soul_balance", "Soul balance must be equal or greater than minimum object unit which is 0.00000001")
		return
	}

	// ensure identity exists
	identity, found, err := models.FindIdentityByObjectID(db.GetPostgresHandle(), body.IdentityId)
	if !found {
		services.Res(res).Error(404, "invalid_identity", "identity_id is unknown")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure identity is an issuer
	if !identity.Issuer {
		services.Res(res).Error(400, "invalid_identity", "identity is not an issuer")
		return
	}

	// add to soul balance
	newIdentity, err := models.AddToSoulByObjectID(db.GetPostgresHandle(), identity.ObjectID, body.SoulBalance)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// create response, hide some fields
	respObj, _ := services.StructToJsonToMap(newIdentity)

	if newIdentity.Issuer {
		respObj["soul_balance"] = newIdentity.SoulBalance
	}

	services.Res(res).Json(respObj)
}
Пример #10
0
// get counts and other numerical information about the state of a wallet
// e.g object balance and count etc
func (c *WalletController) Numbers(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

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

	dbCon := db.GetPostgresHandle()
	resp := map[string]interface{}{}
	query := req.URL.Query()
	count := int64(0)

	// predefine default query values
	query.Set("object_count", "true")
	query.Set("distinct_object_count", "true")
	query.Set("valuable_object_count", "true")
	query.Set("valueless_object_count", "true")
	query.Set("valueable_object_balance", "true")

	// get wallet
	wallet, found, err := models.FindWalletByObjectID(dbCon, params["id"])
	if !found {
		services.Res(res).Error(404, "not_found", "wallet not found")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure wallet matches authorizing wallet
	if wallet.ObjectID != authWalletID {
		services.Res(res).Error(401, "unauthorized", "client does not have permission to access wallet")
		return
	}

	// object_count
	objectCountField := query.Get("object_count")
	if !c.validate.IsEmpty(objectCountField) && services.StringInStringSlice([]string{"true", "false"}, objectCountField) {
		if objectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id": wallet.ID,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["object_count"] = count
			count = 0
		}
	}

	// distinct_object_count
	distinctObjectCountField := query.Get("distinct_object_count")
	if !c.validate.IsEmpty(distinctObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, distinctObjectCountField) {
		if distinctObjectCountField == "true" {
			row, err := dbCon.Raw("SELECT COUNT(*) FROM (SELECT DISTINCT service_id FROM objects WHERE wallet_id = ?) AS distinct_object_count;", wallet.ID).Rows()
			if err != nil {
				c.log.Error(err.Error())
				services.Res(res).Error(500, "", "server error")
				return
			}
			if row.Next() {
				row.Scan(&count)
			}
			resp["distinct_object_count"] = count
			count = 0
		}
	}

	// valuable_object_count
	valuableObjectCountField := query.Get("valuable_object_count")
	if !c.validate.IsEmpty(distinctObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, valuableObjectCountField) {
		if valuableObjectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id": wallet.ID,
				"type":      models.ObjectValue,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["valuable_object_count"] = count
			count = 0
		}
	}

	// valueless_object_count
	valuelessObjectCountField := query.Get("valueless_object_count")
	if !c.validate.IsEmpty(valuelessObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, valuelessObjectCountField) {
		if valuelessObjectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id": wallet.ID,
				"type":      models.ObjectValueless,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["valueless_object_count"] = count
			count = 0
		}
	}

	// valueable_object_balance
	valuableObjectBalanceField := query.Get("valueable_object_balance")
	if !c.validate.IsEmpty(distinctObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, valuableObjectBalanceField) {
		if valuableObjectBalanceField == "true" {
			row, err := dbCon.Raw("SELECT SUM(balance) AS total_balance FROM objects WHERE wallet_id = ? AND type = ?;", wallet.ID, models.ObjectValue).Rows()
			if err != nil {
				c.log.Error(err.Error())
				services.Res(res).Error(500, "", "server error")
				return
			}
			if row.Next() {
				row.Scan(&count)
			}
			resp["valueable_object_balance"] = count
			count = 0
		}
	}

	// opened_object_count
	openedObjectCountField := query.Get("opened_object_count")
	if !c.validate.IsEmpty(openedObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, openedObjectCountField) {
		if openedObjectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id": wallet.ID,
				"open":      true,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["opened_object_count"] = count
			count = 0
		}
	}

	// locked_object_count
	lockedObjectCountField := query.Get("locked_object_count")
	if !c.validate.IsEmpty(lockedObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, lockedObjectCountField) {
		if lockedObjectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id": wallet.ID,
				"open":      false,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["locked_object_count"] = count
			count = 0
		}
	}

	// opened_timed_object_count
	openedTimedObjectCountField := query.Get("opened_timed_object_count")
	if !c.validate.IsEmpty(openedObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, openedTimedObjectCountField) {
		if openedTimedObjectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id":   wallet.ID,
				"open":        true,
				"open_method": models.ObjectOpenTimed,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["opened_timed_object_count"] = count
			count = 0
		}
	}

	// opened_pin_object_count
	openedPinObjectCountField := query.Get("opened_pin_object_count")
	if !c.validate.IsEmpty(openedPinObjectCountField) && services.StringInStringSlice([]string{"true", "false"}, openedPinObjectCountField) {
		if openedPinObjectCountField == "true" {
			q := map[string]interface{}{
				"wallet_id":   wallet.ID,
				"open":        true,
				"open_method": models.ObjectOpenPin,
			}
			dbCon.Model(models.Object{}).Where(q).Count(&count)
			resp["opened_pin_object_count"] = count
			count = 0
		}
	}

	services.Res(res).Json(resp)
}
Пример #11
0
// create a wallet
func (c *WalletController) Create(res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

	// parse body
	var body walletCreateBody
	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
	}

	// identity id is required
	if validator.IsNull(body.IdentityId) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: identity_id")
		return
	}

	// handle is required
	if validator.IsNull(body.Handle) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: handle")
		return
	}

	// password is required
	if validator.IsNull(body.Password) {
		services.Res(res).Error(400, "missing_parameter", "Missing required field: password")
		return
	}

	// identity id must exist
	identity, found, err := models.FindIdentityByObjectID(db.GetPostgresHandle(), body.IdentityId)
	if !found {
		services.Res(res).Error(404, "invalid_identity", "identity_id is unknown")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// handle must be unique across wallets
	_, found, err = models.FindWalletByHandle(db.GetPostgresHandle(), body.Handle)
	if found {
		services.Res(res).Error(400, "handle_registered", "handle has been registered to another wallet")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// password length must be 6 characters
	if len(body.Password) < 6 {
		services.Res(res).Error(400, "invalid_password", "password is too short. minimum length is 6 characters")
		return
	}

	// securely hash password
	hashedPassword, err := services.Bcrypt(body.Password, 10)
	if err != nil {
		c.log.Error("unable to hash password. reason: " + err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	} else {
		body.Password = hashedPassword
	}

	// create wallet object
	newWallet := models.Wallet{
		ObjectID: bson.NewObjectId().Hex(),
		Identity: identity,
		Handle:   body.Handle,
		Password: body.Password,
	}

	// create wallet
	err = models.CreateWallet(db.GetPostgresHandle(), &newWallet)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	respObj, _ := services.StructToJsonToMap(newWallet)
	services.Res(res).Json(respObj)
}
Пример #12
0
// list object
// supports
// - pagination using 'page' query. Use per_page to set the number of results per page. max is 100
// - filters: filter_type, filter_service, filter_open, filter_open_method, filter_gte_date_created
//   filter_lte_date_created
// - sorting: sort_balance, sort_date_created
func (c *WalletController) List(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, db *services.DB) {

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

	dbCon := db.GetPostgresHandle()

	// get wallet
	wallet, found, err := models.FindWalletByObjectID(dbCon, params["id"])
	if !found {
		services.Res(res).Error(404, "not_found", "wallet not found")
		return
	} else if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// ensure wallet matches authorizing wallet
	if wallet.ObjectID != authWalletID {
		services.Res(res).Error(401, "unauthorized", "client does not have permission to access wallet")
		return
	}

	query := req.URL.Query()
	qPage := query.Get("page")
	if c.validate.IsEmpty(qPage) {
		qPage = "0"
	} else if !validator.IsNumeric(qPage) {
		services.Res(res).Error(400, "invalid_parameter", "page query value must be numeric")
		return
	}

	q := make(map[string]interface{})
	q["wallet_id"] = wallet.ID
	order := "id asc"
	limitPerPage := int64(2)
	offset := int64(0)
	currentPage, err := strconv.ParseInt(qPage, 0, 64)
	if err != nil {
		c.log.Error(err.Error())
		services.Res(res).Error(500, "", "server error")
		return
	}

	// set limit per page if provided in query
	qPerPage := query.Get("per_page")
	if !c.validate.IsEmpty(qPerPage) {
		if validator.IsNumeric(qPerPage) {
			qPerPage, _ := strconv.ParseInt(qPerPage, 0, 64)
			if qPerPage > 100 {
				qPerPage = 100
			} else if qPerPage <= 0 {
				qPerPage = limitPerPage
			}
			limitPerPage = qPerPage
		}
	}

	// set current page default and calculate offset
	if currentPage <= 1 {
		currentPage = 1
		offset = 0
	} else {
		offset = (int64(limitPerPage) * currentPage) - int64(limitPerPage)
	}

	// apply type filter if included in query
	filterType := query.Get("filter_type")
	if !c.validate.IsEmpty(filterType) && services.StringInStringSlice([]string{"obj_value", "obj_valueless"}, filterType) {
		q["type"] = filterType
	}

	// apply service filter if included in query
	filterService := query.Get("filter_service")
	if !c.validate.IsEmpty(filterService) {

		// find service
		service, found, err := models.FindServiceByObjectID(db.GetPostgresHandle(), filterService)
		if err != nil {
			c.log.Error(err.Error())
			services.Res(res).Error(500, "", "server error")
			return
		}

		if found {
			q["service_id"] = service.ID
		}
	}

	// apply open filter if included in query
	filterOpen := query.Get("filter_open")
	if !c.validate.IsEmpty(filterOpen) && services.StringInStringSlice([]string{"true", "false"}, filterOpen) {
		q["open"] = filterOpen
	}

	// apply open_method filter if included in query
	filterOpenMethod := query.Get("filter_open_method")
	if !c.validate.IsEmpty(filterOpenMethod) && services.StringInStringSlice([]string{"open", "open_timed", "open_pin"}, filterOpenMethod) {
		q["open_method"] = filterOpenMethod
	}

	// apply filter_gte_date_created filter if included in query
	filterGTEDateCreated := query.Get("filter_gte_date_created")
	if !c.validate.IsEmpty(filterGTEDateCreated) {
		if validator.IsNumeric(filterGTEDateCreated) {
			ts, _ := strconv.ParseInt(filterGTEDateCreated, 0, 64)
			dbCon = dbCon.Where("created_at >= ?", services.UnixToTime(ts).UTC().Format(time.RFC3339Nano))
		}
	}

	// apply filter_lte_date_created filter if included in query
	filterLTEDateCreated := query.Get("filter_lte_date_created")
	if !c.validate.IsEmpty(filterLTEDateCreated) {
		if validator.IsNumeric(filterLTEDateCreated) {
			ts, _ := strconv.ParseInt(filterLTEDateCreated, 0, 64)
			dbCon = dbCon.Where("created_at <= ?", services.UnixToTime(ts).UTC().Format(time.RFC3339Nano))
		}
	}

	// the below connection is used for sorting/ordering
	var dbConSort = dbCon

	// apply sort_balance sort if included
	sortBalance := query.Get("sort_balance")
	if !c.validate.IsEmpty(sortBalance) {
		orderVal := "asc"
		if sortBalance == "-1" {
			orderVal = "desc"
		}
		dbConSort = dbCon.Order("objects.balance " + orderVal)
	}

	// apply ort_date_created sort if included
	sortDateCreated := query.Get("sort_date_created")
	if !c.validate.IsEmpty(sortDateCreated) {
		orderVal := "asc"
		if sortDateCreated == "-1" {
			orderVal = "desc"
		}
		dbConSort = dbConSort.Order("objects.created_at " + orderVal)
	}

	// find objects associated with wallet
	objects := []models.Object{}
	var objectsCount int64

	// count number of objects. I didnt use dbConSort as count will throw an error
	dbCon.Model(models.Object{}).Where(q).Count(&objectsCount)

	// set the original db connection to the sort connection
	dbCon = dbConSort

	// calculate number of pages
	numPages := services.Round(float64(objectsCount) / float64(limitPerPage))

	// fetch the objects
	dbCon.Where(q).Preload("Service.Identity").Preload("Wallet.Identity").Limit(limitPerPage).Offset(offset).Order(order).Find(&objects)

	// prepare response
	respObj, _ := services.StructToJsonToSlice(objects)
	if len(respObj) == 0 {
		respObj = []map[string]interface{}{}
	}

	services.Res(res).Json(map[string]interface{}{
		"results": respObj,
		"_metadata": map[string]interface{}{
			"total_count": objectsCount,
			"per_page":    limitPerPage,
			"page_count":  numPages,
			"page":        currentPage,
		},
	})
}