github.com/angryronald/go-kit@v0.0.0-20240505173814-ff2bd9c79dbf/generic/repository/sql/generic.repository.mutable.go (about)

     1  package sql
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"gorm.io/gorm"
     8  
     9  	"github.com/google/uuid"
    10  
    11  	"github.com/angryronald/go-kit/appcontext"
    12  	"github.com/angryronald/go-kit/cast"
    13  	"github.com/angryronald/go-kit/generic/repository"
    14  )
    15  
    16  type GenericRepository struct {
    17  	db *gorm.DB
    18  }
    19  
    20  func (r *GenericRepository) FindAll(ctx context.Context, params map[string]interface{}, conditionalOperations []repository.ConditionalOperation, relationalOperations []repository.RelationalOperation, page int, limit int, result interface{}) (interface{}, error) {
    21  	if !repository.IsValidOperations(conditionalOperations, relationalOperations, params) {
    22  		return nil, repository.ErrBadParameters
    23  	}
    24  
    25  	where := fmt.Sprintf(`"deleted_at" %s %s`, repository.IS, "NULL")
    26  	i := 0
    27  	args := []interface{}{}
    28  	if len(params) > 0 {
    29  		where = fmt.Sprintf(`%s %s`, where, repository.AND)
    30  	}
    31  	for k, v := range params {
    32  		switch conditionalOperations[i] {
    33  		case repository.IN:
    34  			encapsulation := ""
    35  			if !repository.AreAllNumbers(v.([]interface{})) {
    36  				encapsulation = "'"
    37  			}
    38  			where = fmt.Sprintf(`%s %s %s (%s)`, where, k, conditionalOperations[i], repository.StringJoin(v.([]interface{}), encapsulation))
    39  
    40  			break
    41  
    42  		case repository.ILIKE, repository.LIKE:
    43  			where = fmt.Sprintf(`%s %s %s ?`, where, k, conditionalOperations[i])
    44  			args = append(args, fmt.Sprintf("%%%s%%", v))
    45  			break
    46  
    47  		default:
    48  			where = fmt.Sprintf(`%s %s %s ?`, where, k, conditionalOperations[i])
    49  			args = append(args, v)
    50  		}
    51  
    52  		if i < len(relationalOperations) {
    53  			where = fmt.Sprintf(`%s %s`, where, relationalOperations[i])
    54  		}
    55  		i++
    56  	}
    57  	order := fmt.Sprintf(`"created_at" %s`, repository.DESC)
    58  
    59  	if page == 0 && limit == 0 {
    60  		if err := r.db.Where(
    61  			where,
    62  			args...,
    63  		).Order(order).Find(&result).Error; err != nil {
    64  			if err == gorm.ErrRecordNotFound {
    65  				return nil, repository.ErrNotFound
    66  			}
    67  			return nil, err
    68  		}
    69  
    70  		return result, nil
    71  	}
    72  
    73  	if err := r.db.Where(
    74  		where,
    75  		args...,
    76  	).Order(order).
    77  		Offset((page - 1) * limit).
    78  		Limit(limit).
    79  		Find(&result).Error; err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return result, nil
    84  }
    85  
    86  func (r *GenericRepository) FindOne(ctx context.Context, key string, value interface{}, result interface{}) (interface{}, error) {
    87  	where := fmt.Sprintf(`"deleted_at" %s NULL %s %s %s ?`, repository.IS, repository.AND, key, repository.EQUAL_WITH)
    88  	args := []interface{}{
    89  		value,
    90  	}
    91  	if err := r.db.Where(
    92  		where,
    93  		args,
    94  	).First(&result).Error; err != nil {
    95  		if err == gorm.ErrRecordNotFound {
    96  			return nil, repository.ErrNotFound
    97  		}
    98  		return nil, err
    99  	}
   100  
   101  	return result, nil
   102  }
   103  
   104  func (r *GenericRepository) FindByID(ctx context.Context, id uuid.UUID, result interface{}) (interface{}, error) {
   105  	where := fmt.Sprintf(`"deleted_at" %s NULL %s "id" %s ?`, repository.IS, repository.AND, repository.EQUAL_WITH)
   106  	args := []interface{}{
   107  		id,
   108  	}
   109  	if err := r.db.Where(
   110  		where,
   111  		args,
   112  	).First(&result).Error; err != nil {
   113  		if err == gorm.ErrRecordNotFound {
   114  			return nil, repository.ErrNotFound
   115  		}
   116  		return nil, err
   117  	}
   118  
   119  	return result, nil
   120  }
   121  
   122  func (r *GenericRepository) Insert(ctx context.Context, data interface{}) (interface{}, error) {
   123  	newID, err := uuid.NewRandom()
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	if err = repository.UpdatePropertyValue(data, "ID", newID); err != nil {
   128  		return nil, err
   129  	}
   130  	if err = repository.UpdatePropertyValue(data, "CreatedBy", appcontext.UserID(ctx)); err != nil {
   131  		return nil, err
   132  	}
   133  	if err = repository.UpdatePropertyValue(data, "UpdatedBy", appcontext.UserID(ctx)); err != nil && err != repository.ErrPropertyNotFound {
   134  		return nil, err
   135  	}
   136  
   137  	if err := r.db.Create(data).Error; err != nil {
   138  		if err == gorm.ErrDuplicatedKey {
   139  			return nil, repository.ErrConflict
   140  		}
   141  		return nil, err
   142  	}
   143  	return data, nil
   144  }
   145  
   146  func (r *GenericRepository) Update(ctx context.Context, data interface{}) (interface{}, error) {
   147  	var err error
   148  
   149  	newData, _ := repository.CopyObject(data)
   150  	ID, _ := repository.GetStructPropertyAsString(newData, "ID")
   151  
   152  	if _, err = r.FindByID(ctx, uuid.MustParse(ID), newData); err != nil {
   153  		return nil, repository.ErrNotFound
   154  	}
   155  
   156  	if err = repository.UpdatePropertyValue(data, "UpdatedBy", appcontext.UserID(ctx)); err != nil && err != repository.ErrPropertyNotFound {
   157  		return nil, err
   158  	}
   159  
   160  	if err = r.db.Save(data).Error; err != nil {
   161  		return nil, err
   162  	}
   163  	return data, nil
   164  }
   165  
   166  func (r *GenericRepository) Delete(ctx context.Context, data interface{}) (interface{}, error) {
   167  	userID := appcontext.UserID(ctx)
   168  	if err := repository.UpdatePropertyValue(data, "DeletedBy", &userID); err != nil && err != repository.ErrPropertyNotFound {
   169  		return nil, err
   170  	}
   171  
   172  	if err := r.db.Delete(data).Error; err != nil {
   173  		return nil, err
   174  	}
   175  	return data, nil
   176  }
   177  
   178  func (r *GenericRepository) Upsert(ctx context.Context, data interface{}) (interface{}, error) {
   179  	var currentID string
   180  	var err error
   181  	currentID, _ = repository.GetStructPropertyAsString(data, "ID")
   182  	if currentID == "" {
   183  		newID, err := uuid.NewRandom()
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  
   188  		if err = repository.UpdatePropertyValue(data, "ID", newID); err != nil {
   189  			return nil, err
   190  		}
   191  		if err = repository.UpdatePropertyValue(data, "CreatedBy", appcontext.UserID(ctx)); err != nil {
   192  			return nil, err
   193  		}
   194  	}
   195  
   196  	if err = repository.UpdatePropertyValue(data, "UpdatedBy", appcontext.UserID(ctx)); err != nil && err != repository.ErrPropertyNotFound {
   197  		return nil, err
   198  	}
   199  
   200  	if err := r.db.Save(data).Error; err != nil {
   201  		if err == gorm.ErrDuplicatedKey {
   202  			return nil, repository.ErrConflict
   203  		}
   204  		return nil, err
   205  	}
   206  	return data, nil
   207  }
   208  
   209  func (r *GenericRepository) BulkInsert(ctx context.Context, data interface{}) (interface{}, error) {
   210  	tx := r.db.Begin()
   211  	defer func() {
   212  		if r := recover(); r != nil {
   213  			tx.Rollback()
   214  		}
   215  	}()
   216  
   217  	copyOfData, err := repository.CopySliceOfPointers(data)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	dataInArray := cast.StructPointerArrayToInterfacePointerArray(copyOfData)
   223  	for i := 0; i < len(dataInArray); i++ {
   224  		newID, err := uuid.NewRandom()
   225  		if err != nil {
   226  			return nil, err
   227  		}
   228  
   229  		if err = repository.UpdatePropertyValue(dataInArray[i], "ID", newID); err != nil {
   230  			return nil, err
   231  		}
   232  		if err = repository.UpdatePropertyValue(dataInArray[i], "CreatedBy", appcontext.UserID(ctx)); err != nil {
   233  			return nil, err
   234  		}
   235  		if err = repository.UpdatePropertyValue(dataInArray[i], "UpdatedBy", appcontext.UserID(ctx)); err != nil {
   236  			return nil, err
   237  		}
   238  	}
   239  
   240  	dataInArrayWithIdentity, err := repository.CopySliceOfPointersWithIdentitySeparated(copyOfData, dataInArray)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	if err := r.db.Create(dataInArrayWithIdentity).Error; err != nil {
   246  		tx.Rollback()
   247  		if err == gorm.ErrDuplicatedKey {
   248  			return nil, repository.ErrConflict
   249  		}
   250  		return nil, err
   251  	}
   252  	return dataInArrayWithIdentity, tx.Commit().Error
   253  }
   254  
   255  func (r *GenericRepository) BulkUpsert(ctx context.Context, data interface{}) (interface{}, error) {
   256  	tx := r.db.Begin()
   257  	defer func() {
   258  		if r := recover(); r != nil {
   259  			tx.Rollback()
   260  		}
   261  	}()
   262  
   263  	copyOfData, err := repository.CopySliceOfPointers(data)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	dataInArray := cast.StructPointerArrayToInterfacePointerArray(copyOfData)
   269  	for i := 0; i < len(dataInArray); i++ {
   270  		var currentID string
   271  		var err error
   272  		currentID, _ = repository.GetStructPropertyAsString(data, "ID")
   273  		if currentID == "" {
   274  			newID, err := uuid.NewRandom()
   275  			if err != nil {
   276  				return nil, err
   277  			}
   278  
   279  			if err = repository.UpdatePropertyValue(dataInArray[i], "ID", newID); err != nil {
   280  				return nil, err
   281  			}
   282  			if err = repository.UpdatePropertyValue(dataInArray[i], "CreatedBy", appcontext.UserID(ctx)); err != nil {
   283  				return nil, err
   284  			}
   285  		}
   286  		if err = repository.UpdatePropertyValue(dataInArray[i], "UpdatedBy", appcontext.UserID(ctx)); err != nil {
   287  			return nil, err
   288  		}
   289  	}
   290  
   291  	dataInArrayWithIdentity, err := repository.CopySliceOfPointersWithIdentitySeparated(copyOfData, dataInArray)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	if err := tx.Save(dataInArrayWithIdentity).Error; err != nil {
   297  		tx.Rollback()
   298  		if err == gorm.ErrDuplicatedKey {
   299  			return nil, repository.ErrConflict
   300  		}
   301  		return nil, err
   302  	}
   303  
   304  	return dataInArrayWithIdentity, tx.Commit().Error
   305  }
   306  
   307  func (r *GenericRepository) Query(ctx context.Context, query string, params []interface{}, result interface{}) (interface{}, error) {
   308  	if err := r.db.Raw(query, params...).Find(&result).Error; err != nil {
   309  		if err == gorm.ErrRecordNotFound {
   310  			return nil, repository.ErrNotFound
   311  		}
   312  		return nil, err
   313  	}
   314  
   315  	return result, nil
   316  }
   317  
   318  func NewRepository(db *gorm.DB) repository.GenericRepositoryInterface {
   319  	return &GenericRepository{
   320  		db: db,
   321  	}
   322  }
   323  
   324  func NewMutableRepository(db *gorm.DB) repository.MutableGenericRepositoryInterface {
   325  	return &GenericRepository{
   326  		db: db,
   327  	}
   328  }