Skip to content

Commit

Permalink
implement use of transactions instead of raw operations
Browse files Browse the repository at this point in the history
Signed-off-by: Rajiv Harlalka <[email protected]>
  • Loading branch information
rajivharlalka committed Oct 8, 2024
1 parent 7afaaa9 commit 2077726
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 20 deletions.
58 changes: 46 additions & 12 deletions backend/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,17 @@ func HandleQPYear(w http.ResponseWriter, r *http.Request) {
func HandleApprovePaper(w http.ResponseWriter, r *http.Request) {
approverUsername := r.Context().Value(CLAIMS_KEY).(*Claims).Username

db := db.GetDB()
tx, err := db.BeginTransaction()
if err != nil {
sendErrorResponse(w, http.StatusInternalServerError, "could not process request, try again later", nil)
config.Get().Logger.Errorf("HandleApprovePaper: could not start transaction: %+v", err.Error())
return
}

var qpDetails models.QuestionPaper
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&qpDetails); err != nil {
defer tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusInternalServerError, "Could not find Question Paper, Try Later!", nil)
config.Get().Logger.Errorf("HandleApprovePaper: could not approve paper, invalid Body: %+v", err.Error())
return
Expand All @@ -74,8 +81,9 @@ func HandleApprovePaper(w http.ResponseWriter, r *http.Request) {
srcFile := filepath.Join(config.Get().StaticFilesStorageLocation, qpDetails.FileLink)
destFile := utils.SanitizeFileLink(filepath.Join(config.Get().StaticFilesStorageLocation, destFileLink))

err := utils.CopyFile(srcFile, destFile)
err = utils.CopyFile(srcFile, destFile)
if err != nil {
defer tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusInternalServerError, "Could not move Question Paper, Try Later!", nil)
config.Get().Logger.Errorf("HandleApprovePaper: could not approve paper, could not move question paper: %+v", err.Error())
return
Expand All @@ -93,24 +101,27 @@ func HandleApprovePaper(w http.ResponseWriter, r *http.Request) {
ApprovedBy: approverUsername,
}

id, err := db.InsertNewPaper(&newQPDetails)
id, err := tx.InsertNewPaper(&newQPDetails)
if err != nil {
// undelete file
utils.DeleteFile(destFile)
defer tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusInternalServerError, "could not update file details, try again later", nil)
config.Get().Logger.Errorf("HandleApprovePaper: Could not approve paper: %+v", err.Error())
return
}
// log line to help which entry was made by deleting which paper for recovery
config.Get().Logger.Infof("HandleApprovePaper: Id %d added against Id %d", id, qpDetails.ID)

err = db.MarkPaperAsSoftDeletedAndUnApprove(qpDetails.ID, approverUsername)
err = tx.MarkPaperAsSoftDeletedAndUnApprove(qpDetails.ID, approverUsername)
if err != nil {
defer tx.Tx.Rollback(context.Background())
utils.DeleteFile(destFile)
sendErrorResponse(w, http.StatusInternalServerError, "error updating paper details!", nil)
config.Get().Logger.Errorf("HandleApprovePaper: error soft-deleting paper: %+v PaperDetails: %d", err.Error(), qpDetails.ID)
config.Get().Logger.Errorf("HandleApprovePaper: error soft-deleting paper: %+v PaperDetails: %d, id: %d rolledback", err.Error(), qpDetails.ID, id)
return
}
defer tx.Tx.Commit(context.Background())
sendResponse(w, http.StatusOK, httpResp{Message: "File Approved successfully"})
}

Expand Down Expand Up @@ -189,14 +200,21 @@ func HandleQPSearch(w http.ResponseWriter, r *http.Request) {
}

func ListAllPapers(w http.ResponseWriter, r *http.Request) {
db := db.GetDB()
tx, err := db.BeginTransaction()
if err != nil {
sendErrorResponse(w, http.StatusInternalServerError, "could not process request, try again later", nil)
config.Get().Logger.Errorf("HandleApprovePaper: could not start transaction: %+v", err.Error())
return
}

qps, err := db.FetchAllQuestionPapers()
qps, err := tx.FetchAllQuestionPapers()
if err != nil {
tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusInternalServerError, err.Error(), nil)
return
}

tx.Tx.Commit(context.Background())
config.Get().Logger.Info("listUnapprovedPapers: Unapproved Question paper count: ", len(qps))
sendResponse(w, http.StatusOK, qps)
}
Expand Down Expand Up @@ -359,12 +377,17 @@ func HandleProfile(w http.ResponseWriter, r *http.Request) {

func HandleFetchSimilarPapers(w http.ResponseWriter, r *http.Request) {
queryParams := r.URL.Query()
var err error
db := db.GetDB()
tx, err := db.BeginTransaction()
if err != nil {
sendErrorResponse(w, http.StatusInternalServerError, "could not process request, try again later", nil)
config.Get().Logger.Errorf("HandleApprovePaper: could not start transaction: %+v", err.Error())
return
}

var parsedParams models.QuestionPaper
// parse Query Params
if !queryParams.Has("course_code") {
tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusBadRequest, "query parameter course_code is mandatory", nil)
config.Get().Logger.Errorf("HandleSimilarPapers: query param 'course_code' not received")
return
Expand All @@ -373,6 +396,7 @@ func HandleFetchSimilarPapers(w http.ResponseWriter, r *http.Request) {
if queryParams.Has("year") {
parsedParams.Year, err = strconv.Atoi(queryParams.Get("year"))
if err != nil {
tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusBadRequest, "query parameter year has to be integer", nil)
config.Get().Logger.Errorf("HandleSimilarPapers: query param 'year' received a non-int value")
return
Expand All @@ -387,35 +411,45 @@ func HandleFetchSimilarPapers(w http.ResponseWriter, r *http.Request) {
parsedParams.Exam = queryParams.Get("exam")
}
fmt.Print(parsedParams)
questionPapers, err := db.GetQuestionPaperWithExactMatch(&parsedParams)
questionPapers, err := tx.GetQuestionPaperWithExactMatch(&parsedParams)
if err != nil {
tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusInternalServerError, "could not fetch question papers, try again later", nil)
config.Get().Logger.Errorf("HandleFetchSimilarPapers: Error fetching papers from db: %+v", err.Error())
return
}

tx.Tx.Commit(context.Background())
sendResponse(w, http.StatusOK, questionPapers)
}

func HandleDeletePaper(w http.ResponseWriter, r *http.Request) {
approverUsername := r.Context().Value(CLAIMS_KEY).(*Claims).Username
db := db.GetDB()
tx, err := db.BeginTransaction()
if err != nil {
sendErrorResponse(w, http.StatusInternalServerError, "could not process request, try again later", nil)
config.Get().Logger.Errorf("HandleApprovePaper: could not start transaction: %+v", err.Error())
return
}
var requestBody struct {
Id int `json:"id"`
}
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&requestBody); err != nil {
tx.Tx.Rollback(context.Background())
sendErrorResponse(w, http.StatusInternalServerError, "Could not find Question Paper, Try Later!", nil)
config.Get().Logger.Errorf("HandleDeletePaper: could not approve paper, invalid Body: %+v", err.Error())
return
}

err := db.MarkPaperAsSoftDeletedAndUnApprove(requestBody.Id, approverUsername)
err = tx.MarkPaperAsSoftDeletedAndUnApprove(requestBody.Id, approverUsername)
if err != nil {
sendErrorResponse(w, http.StatusInternalServerError, "error updating paper details!", nil)
tx.Tx.Rollback(context.Background())
config.Get().Logger.Errorf("HandleDeletePaper: error soft-deleting paper: %+v PaperDetails: %d", err.Error(), requestBody.Id)
return
}
tx.Tx.Commit(context.Background())
config.Get().Logger.Infof("HandleDeletePaper: Deleted paper: %d", requestBody.Id)
sendResponse(w, http.StatusOK, httpResp{Message: "File Deleted successfully"})
}
Expand Down
15 changes: 15 additions & 0 deletions backend/pkg/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"sync"

"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/metakgp/iqps/backend/pkg/config"
)
Expand All @@ -19,6 +20,10 @@ type db struct {
Db *pgxpool.Pool
}

type txn struct {
Tx pgx.Tx
}

const init_db = `CREATE TABLE IF NOT EXISTS iqps (
id integer primary key generated always as identity,
course_code TEXT NOT NULL DEFAULT '',
Expand Down Expand Up @@ -74,3 +79,13 @@ func GetDB() *db {
}
return database
}

func BeginTransaction() (*txn, error) {
db := GetDB()
tx, err := db.Db.Begin(context.Background())
if err != nil {
return nil, err
}

return &txn{Tx: tx}, nil
}
16 changes: 8 additions & 8 deletions backend/pkg/db/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"github.com/metakgp/iqps/backend/pkg/models"
)

func (db *db) FetchAllQuestionPapers() ([]models.QuestionPaper, error) {
rows, err := db.Db.Query(context.Background(), "SELECT course_code,course_name,year,exam,filelink,id,from_library FROM iqps ORDER BY upload_timestamp ASC")
func (db *txn) FetchAllQuestionPapers() ([]models.QuestionPaper, error) {
rows, err := db.Tx.Query(context.Background(), "SELECT course_code,course_name,year,exam,filelink,id,from_library FROM iqps ORDER BY upload_timestamp ASC")
if err != nil {
config.Get().Logger.Errorf("FetchAllQuestionpapers: Could not fetch all question papers, error: %+v", err.Error())
return nil, errors.New("unable to fetch question paper, try again later")
Expand All @@ -33,7 +33,7 @@ func (db *db) FetchAllQuestionPapers() ([]models.QuestionPaper, error) {
return qps, nil
}

func (db *db) InsertNewPaper(qpDetails *models.QuestionPaper) (int, error) {
func (db *txn) InsertNewPaper(qpDetails *models.QuestionPaper) (int, error) {
query := "INSERT INTO iqps (course_code, course_name, year, exam, filelink, semester, approve_status, from_library, approved_by) VALUES (@course_code, @course_name, @year, @exam, @filelink, @semester, @approve_status, @from_library, @approved_by) RETURNING id"
params := pgx.NamedArgs{
"course_code": qpDetails.CourseCode,
Expand All @@ -47,21 +47,21 @@ func (db *db) InsertNewPaper(qpDetails *models.QuestionPaper) (int, error) {
"approved_by": qpDetails.ApprovedBy,
}
var id int
err := db.Db.QueryRow(context.Background(), query, params).Scan(&id)
err := db.Tx.QueryRow(context.Background(), query, params).Scan(&id)
if err != nil {
return 0, err
}
return id, nil
}

func (db *db) MarkPaperAsSoftDeletedAndUnApprove(qpID int, approvedBy string) error {
func (db *txn) MarkPaperAsSoftDeletedAndUnApprove(qpID int, approvedBy string) error {
query := "UPDATE iqps set approve_status=false, is_deleted = true, approved_by=@approved_by where id=@qpID and is_deleted=false"
params := pgx.NamedArgs{
"qpID": qpID,
"approved_by": approvedBy,
}

ct, err := db.Db.Exec(context.Background(), query, params)
ct, err := db.Tx.Exec(context.Background(), query, params)
if err != nil {
return err
}
Expand All @@ -72,7 +72,7 @@ func (db *db) MarkPaperAsSoftDeletedAndUnApprove(qpID int, approvedBy string) er
return nil
}

func (db *db) GetQuestionPaperWithExactMatch(paper *models.QuestionPaper) ([]models.QuestionPaper, error) {
func (db *txn) GetQuestionPaperWithExactMatch(paper *models.QuestionPaper) ([]models.QuestionPaper, error) {
query := "SELECT course_code,course_name,year,exam,filelink,id,from_library,semester from iqps where is_deleted=false and approve_status=true and course_code = @course_code"
params := pgx.NamedArgs{
"course_code": paper.CourseCode,
Expand All @@ -92,7 +92,7 @@ func (db *db) GetQuestionPaperWithExactMatch(paper *models.QuestionPaper) ([]mod
query += " and semester = @semester"
params["semester"] = paper.Semester
}
rows, err := db.Db.Query(context.Background(), query, params)
rows, err := db.Tx.Query(context.Background(), query, params)
if err != nil {
config.Get().Logger.Errorf("GetQuestionPaperWithExactMatch: Could not fetch all question papers, error: %+v", err.Error())
return nil, errors.New("unable to fetch question paper, try again later")
Expand Down

0 comments on commit 2077726

Please sign in to comment.