Skip to content

Instantly share code, notes, and snippets.

@jba
Created September 5, 2022 13:09
Show Gist options
  • Save jba/ddcc3908c813f5b4afc845a69107d925 to your computer and use it in GitHub Desktop.
Save jba/ddcc3908c813f5b4afc845a69107d925 to your computer and use it in GitHub Desktop.
// https://gitlab.com/yoanyombapro/CubeMicroservices/-/blob/08ad700e385853134c5149155e2681c40db1195c/podinfo/pkg/database/user.go
package database
import (
"context"
"database/sql"
"errors"
"time"
"github.com/jinzhu/gorm"
"go.uber.org/zap"
model "gitlab.com/yoanyombapro/CubeMicroservices/podinfo/pkg/models/proto"
)
// CreateUser creates a user account record in the database
func (db *Database) CreateUser(ctx context.Context, user model.User) (*model.User, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var userOrm model.UserORM
// check if the user exists in the database based off of
// email and username
// Note: Email and Usernames must be unique across the entire database
recordNotFound := tx.Where(model.UserORM{Email: user.Email, UserName: user.UserName}).First(&userOrm).RecordNotFound()
// if the user does exist and the user account is not active, reactivate the user account
// and update the user state in the backend database
if !recordNotFound && !userOrm.IsActive {
userOrm.IsActive = true
if err := tx.Save(&userOrm).Error; err != nil {
db.Logger.Error("failed to create user", zap.Error(err))
return nil, err
}
return convertUserOrmToGenericUser(ctx, userOrm)
} else if !recordNotFound {
db.Logger.Error("failed to create user because user already exists")
return nil, errors.New("user already exists")
}
// convert the input user field to orm
userOrm, err := user.ToORM(ctx)
if err != nil {
db.Logger.Error("failed to create user", zap.Error(err))
return nil, err
}
// generate a random reset token for the user account
// and set it
userOrm.ResetToken = GenerateRandomToken(20)
currentTime := time.Now()
tokenExpirationTime := currentTime.Add(time.Hour * 24 * 10)
userOrm.ResetTokenExpiration = &tokenExpirationTime
// activate user account
userOrm.IsActive = true
// save the user to the database
if err := tx.Create(&userOrm).Error; err != nil {
db.Logger.Error("failed to create user", zap.Error(err))
return nil, err
}
db.Logger.Info("user successfully created",
zap.String("id", string(userOrm.Id)),
zap.String("username", userOrm.UserName),
zap.String("email", userOrm.Email))
return convertUserOrmToGenericUser(ctx, userOrm)
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return &model.User{}, err
}
createdUser := output.(*model.User)
return createdUser, nil
}
// convertUserOrmToGenericUser converts a user orm to generic type
func convertUserOrmToGenericUser(ctx context.Context, userOrm model.UserORM) (*model.User, error) {
createdUser, err := userOrm.ToPB(ctx)
if err != nil {
return nil, err
}
if err = createdUser.Validate(); err != nil {
return nil, err
}
return &createdUser, nil
}
// GetUserByID queries the database and obtains a user record by id
func (db *Database) GetUserByID(ctx context.Context, userID uint32) (*model.User, error) {
var userOrm model.UserORM
if recordNotFound := db.Engine.Where(model.UserORM{Id: userID}).First(&userOrm).RecordNotFound(); recordNotFound {
db.Logger.Error("user does not exist", zap.String("id", string(userID)))
return nil, errors.New("user does not exist")
}
// convert the obtained user ORM object to a user object and validate all fields are there
userObj, err := userOrm.ToPB(ctx)
if err != nil {
db.Logger.Error("failed to convert fields to protobuf format")
return nil, err
}
// perform field validation
if err = userObj.Validate(); err != nil {
db.Logger.Error("field validation failed")
return nil, err
}
db.Logger.Info("user successfully obtained user by id",
zap.String("id", string(userOrm.Id)),
zap.String("username", userOrm.UserName),
zap.String("email", userOrm.Email))
return &userObj, nil
}
// CreateUserProfile creates a user profile and ties it to a user account record
// if the account record exists.
func (db *Database) CreateUserProfile(ctx context.Context, userID uint32, profile model.Profile) (*model.Profile, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var userOrm *model.UserORM
// first validate the profile has all necessary fields of interest
if err := profile.Validate(); err != nil {
db.Logger.Error("profile field validation failed", zap.Error(err))
return nil, err
}
// check that the user exists
exists, userOrm, err := db.GetUserIfExists(userID, "", "")
if !exists {
db.Logger.Error("user account does not exist. please create one and try again", zap.Error(err))
return nil, err
}
if userOrm.Profile != nil && userOrm.Profile.Id != 0 {
db.Logger.Error("profile already exists")
return nil, errors.New("profile already exists")
}
// update the user ORM object with the profile ORM object
profileOrm, err := profile.ToORM(ctx)
if err != nil {
db.Logger.Error("failed to convert profile fields to orm type", zap.Error(err))
return nil, err
}
userOrm.Profile = &profileOrm
// Updates only the relevant fields of interest in a user entity in the database
if err = tx.Save(&userOrm).Error; err != nil {
db.Logger.Error("failed to create user profile", zap.Error(err))
return nil, err
}
profile.Id = userOrm.Profile.Id
db.Logger.Info("successfully created a profile for the user account",
zap.String("accountId", string(userOrm.Id)),
zap.String("profileId", string(profile.Id)))
return &profile, nil
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
createdProfile := output.(*model.Profile)
return createdProfile, nil
}
// CreateUserSubscription creates a subscription and ties it to a user account record if the
// account record exists, and the subscription does not. if the subscription does indeed exist and is
// inactive, it is reactivated.
func (db *Database) CreateUserSubscription(ctx context.Context, userID uint32, subscription model.Subscriptions) error {
transaction := func(tx *gorm.DB) error {
var (
userOrm *model.UserORM
subscriptionExist = false
)
// validate the subscription object
if subscription.SubscriptionName == "" ||
subscription.StartDate == nil ||
subscription.EndDate == nil ||
subscription.SubscriptionStatus == "" {
db.Logger.Error("invalid subscription. missing subscription name, start date, enddate, or status",
zap.Any("subscription", subscription))
return errors.New("invalid subscription")
}
// check and make sure the user account with the specified userid exists
exists, userOrm, err := db.GetUserIfExists(userID, "", "")
if !exists {
db.Logger.Error("user account does not exist", zap.Error(err))
return err
}
// convert the subscription object to an ORM type
subscriptionOrm, err := subscription.ToORM(ctx)
if err != nil {
db.Logger.Error("failed to convert subscription to orm type", zap.Error(err))
return err
}
subscriptions := make([]*model.SubscriptionsORM, len(userOrm.Subscriptions), len(userOrm.Subscriptions))
for _, oldSubscription := range userOrm.Subscriptions {
if oldSubscription.SubscriptionName == subscriptionOrm.SubscriptionName && !subscriptionExist {
// activate the subscription if it is not already active
oldSubscription.IsActive = true
oldSubscription.EndDate = subscriptionOrm.EndDate
subscriptionExist = true
db.Logger.Info("re-activating subscripion", zap.String("id", string(subscriptionOrm.Id)),
zap.String("name", subscription.SubscriptionName))
}
subscriptions = append(subscriptions, oldSubscription)
}
if !subscriptionExist {
subscriptions = append(subscriptions, &subscriptionOrm)
db.Logger.Info("new subscripion added to subscriptions list", zap.String("id", string(subscriptionOrm.Id)),
zap.String("name", subscription.SubscriptionName))
}
userOrm.Subscriptions = subscriptions
// save the user with the updated subscriptions
if err = tx.Save(&userOrm).Error; err != nil {
db.Logger.Error("failed to create user subscription", zap.Error(err))
return err
}
db.Logger.Info("successfully created user subscription")
return nil
}
return db.PerformTransaction(transaction)
}
// UpdateUser updates a user record if it already exists in the backend
func (db *Database) UpdateUser(ctx context.Context, userID uint32, user model.User) (*model.User, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
// first and foremost we check for the existence of the user
exists, _, err := db.GetUserIfExists(userID, "", "")
if !exists {
db.Logger.Error("failed to obtain user by id as user does not exist", zap.Error(err))
return nil, err
}
// convert the user to an ORM type
userOrm, err := user.ToORM(ctx)
if err != nil {
db.Logger.Error("failed to convert user object to orm type", zap.Error(err))
return nil, err
}
// update the actual user in the database
if err := tx.Save(&userOrm).Error; err != nil {
db.Logger.Error("failed to update user", zap.Error(err))
return nil, err
}
db.Logger.Info("Successfully updated user", zap.String("id", string(userOrm.Id)),
zap.String("user name", string(userOrm.UserName)))
return &user, nil
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return &model.User{}, err
}
updatedUser := output.(*model.User)
return updatedUser, nil
}
// UpdateUserSubscription updates a subscription tied to a user account if it exists
func (db *Database) UpdateUserSubscription(ctx context.Context, userID uint32, subscriptionID uint32, newSubscription model.Subscriptions) (*model.Subscriptions, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var (
userOrm model.UserORM
updatedSubscriptions []*model.SubscriptionsORM
)
// validate the new version of the subscription
if err := newSubscription.Validate(); err != nil {
db.Logger.Error("invalid subscription", zap.Error(err))
return nil, err
}
// convert the subscription to an ORM type
subscriptionOrm, err := newSubscription.ToORM(ctx)
if err != nil {
db.Logger.Error("failed to convert subscription to orm type", zap.Error(err))
return nil, err
}
// we check for the existence of the user account
// and the subscription
exist, _, err := db.GetSubscriptionExistById(userID, subscriptionID)
if !exist {
db.Logger.Error("subscription does not exist", zap.Error(err))
return nil, err
}
// obtain the user from the database
if err := tx.Where(model.UserORM{Id: userID}).First(&userOrm).Error; err != nil {
db.Logger.Error("failed to obtain user from backend database", zap.Error(err))
return nil, err
}
// update the subscription if an existing version exists in the database
for _, oldSubscription := range userOrm.Subscriptions {
if oldSubscription.SubscriptionName == subscriptionOrm.SubscriptionName {
updatedSubscriptions = append(updatedSubscriptions, &subscriptionOrm)
db.Logger.Info("updated subscription and added to subscription list")
} else {
updatedSubscriptions = append(updatedSubscriptions, oldSubscription)
}
}
// update the subscription list tied to the user
userOrm.Subscriptions = updatedSubscriptions
// update the actual user in the database
if err := tx.Save(&userOrm).Error; err != nil {
db.Logger.Error("failed to successfully update subscription", zap.Error(err))
return nil, err
}
db.Logger.Info("successfully updated subscription",
zap.String("userId", string(userID)), zap.String("subscriptionId", string(subscriptionID)))
return &newSubscription, nil
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
updatedSubscription := output.(*model.Subscriptions)
return updatedSubscription, nil
}
// UpdateUserProfile updates a user profile tied to a user account if such profile exists
func (db *Database) UpdateUserProfile(ctx context.Context, userID, profileID uint32, newProfile model.Profile) (*model.Profile, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var userOrm model.UserORM
// first and foremost we check for the existence of the user account
// and the profile
exist, _, err := db.GetUserProfileIfExists(userID)
if !exist {
db.Logger.Error("failed to obtain user profile as it does not exist", zap.Error(err))
return nil, err
}
// obtain the user from the database
if err := tx.Where(model.UserORM{Id: userID}).First(&userOrm).Error; err != nil {
db.Logger.Error("failed to obtain user", zap.Error(err))
return nil, err
}
// convert the profile to an ORM type
profileOrm, err := newProfile.ToORM(ctx)
if err != nil {
db.Logger.Error("failed to convert user profile to orm type", zap.Error(err))
return nil, err
}
// update the profile tied to the user
userOrm.Profile = &profileOrm
// update the actual user in the database
if err := tx.Save(&userOrm).Error; err != nil {
db.Logger.Error("failed to update user profile", zap.Error(err))
return nil, err
}
db.Logger.Info("successfully updated user profile",
zap.String("userId", string(userID)), zap.String("profileId", string(profileOrm.Id)))
return newProfile, nil
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
updatedProfile := output.(*model.Profile)
return updatedProfile, nil
}
// DeleteUser deletes a user account and any reference objects tied to the user
func (db *Database) DeleteUser(ctx context.Context, userID uint32) error {
transaction := func(tx *gorm.DB) error {
var userOrm model.UserORM
if userID == 0 {
db.Logger.Error("invalid user id")
return errors.New("invalid user id")
}
exist, _, err := db.GetUserIfExists(userID, "", "")
if !exist {
db.Logger.Error("failed to obtain user by id. user does not exist", zap.Error(err))
return err
}
if err = tx.Where(model.UserORM{Id: userID}).Delete(&userOrm).Error; err != nil {
db.Logger.Error("failed to successfully delete user account", zap.Error(err))
return err
}
db.Logger.Info("successfully deleted user account", zap.String("userId", string(userID)))
return nil
}
return db.PerformTransaction(transaction)
}
// DeleteUserProfile deletes a user profile tied to a user account
func (db *Database) DeleteUserProfile(ctx context.Context, userID, profileID uint32) error {
transaction := func(tx *gorm.DB) error {
// check the user of interest has a profile to even delete
exist, profile, err := db.GetUserProfileIfExists(userID)
if !exist {
db.Logger.Error("failed to obtain user profile as it does not exist", zap.Error(err))
return err
}
// attempt deletion of the profile
if err = tx.Where(model.ProfileORM{Id: profileID}).Delete(&profile).Error; err != nil {
db.Logger.Error("failed to delete user profile", zap.Error(err))
return err
}
db.Logger.Info("successfully deleted user Profile",
zap.String("userId", string(userID)), zap.String("profileId", string(profileID)))
return nil
}
return db.PerformTransaction(transaction)
}
// DeleteUserSubscription deletes a subscription tied to a user account
func (db *Database) DeleteUserSubscription(ctx context.Context, userID, subscriptionID uint32) error {
transaction := func(tx *gorm.DB) error {
// get a subscription by id
exist, _, err := db.GetSubscriptionExistById(userID, subscriptionID)
if !exist {
db.Logger.Error("failed to get subscription by id as it does not exist", zap.Error(err))
return err
}
// delete from subscriptions table in db where id == subscriptions id
if err = tx.Where(model.SubscriptionsORM{Id: subscriptionID}).Delete(&model.SubscriptionsORM{}).Error; err != nil {
db.Logger.Error("failed to delete subscription", zap.Error(err))
return err
}
db.Logger.Info("successfully deleted subscription",
zap.String("userId", string(userID)), zap.String("subscriptionId", string(subscriptionID)))
return nil
}
return db.PerformTransaction(transaction)
}
// GetAllUsers obtains user records. The max number of records returned is defined
// by the limit input parameter.
func (db *Database) GetAllUsers(ctx context.Context, limit uint32) ([]model.User, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var users = make([]model.User, limit, limit+1)
var iteration uint32 = 0
rows, err := tx.Limit(limit).Model(&model.UserORM{}).Rows()
defer rows.Close()
if err != nil {
db.Logger.Error("failed to obtain set of user object from the database", zap.Error(err))
return nil, err
}
for rows.Next() {
var entry model.UserORM
// scan the data that the row pointer points to into a userOrm object
if err := tx.ScanRows(rows, &entry); err != nil {
db.Logger.Error("failed to scan the returned row", zap.Error(err))
return nil, err
}
user, err := entry.ToPB(ctx)
if err != nil {
db.Logger.Error("failed to convert orm type to pb type", zap.Error(err))
return nil, err
}
if err = user.Validate(); err != nil {
db.Logger.Error("user object failed validation check", zap.Error(err))
return nil, err
}
users = append(users, user)
iteration++
if iteration == limit {
break
}
}
db.Logger.Info("successfully obtained users from backend database")
return users, rows.Close()
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
users := output.([]model.User)
return users, nil
}
// GetAllUsersByAccountType obtains user records by account type. The upper bound on the number of
// user records returned is defined by the limit input parameter.
func (db *Database) GetAllUsersByAccountType(ctx context.Context, accountType string, limit uint32) ([]model.User, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var (
users = make([]model.User, limit)
)
rows, err := tx.Limit(limit).Where(model.UserORM{UserAccountType: accountType}).Model(&model.UserORM{}).Rows()
if err != nil {
db.Logger.Error("failed to obtain users by accounttypes", zap.Error(err))
return nil, err
}
defer rows.Close()
for rows.Next() {
entry := model.UserORM{}
// scan the data that the row pointer points to into a userOrm object
if err := tx.ScanRows(rows, &entry); err != nil {
db.Logger.Error("failed to scan row", zap.Error(err))
return nil, err
}
user, err := entry.ToPB(ctx)
if err != nil {
db.Logger.Error("failed to convert user orm type to pb type", zap.Error(err))
return nil, err
}
if err = user.Validate(); err != nil {
db.Logger.Error("user validation failed", zap.Error(err))
return nil, err
}
users = append(users, user)
}
db.Logger.Info("successfully obtained users by account type")
return users, rows.Close()
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
users := output.([]model.User)
return users, nil
}
// GetAllUsersByIntent queries the database for all user records based on intent. The
// upper bound on the number of user records returned is defined by the limit input parameter.
func (db *Database) GetAllUsersByIntent(ctx context.Context, intent string, limit uint32) ([]model.User, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var users = make([]model.User, limit, limit+10)
rows, err := tx.Limit(limit).Where(model.UserORM{Intent: intent}).Model(&model.UserORM{}).Rows()
if err != nil {
db.Logger.Error("failed to obtain users by intent", zap.Error(err))
return nil, err
}
defer rows.Close()
for rows.Next() {
entry := model.UserORM{}
// scan the data that the row pointer points to into a userOrm object
if err := tx.ScanRows(rows, &entry); err != nil {
db.Logger.Error("failed to scan row", zap.Error(err))
return nil, err
}
user, err := entry.ToPB(ctx)
if err != nil {
db.Logger.Error("failed to convert user orm type to pb type", zap.Error(err))
return nil, err
}
if err = user.Validate(); err != nil {
db.Logger.Error("user validation call failed", zap.Error(err))
return nil, err
}
users = append(users, user)
}
db.Logger.Info("successfully obtained user by intent")
return users, rows.Close()
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
users := output.([]model.User)
return users, nil
}
// GetUserProfile queries the database for a user profile record based on user account id
func (db *Database) GetUserProfile(ctx context.Context, userID uint32) (model.Profile, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
// obtain the user of interest
user, err := db.GetUserByID(ctx, userID)
if err != nil {
db.Logger.Error("failed to obtain user account by id", zap.Error(err))
return nil, err
}
// validate that the user has all necessary fields
if err = user.Validate(); err != nil {
db.Logger.Error("user validation failed", zap.Error(err))
return nil, err
}
// from the user object extract the profile object
profile := user.Profile
if profile != nil {
if err = profile.Validate(); err != nil {
db.Logger.Error("user profile validation failed", zap.Error(err))
return nil, err
}
db.Logger.Info("profile does not exist")
// return the profile object
return *profile, nil
}
db.Logger.Info("successfully returned user profile")
return model.Profile{}, errors.New("user profile does not exist")
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return model.Profile{}, err
}
profileData := output.(model.Profile)
return profileData, nil
}
// GetAllUserProfilesByType queries the database and returns all user profile records
// with a specified profile type. The upper bound on the number of records to return
// is defined by the limit input parameter.
func (db *Database) GetAllUserProfilesByType(ctx context.Context, profileType string, limit uint32) ([]model.Profile, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var profiles = make([]model.Profile, limit, limit+10)
rows, err := tx.Limit(limit).Where(model.ProfileORM{ProfileType: profileType}).Model(&model.ProfileORM{}).Rows()
if err != nil {
db.Logger.Error("failed to obtain user by profile type")
return nil, err
}
defer rows.Close()
// obtain the value of interest from the rows returned by the query
for rows.Next() {
entry := model.ProfileORM{}
// scan the data that the row pointer points to into a userOrm object
if err := tx.ScanRows(rows, &entry); err != nil {
db.Logger.Error("failed to scan rows", zap.Error(err))
return nil, err
}
profile, err := entry.ToPB(ctx)
if err != nil {
db.Logger.Error("failed to convert user orm type to pb type", zap.Error(err))
return nil, err
}
if err = profile.Validate(); err != nil {
db.Logger.Error("user profile validation failed", zap.Error(err))
return nil, err
}
profiles = append(profiles, profile)
}
db.Logger.Info("successfully returned user profile by profile types")
return profiles, rows.Close()
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
profileData := output.([]model.Profile)
return profileData, nil
}
// GetAllUserProfiles queries the database and returns a set of user profile records.
// The upper bound on the number of records returned is defined by the limit
// input parameter.
func (db *Database) GetAllUserProfiles(ctx context.Context, limit uint32) ([]model.Profile, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var profiles = make([]model.Profile, limit, limit+10)
rows, err := tx.Limit(limit).Model(&model.ProfileORM{}).Rows()
if err != nil {
db.Logger.Error(err.Error())
return nil, err
}
defer rows.Close()
data, err := extractFromRows(rows, tx, model.ProfileORM{})
if err != nil {
db.Logger.Error(err.Error())
return nil, err
}
profilesOrm := make([]model.ProfileORM, len(data))
for _, val := range data {
profilesOrm = append(profilesOrm, val.(model.ProfileORM))
}
for _, profileOrm := range profilesOrm {
// convert to object type and validate
profile, err := profileOrm.ToPB(ctx)
if err != nil {
db.Logger.Error(err.Error())
return nil, err
}
if err = profile.Validate(); err != nil {
db.Logger.Error(err.Error())
return nil, err
}
profiles = append(profiles, profile)
}
db.Logger.Info("successfully obtained user profiles")
return profiles, rows.Close()
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
profileData := output.([]model.Profile)
return profileData, nil
}
// GetAllUserProfilesByNationality queries the database for a list of profiles
// of a certain nationality. The maximal amount of records to return is defined
// by the limit input parameter
func (db *Database) GetAllUserProfilesByNationality(ctx context.Context, nationality string, limit uint32) ([]model.Profile, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var profiles = make([]model.Profile, limit, limit+10)
// get all profiles by profile type by querying the database
rows, err := tx.Limit(limit).Where(model.ProfileORM{Nationality: nationality}).Model(&model.ProfileORM{}).Rows()
if err != nil {
db.Logger.Error(err.Error())
return nil, err
}
defer rows.Close()
// obtain the value of interest from the rows returned by the query
for rows.Next() {
entry := model.ProfileORM{}
// scan the data that the row pointer points to into a userOrm object
if err := tx.ScanRows(rows, &entry); err != nil {
db.Logger.Error(err.Error())
return nil, err
}
profile, err := entry.ToPB(ctx)
if err != nil {
db.Logger.Error(err.Error())
return nil, err
}
if err = profile.Validate(); err != nil {
db.Logger.Error(err.Error())
return nil, err
}
profiles = append(profiles, profile)
}
db.Logger.Info("Successfully obtained user profiles by nationality")
return profiles, rows.Close()
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
profileData := output.([]model.Profile)
return profileData, nil
}
// GetAllUserSubscriptions queries the database for a user based off of the
// user id and returns a set of subscriptions the user has
func (db *Database) GetAllUserSubscriptions(ctx context.Context, user_id uint32) ([]model.Subscriptions, error) {
transaction := func(tx *gorm.DB) (interface{}, error) {
var subscriptions = make([]model.Subscriptions, 10, 20)
user, err := db.GetUserByID(ctx, user_id)
if err != nil {
return nil, err
}
for _, subscription := range user.GetSubscriptions() {
subscriptions = append(subscriptions, *subscription)
}
return subscriptions, nil
}
output, err := db.PerformComplexTransaction(transaction)
if err != nil {
return nil, err
}
subscriptions := output.([]model.Subscriptions)
return subscriptions, nil
}
// extractFromRows operates on database rows returned by a given query. It transforms the
// obtains row values into an ORM type and returns any errors that may have occurred throughout
// the lifecycle of the function call as well as an abstract type tied to the list of ORM
// types obtained from all rows
func extractFromRows(rows *sql.Rows, tx *gorm.DB, dbModel interface{}) ([]interface{}, error) {
var data = make([]interface{}, 20)
defer rows.Close()
// iterate over all rows obtained from the query
for rows.Next() {
// scan the data that the row pointer points to into a userOrm object
if err := tx.ScanRows(rows, &dbModel); err != nil {
return nil, err
}
data = append(data, dbModel)
}
// we call rows.close() again to reduce any potential
// side effects that may arise. It is much safer to reclose
// a database handle than to potential leave it open as a sideffect
// of some mishandled error
return data, rows.Close()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment