Files
api-antrian-operasi/internal/domain/access/repository.go
T

352 lines
8.3 KiB
Go

package access
import (
"antrian-operasi/internal/database"
queryUtils "antrian-operasi/internal/utils/query"
"context"
"database/sql"
"log"
"time"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
)
const DB_NAME = "db_role_access"
const TBL_USER = "role_users"
const TBL_PERMISSION = "role_permission"
const TBL_USER_PERMISSION = "role_user_permission"
type IAccessRepository interface {
FindUserByKeycloakId(c context.Context, id string) ([]RoleUserModel, error)
CreateUserPermission(c context.Context, req SyncKeycloakRoleRequest) error
UpdateUserPermission(c context.Context, userId string, req SyncKeycloakRoleRequest) error
}
type accessRepo struct {
queryBuilder *queryUtils.QueryBuilder
db database.Service
}
func NewRepository(dbService database.Service) IAccessRepository {
queryBuilder := queryUtils.NewQueryBuilder(queryUtils.DBTypePostgreSQL).SetAllowedColumns([]string{
"id", "name", "is_active", "keycloak_id", "id_user", "id_permission", "email", "created_at",
}).SetAllowedTables([]string{TBL_USER})
queryBuilder.SetSecurityOptions(false, 100)
return accessRepo{
queryBuilder: queryBuilder,
db: dbService,
}
}
func (r accessRepo) FindUserByKeycloakId(c context.Context, id string) ([]RoleUserModel, error) {
var result []RoleUserModel
query := queryUtils.DynamicQuery{
From: TBL_USER,
Fields: []queryUtils.SelectField{
{Expression: "id"},
{Expression: "keycloak_id"},
},
Filters: []queryUtils.FilterGroup{
{
Filters: []queryUtils.DynamicFilter{
{Column: "keycloak_id", Operator: queryUtils.OpEqual, Value: id},
}, LogicOp: "AND",
},
},
}
dbconn, err := r.db.GetSQLXDB(DB_NAME)
if err != nil {
log.Printf("Unable to connect db : %s", err)
return result, err
}
err = r.queryBuilder.ExecuteQuery(c, dbconn, query, &result)
if err != nil {
log.Printf("Unable to execute query search user : %s", err)
return result, err
}
return result, err
}
func (r accessRepo) CreateUserPermission(c context.Context, req SyncKeycloakRoleRequest) error {
db, err := r.db.GetSQLXDB(DB_NAME)
if err != nil {
return err
}
// START TRANSACTION
tx, err := db.BeginTx(c, nil)
if err != nil {
return err
}
// insert user
userId, err := r.insertUser(c, tx, req)
if err != nil {
tx.Rollback()
return err
}
err = r.getOrCreateUserPermission(c, db, tx, userId, req.Roles)
if err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
func (r accessRepo) UpdateUserPermission(c context.Context, userId string, req SyncKeycloakRoleRequest) error {
db, err := r.db.GetSQLXDB(DB_NAME)
if err != nil {
return err
}
// START TRANSACTION
tx, err := db.BeginTx(c, nil)
if err != nil {
return err
}
// delete all user permission
err = r.deletePermissionByUserId(c, tx, userId)
if err != nil {
tx.Rollback()
return err
}
err = r.getOrCreateUserPermission(c, db, tx, userId, req.Roles)
if err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
// PRIVATE FUNCTIONS
// Table user function
func (r accessRepo) insertUser(c context.Context, tx *sql.Tx, req SyncKeycloakRoleRequest) (string, error) {
newUserId := uuid.New().String()
insertUserQuery := queryUtils.InsertData{
Columns: []string{
"id", "name", "email", "keycloak_id", "created_at",
},
Values: []interface{}{
newUserId, req.Username, req.Email, req.KeycloakId, time.Now(),
},
}
returningCols := []string{
"id",
}
sql, args, err := r.queryBuilder.BuildInsertQuery(TBL_USER, insertUserQuery, returningCols...)
if err != nil {
log.Printf("error building query create user %s", err)
return newUserId, err
}
_, err = tx.ExecContext(c, sql, args...)
if err != nil {
log.Printf("error executing query create user %s", err)
return newUserId, err
}
log.Printf(sql, args)
log.Printf("success insert user")
return newUserId, nil
}
// End Table user function
// Table permission functions
func (r accessRepo) findPermissionByRoleName(c context.Context, dbconn *sqlx.DB, roles []string) ([]RolePermissionModel, error) {
var result []RolePermissionModel
query := queryUtils.DynamicQuery{
From: TBL_PERMISSION,
Fields: []queryUtils.SelectField{
{Expression: "id"},
{Expression: "name"},
},
Filters: []queryUtils.FilterGroup{
{
Filters: []queryUtils.DynamicFilter{
{Column: "name", Operator: queryUtils.OpIn, Value: roles},
}, LogicOp: "AND",
},
},
}
err := r.queryBuilder.ExecuteQuery(c, dbconn, query, &result)
if err != nil {
return result, err
}
return result, nil
}
func (r accessRepo) insertPermissionByRoleName(c context.Context, tx *sql.Tx, newRoles []string) ([]string, error) {
var idRoles []string
if len(newRoles) > 0 {
var valuePermission [][]interface{}
for _, role := range newRoles {
id := uuid.New().String()
itemValues := []interface{}{id, role, false}
idRoles = append(idRoles, id)
valuePermission = append(valuePermission, itemValues)
}
insertPermissionQuery := queryUtils.InsertBulkData{
Columns: []string{
"id", "name", "is_active",
},
Values: valuePermission,
}
returningCols := []string{
"id",
}
sql, args, err := r.queryBuilder.BuildBulkInsertQuery(TBL_PERMISSION, insertPermissionQuery, returningCols...)
if err != nil {
log.Printf("error building query insert new permission %s", err)
return idRoles, err
}
_, err = tx.ExecContext(c, sql, args...)
if err != nil {
log.Println(err)
return idRoles, err
}
log.Printf(sql, args)
log.Printf("success insert new permission")
}
return idRoles, nil
}
// END table permission functions
// Table user permission functions
func (r accessRepo) deletePermissionByUserId(c context.Context, tx *sql.Tx, userId string) error {
filters := []queryUtils.FilterGroup{
{
Filters: []queryUtils.DynamicFilter{
{Column: "id_user", Operator: queryUtils.OpEqual, Value: userId},
},
},
}
sql, args, err := r.queryBuilder.BuildDeleteQuery(TBL_USER_PERMISSION, filters)
if err != nil {
log.Printf("Unable to create delete user permission query : %v", err)
return err
}
_, err = tx.ExecContext(c, sql, args...)
if err != nil {
log.Printf("Unable to executing delete user permission : %v", err)
return err
}
return nil
}
func (r accessRepo) insertUserPermission(c context.Context, tx *sql.Tx, userId string, roleIds []string) error {
if len(roleIds) > 0 {
var valueUserPermission [][]interface{}
for _, roleId := range roleIds {
id := uuid.New().String()
itemValues := []interface{}{id, userId, roleId}
valueUserPermission = append(valueUserPermission, itemValues)
}
insertUserPermissionQuery := queryUtils.InsertBulkData{
Columns: []string{
"id", "id_user", "id_permission",
},
Values: valueUserPermission,
}
returningCols := []string{
"id",
}
sql, args, err := r.queryBuilder.BuildBulkInsertQuery(TBL_USER_PERMISSION, insertUserPermissionQuery, returningCols...)
if err != nil {
log.Printf("error building query insert user permission %s", err)
return err
}
_, err = tx.ExecContext(c, sql, args...)
if err != nil {
log.Println(err)
return err
}
log.Printf(sql, args)
log.Printf("success insert user permission")
}
return nil
}
func (r accessRepo) getOrCreateUserPermission(c context.Context, db *sqlx.DB, tx *sql.Tx, userId string, roles []string) error {
var permissionIds []string
mapPermissionExist := make(map[string]string)
var newRoles []string
// fetch permission by name, append the permission ids
fetchRoles, err := r.findPermissionByRoleName(c, db, roles)
if err != nil {
return err
}
if len(fetchRoles) > 0 {
for _, item := range fetchRoles {
permissionIds = append(permissionIds, item.ID)
mapPermissionExist[item.Name] = item.ID
}
}
// if permission not exist, insert non-existed permission first then append the permission ids
for _, role := range roles {
if mapPermissionExist[role] == "" {
newRoles = append(newRoles, role)
}
}
// insert new , non existed permission
if len(newRoles) > 0 {
newRoleIds, err := r.insertPermissionByRoleName(c, tx, newRoles)
if err != nil {
return err
} else { // append new role ids to permission ids
permissionIds = append(permissionIds, newRoleIds...)
}
}
// insert all permission to user
err = r.insertUserPermission(c, tx, userId, permissionIds)
if err != nil {
return err
}
return nil
}
// End Table user permission functions