diff --git a/internal/api/repository/doctor.go b/internal/api/repository/doctor.go index 3b7ca8b..e0ddb24 100644 --- a/internal/api/repository/doctor.go +++ b/internal/api/repository/doctor.go @@ -6,6 +6,7 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" + "github.com/jmoiron/sqlx" ) // DBDoctor represents a doctor from the database @@ -44,12 +45,12 @@ func (r *DoctorRepository) GetDoctorDetailsByUserID(userID string) (*DBDoctor, e return &dbDoctor, nil } -// CreateDoctor creates a new doctor record -func (r *DoctorRepository) CreateDoctor(doctorDetails *models.DoctorDetails, userID string) error { +// CreateDoctorTx creates a new doctor record within a transaction +func (r *DoctorRepository) CreateDoctorTx(tx *sqlx.Tx, doctorDetails *models.DoctorDetails, userID string) error { query := `INSERT INTO doctor (Doctor_UsersID, DoctorID, DoctorName, DoctorCreatedAt, DoctorLastUpdatedAt) VALUES (?, ?, ?, NOW(), NOW())` - _, err := database.DB.Exec(query, userID, doctorDetails.DoctorID, doctorDetails.DoctorName) + _, err := tx.Exec(query, userID, doctorDetails.DoctorID, doctorDetails.DoctorName) if err != nil { return fmt.Errorf("database error creating doctor: %w", err) } diff --git a/internal/api/repository/patient.go b/internal/api/repository/patient.go index 4b53e39..33e035d 100644 --- a/internal/api/repository/patient.go +++ b/internal/api/repository/patient.go @@ -7,6 +7,7 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" + "github.com/jmoiron/sqlx" ) // DBPatient represents a patient from the database @@ -61,9 +62,9 @@ func (r *PatientRepository) GetPatientDetailsByUserID(userID string) (*models.Pa }, nil } -// CreatePatient creates a new patient record -func (r *PatientRepository) CreatePatient(patientRecord *models.PatientDetails, userID string) error { - // Parse DOB to time.Time +// CreatePatientTx creates a new patient record within a transaction +func (r *PatientRepository) CreatePatientTx(tx *sqlx.Tx, patientRecord *models.PatientDetails, userID string) error { + // Parse DOB to time.Time. 2006-1-02 = reference format YYYY-MM-DD in Go, not a default value dob, err := time.Parse("2006-01-02", patientRecord.DateOfBirth) if err != nil { return fmt.Errorf("invalid date of birth format: %w", err) @@ -72,7 +73,7 @@ func (r *PatientRepository) CreatePatient(patientRecord *models.PatientDetails, query := `INSERT INTO patient (Patient_UsersID, PatientMedrec, PatientName, PatientDoB, PatientCreatedAt, PatientUpdatedAt) VALUES (?, ?, ?, ?, NOW(), NOW())` - _, err = database.DB.Exec(query, userID, patientRecord.PatientID, patientRecord.PatientName, dob) + _, err = tx.Exec(query, userID, patientRecord.PatientID, patientRecord.PatientName, dob) if err != nil { return fmt.Errorf("database error creating patient: %w", err) } diff --git a/internal/api/repository/shortlink.go b/internal/api/repository/shortlink.go index 1799941..2c158f7 100644 --- a/internal/api/repository/shortlink.go +++ b/internal/api/repository/shortlink.go @@ -8,6 +8,7 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" + "github.com/jmoiron/sqlx" ) // DBShortLink represents a shortlink from the database @@ -69,8 +70,8 @@ func (r *ShortLinkRepository) GetShortLinkByToken(token string) (*models.ShortLi return dbShortLink.ToShortLink(), nil } -// CreateShortLink stores a new shortlink in the database -func (r *ShortLinkRepository) CreateShortLink(shortLink *models.ShortLink) error { +// CreateShortLinkTx stores a new shortlink in the database within a transaction +func (r *ShortLinkRepository) CreateShortLinkTx(tx *sqlx.Tx, shortLink *models.ShortLink) error { query := `INSERT INTO shortlink ( ShortlinkCode, Shortlink_PatientID, @@ -93,7 +94,7 @@ func (r *ShortLinkRepository) CreateShortLink(shortLink *models.ShortLink) error return fmt.Errorf("invalid expiration date: %w", err) } - _, err = database.DB.Exec( + _, err = tx.Exec( query, shortLink.Token, shortLink.PatientID, @@ -112,8 +113,8 @@ func (r *ShortLinkRepository) CreateShortLink(shortLink *models.ShortLink) error return nil } -// UpdateShortLink updates an existing shortlink in the database -func (r *ShortLinkRepository) UpdateShortLink(shortLink *models.ShortLink) error { +// UpdateShortLinkTx updates an existing shortlink in the database within a transaction +func (r *ShortLinkRepository) UpdateShortLinkTx(tx *sqlx.Tx, shortLink *models.ShortLink) error { query := `UPDATE shortlink SET ShortlinkIsRevoked = ?, ShortlinkRemainingTries = ?, @@ -125,7 +126,7 @@ func (r *ShortLinkRepository) UpdateShortLink(shortLink *models.ShortLink) error return fmt.Errorf("invalid expiration date: %w", err) } - _, err = database.DB.Exec( + _, err = tx.Exec( query, shortLink.IsRevoked, shortLink.RemainingTries, diff --git a/internal/api/repository/study.go b/internal/api/repository/study.go index bb88e28..2d02c51 100644 --- a/internal/api/repository/study.go +++ b/internal/api/repository/study.go @@ -6,6 +6,7 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" + "github.com/jmoiron/sqlx" ) // DBStudy represents a study from the database @@ -69,8 +70,8 @@ func (r *StudyRepository) GetStudyByUID(studyUID string) (*DBStudy, error) { return &study, nil } -// CreateStudy creates a new study record for a patient -func (r *StudyRepository) CreateStudy(patientID string, study models.Study) error { +// CreateStudyTx creates a new study record for a patient within a transaction +func (r *StudyRepository) CreateStudyTx(tx *sqlx.Tx, patientID string, study models.Study) error { // Parse study date if provided var studyDate *time.Time if study.StudyDate != "" { @@ -85,7 +86,7 @@ func (r *StudyRepository) CreateStudy(patientID string, study models.Study) erro (Study_PatientID, StudyIUID, StudyAccessionNumber, StudyDate, StudyCreatedAt, StudyUpdatedAt) VALUES (?, ?, ?, ?, NOW(), NOW())` - _, err := database.DB.Exec(query, + _, err := tx.Exec(query, patientID, study.StudyInstanceUID, study.AccessionNumber, diff --git a/internal/api/repository/user.go b/internal/api/repository/user.go index 89047eb..957bedd 100644 --- a/internal/api/repository/user.go +++ b/internal/api/repository/user.go @@ -7,6 +7,7 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" + "github.com/jmoiron/sqlx" "golang.org/x/crypto/bcrypt" ) @@ -140,8 +141,8 @@ func (r *UserRepository) RevokeRefreshToken(token string) error { return nil } -// CreateUser creates a new user -func (r *UserRepository) CreateUser(user *models.User) error { +// CreateUserTx creates a new user within a transaction +func (r *UserRepository) CreateUserTx(tx *sqlx.Tx, user *models.User) error { // Hash the password before storing hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) if err != nil { @@ -151,7 +152,7 @@ func (r *UserRepository) CreateUser(user *models.User) error { query := `INSERT INTO user (UserEmail, UserPassword, UserRole, UserName, UserCreatedAt, UserUpdatedAt) VALUES (?, ?, ?, ?, NOW(), NOW())` - result, err := database.DB.Exec(query, user.Email, string(hashedPassword), user.Role, user.Name) + result, err := tx.Exec(query, user.Email, string(hashedPassword), user.Role, user.Name) if err != nil { return fmt.Errorf("database error creating user: %w", err) } diff --git a/internal/api/routes.go b/internal/api/routes.go index 3a8fb79..efd7a33 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -88,8 +88,8 @@ func SetupRouter(cfg *config.Config, logger *zap.Logger) http.Handler { r.Post("/refresh", authHandler.RefreshToken) r.Post("/logout", authHandler.Logout) - // Registration endpoint - registerService := service.NewRegisterService() + // Registration endpoint + registerService := service.NewRegisterService(logger) registerHandler := handlers.NewRegisterHandler(logger, registerService) r.Post("/register", registerHandler.Register) diff --git a/internal/api/service/register_service.go b/internal/api/service/register_service.go index ee7d2ae..7c28ea8 100644 --- a/internal/api/service/register_service.go +++ b/internal/api/service/register_service.go @@ -7,6 +7,8 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/repository" + "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" + "go.uber.org/zap" ) var ( @@ -15,6 +17,7 @@ var ( ErrInvalidRole = errors.New("invalid user role") ErrInvalidPatient = errors.New("invalid patient data") ErrInvalidDoctor = errors.New("invalid doctor data") + ErrTransaction = errors.New("transaction error") ) // RegisterRequest represents a user registration request @@ -34,15 +37,22 @@ type RegisterService struct { patientRepo *repository.PatientRepository doctorRepo *repository.DoctorRepository studyRepo *repository.StudyRepository + logger *zap.Logger } // NewRegisterService creates a new register service -func NewRegisterService() *RegisterService { +func NewRegisterService(logger *zap.Logger) *RegisterService { + if logger == nil { + // If no logger is provided, create a no-op logger + logger = zap.NewNop() + } + return &RegisterService{ userRepo: repository.NewUserRepository(), patientRepo: repository.NewPatientRepository(), doctorRepo: repository.NewDoctorRepository(), studyRepo: repository.NewStudyRepository(), + logger: logger, } } @@ -62,7 +72,7 @@ func (s *RegisterService) Register(req *RegisterRequest) (*models.User, error) { return nil, ErrInvalidDoctor } - // Check if email already exists + // Check if email already exists - do this outside the transaction existingUser, err := s.userRepo.GetUserByEmail(req.Email) if err != nil && err != sql.ErrNoRows { return nil, fmt.Errorf("error checking existing user: %w", err) @@ -72,6 +82,22 @@ func (s *RegisterService) Register(req *RegisterRequest) (*models.User, error) { return nil, ErrEmailExists } + // Start a transaction + tx, err := database.DB.Beginx() + if err != nil { + s.logger.Error("Failed to begin transaction", zap.Error(err)) + return nil, fmt.Errorf("%w: failed to begin transaction", ErrTransaction) + } + + // Ensure the transaction is rolled back if we return an error + defer func() { + if r := recover(); r != nil { + s.logger.Error("Panic in transaction", zap.Any("recover", r)) + tx.Rollback() + panic(r) // re-throw the panic after cleanup + } + }() + // Create user newUser := &models.User{ Email: req.Email, @@ -80,15 +106,19 @@ func (s *RegisterService) Register(req *RegisterRequest) (*models.User, error) { Name: req.Name, } - if err := s.userRepo.CreateUser(newUser); err != nil { + // Use the transaction for user creation + if err := s.userRepo.CreateUserTx(tx, newUser); err != nil { + s.logger.Error("Failed to create user", zap.Error(err), zap.String("email", req.Email)) + tx.Rollback() return nil, fmt.Errorf("error creating user: %w", err) } // Create role-specific data if req.Role == "patient" { - err = s.patientRepo.CreatePatient(req.Patient, newUser.ID) + err = s.patientRepo.CreatePatientTx(tx, req.Patient, newUser.ID) if err != nil { - // TODO: Consider rollback user creation if this fails + s.logger.Error("Failed to create patient", zap.Error(err), zap.String("patientID", req.Patient.PatientID)) + tx.Rollback() return nil, fmt.Errorf("error creating patient: %w", err) } @@ -99,19 +129,37 @@ func (s *RegisterService) Register(req *RegisterRequest) (*models.User, error) { continue // Skip studies without UIDs } - err = s.studyRepo.CreateStudy(req.Patient.PatientID, study) + err = s.studyRepo.CreateStudyTx(tx, req.Patient.PatientID, study) if err != nil { + s.logger.Error("Failed to create study", + zap.Error(err), + zap.String("patientID", req.Patient.PatientID), + zap.String("studyUID", study.StudyInstanceUID)) + tx.Rollback() return nil, fmt.Errorf("error creating study for patient: %w", err) } } } } else if req.Role == "ref_doctor" || req.Role == "expertise_doctor" { - err = s.doctorRepo.CreateDoctor(req.Doctor, newUser.ID) + err = s.doctorRepo.CreateDoctorTx(tx, req.Doctor, newUser.ID) if err != nil { - // TODO: Consider rollback user creation if this fails + s.logger.Error("Failed to create doctor", zap.Error(err), zap.String("doctorID", req.Doctor.DoctorID)) + tx.Rollback() return nil, fmt.Errorf("error creating doctor: %w", err) } } + // Commit the transaction + if err := tx.Commit(); err != nil { + s.logger.Error("Failed to commit transaction", zap.Error(err)) + tx.Rollback() // This is actually redundant as the transaction will be rolled back on failure + return nil, fmt.Errorf("%w: failed to commit transaction", ErrTransaction) + } + + s.logger.Info("Successfully registered new user", + zap.String("email", req.Email), + zap.String("role", req.Role), + zap.String("userID", newUser.ID)) + return newUser, nil } diff --git a/internal/api/service/shortlink_service.go b/internal/api/service/shortlink_service.go index e8334ed..ae2ab52 100644 --- a/internal/api/service/shortlink_service.go +++ b/internal/api/service/shortlink_service.go @@ -13,6 +13,7 @@ import ( "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/repository" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/auth" + "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/database" "go.uber.org/zap" ) @@ -150,13 +151,36 @@ func (s *ShortLinkService) GenerateShortLink(req *models.GenerateShortLinkReques RemainingTries: s.maxAttempts, } - // Store the short link in the database - err = s.shortLinkRepo.CreateShortLink(shortLink) + // Start a transaction for creating the shortlink + tx, err := database.DB.Beginx() + if err != nil { + s.logger.Error("Failed to start transaction", zap.Error(err)) + return nil, fmt.Errorf("database error: %w", err) + } + + // Set up deferred rollback that will be canceled if we commit + defer func() { + if tx != nil { + tx.Rollback() + } + }() + + // Store the short link in the database using transaction + err = s.shortLinkRepo.CreateShortLinkTx(tx, shortLink) if err != nil { s.logger.Error("Failed to store shortlink in database", zap.Error(err)) return nil, ErrCreationFailed } + // Commit the transaction + if err = tx.Commit(); err != nil { + s.logger.Error("Failed to commit transaction", zap.Error(err)) + return nil, errors.New("database error") + } + + // Clear the tx to prevent the deferred rollback + tx = nil + // Generate the full URL using the configured base URL fullURL := fmt.Sprintf("%s/short-auth?short=%s", s.baseURL, token) @@ -203,43 +227,101 @@ func (s *ShortLinkService) ValidateShortLink(req *models.ShortLinkAuthRequest) ( // Normalize and hash the provided DOB dob := normalizeDOB(req.DOB) if !isValidDOBFormat(dob) { + // Use a transaction for updating the tries counter + tx, err := database.DB.Beginx() + if err != nil { + s.logger.Error("Failed to start transaction", zap.Error(err)) + return nil, fmt.Errorf("database error: %w", err) + } + + // Set up deferred rollback that will be canceled if we commit + defer func() { + if tx != nil { + tx.Rollback() + } + }() + // Decrement remaining tries on invalid format shortLink.RemainingTries-- - // Update the shortlink in the database - err = s.shortLinkRepo.UpdateShortLink(shortLink) + // Update the shortlink in the database using the transaction + err = s.shortLinkRepo.UpdateShortLinkTx(tx, shortLink) if err != nil { s.logger.Error("Failed to update shortlink tries", zap.Error(err)) + return nil, errors.New("invalid date of birth format, expected YYYY-MM-DD") } + // Commit the transaction + if err = tx.Commit(); err != nil { + s.logger.Error("Failed to commit transaction", zap.Error(err)) + return nil, errors.New("database error") + } + + // Clear the tx to prevent the deferred rollback + tx = nil + return nil, errors.New("invalid date of birth format, expected YYYY-MM-DD") } hashedDOB := hashDOB(dob) + // Start a transaction for updating the tries counter + tx, err := database.DB.Beginx() + if err != nil { + s.logger.Error("Failed to start transaction", zap.Error(err)) + return nil, fmt.Errorf("database error: %w", err) + } + + // Set up deferred rollback that will be canceled if we commit + defer func() { + if tx != nil { + tx.Rollback() + } + }() + // Verify the DOB if hashedDOB != shortLink.HashedDOB { // Decrement remaining tries on failed verification shortLink.RemainingTries-- - // Update the shortlink in the database - err = s.shortLinkRepo.UpdateShortLink(shortLink) + // Update the shortlink in the database using the transaction + err = s.shortLinkRepo.UpdateShortLinkTx(tx, shortLink) if err != nil { s.logger.Error("Failed to update shortlink tries", zap.Error(err)) + return nil, ErrInvalidDOB } + // Commit the transaction + if err = tx.Commit(); err != nil { + s.logger.Error("Failed to commit transaction", zap.Error(err)) + return nil, errors.New("database error") + } + + // Clear the tx to prevent the deferred rollback + tx = nil + return nil, ErrInvalidDOB } // DOB verified, reset tries count as successful login shortLink.RemainingTries = s.maxAttempts - // Update the shortlink in the database - err = s.shortLinkRepo.UpdateShortLink(shortLink) + // Update the shortlink in the database using the transaction + err = s.shortLinkRepo.UpdateShortLinkTx(tx, shortLink) if err != nil { s.logger.Error("Failed to update shortlink tries after successful validation", zap.Error(err)) + return nil, errors.New("database error") } + // Commit the transaction + if err = tx.Commit(); err != nil { + s.logger.Error("Failed to commit transaction", zap.Error(err)) + return nil, errors.New("database error") + } + + // Clear the tx to prevent the deferred rollback + tx = nil + return shortLink, nil } diff --git a/test/http/dicom-proxy.http b/test/http/dicom-proxy.http index 8eafa4b..26225a2 100644 --- a/test/http/dicom-proxy.http +++ b/test/http/dicom-proxy.http @@ -1,5 +1,5 @@ ### Local OHIF Proxy Test File -@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMiIsImVtYWlsIjoicGF0aWVudCIsInJvbGUiOiJwYXRpZW50IiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImV4cCI6MTc0NjM2NDczMiwiaWF0IjoxNzQ2MzYyOTMyfQ.4IGGV77jnewQVXOCuFWmcx4X7EMMxx341j6DeNKYcFY +@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJ1c2VyX25hbWUiOiJBZG1pbiBVc2VyIiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImhvbWVfdXJsIjoiLyIsInN0dWR5X2xpc3QiOiJlbmFibGVkIiwiZXhwIjoxNzQ3Mzg0MDE2LCJpYXQiOjE3NDcyOTc2MTZ9.Ak1DECP1MXzQAPyU-AJM6Tsu6-sw04UtWYvY37-SaT4 @baseUrl = http://localhost:5555 # @baseUrl = http://devone.aplikasi.web.id:5555 @@ -20,11 +20,13 @@ Authorization: Bearer {{token}} # Returns studies matching patient name (replace SMITH with a name in your dataset) GET {{baseUrl}}/dicomWeb/studies?limit=10&offset=0&fuzzymatching=true&includefield=00081030%2C00080060%2C00080090&00080090=DR.%20HERWINDO%20RIDWAN%2C%20SP.OT Accept: application/dicom+json +Authorization: Bearer {{token}} ### 4. QIDO-RS: Search for Studies with Date Range # Returns studies within a date range GET {{baseUrl}}/dicomWeb/studies?StudyDate=20250301-20250415 Accept: application/dicom+json +Authorization: Bearer {{ token }} ### 5. QIDO-RS: Search for Series in a Study # Replace STUDY_INSTANCE_UID with an actual Study UID from your data diff --git a/test/http/ohif-flow.http b/test/http/ohif-flow.http index 8471abd..fa1a47b 100644 --- a/test/http/ohif-flow.http +++ b/test/http/ohif-flow.http @@ -1,4 +1,4 @@ -@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoicGF0aWVudDFAZXhhbXBsZS5jb20iLCJyb2xlIjoicGF0aWVudCIsInVzZXJfbmFtZSI6IkRJRElUIFNVWUFUTkFeUi4xMDA0OS4xOCIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJwYXRpZW50X2lkIjoiMDAyMTE2MjIiLCJwYXRpZW50X25hbWUiOiJESURJVCBTVVlBVE5BXlIuMTAwNDkuMTgiLCJzdHVkeV9pdWlkcyI6WyIxLjIuODI2LjAuMS4zNjgwMDQzLjkuNzMwNy4xLjIwMTgwNTMwMDY2IiwiMS4yLjgyNi4wLjEuMzY4MDA0My45LjczMDcuMS4yMDE4MDcxMzAzNiJdLCJhY2Nlc3Npb25fbnVtYmVycyI6WyJDUi4xODA1MzAuMDY2IiwiQ1IuMTgwNzEzLjAzNiJdLCJob21lX3VybCI6InZpZXdlcj9TdHVkeUluc3RhbmNlVUlEcz0xLjIuODI2LjAuMS4zNjgwMDQzLjkuNzMwNy4xLjIwMTgwNTMwMDY2Iiwic3R1ZHlfbGlzdCI6ImRpc2FibGVkIiwiZXhwIjoxNzQ3MzY0OTY1LCJpYXQiOjE3NDcyNzg1NjV9.I6L7nnT-xexPtkTIfIXetT41KGQlVPbCY8l_2fTgJdg +@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJ1c2VyX25hbWUiOiJBZG1pbiBVc2VyIiwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImhvbWVfdXJsIjoiLyIsInN0dWR5X2xpc3QiOiJlbmFibGVkIiwiZXhwIjoxNzQ3Mzg0MDE2LCJpYXQiOjE3NDcyOTc2MTZ9.Ak1DECP1MXzQAPyU-AJM6Tsu6-sw04UtWYvY37-SaT4 @token_exp_doctor = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsImVtYWlsIjoiYWRtaW4iLCJyb2xlIjoiZXhwZXJ0aXNlX2RvY3RvciIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJleHAiOjE3NDY1MDQ1MTYsImlhdCI6MTc0NjQxODExNn0.vlDrns1oPFXHE5--TmWqwzvzxnfcCPcV2UW8_4GwDwE @baseUrl = http://localhost:5555 diff --git a/test/http/register-flow.http b/test/http/register-flow.http index 93d0eb1..9333d3b 100644 --- a/test/http/register-flow.http +++ b/test/http/register-flow.http @@ -4,6 +4,17 @@ @baseUrl = http://localhost:5555 +### Register an admin user +POST {{baseUrl}}/auth/register +Content-Type: application/json + +{ + "email": "admin@example.com", + "password": "password123", + "name": "Admin User", + "role": "admin" +} + ### Register a new patient with multiple studies POST {{baseUrl}}/auth/register Content-Type: application/json @@ -24,7 +35,24 @@ Content-Type: application/json "accession_number": "CR.180530.066", "study_date": "2018-05-30", "study_description": "Chest X-Ray" - }, + } + ] +} +### +POST {{baseUrl}}/auth/register +Content-Type: application/json + +{ + "email": "patient1@example.com", + "password": "password123", + "name": "DIDIT SUYATNA^R.10049.18", + "role": "patient", + "patient": { + "patient_id": "00211622", + "patient_name": "DIDIT SUYATNA^R.10049.18", + "date_of_birth": "1980-01-01" + }, + "studies": [ { "study_instance_uid": "1.2.826.0.1.3680043.9.7307.1.20180713036", "accession_number": "CR.180713.036", @@ -73,21 +101,6 @@ Content-Type: application/json } } -### Register a new expertise doctor -POST {{baseUrl}}/auth/register -Content-Type: application/json - -{ - "email": "expert@example.com", - "password": "password123", - "name": "Dr. Expertise", - "role": "expertise_doctor", - "doctor": { - "doctor_id": "EX456", - "doctor_name": "Dr. Expertise" - } -} - ### Register another referring doctor POST {{baseUrl}}/auth/register Content-Type: application/json @@ -103,15 +116,19 @@ Content-Type: application/json } } -### Register an admin user +### Register a new expertise doctor POST {{baseUrl}}/auth/register Content-Type: application/json { - "email": "admin@example.com", + "email": "expert@example.com", "password": "password123", - "name": "Admin User", - "role": "admin" + "name": "Dr. Expertise", + "role": "expertise_doctor", + "doctor": { + "doctor_id": "EX456", + "doctor_name": "Dr. Expertise" + } } ### Login with registered user @@ -150,10 +167,12 @@ Content-Type: application/json "password": "password123" } +@refresh_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNCIsImVtYWlsIjoiZG9jdG9yQGV4YW1wbGUuY29tIiwicm9sZSI6InJlZl9kb2N0b3IiLCJ1c2VyX25hbWUiOiJEUi4gSEVSV0lORE8gUklEV0FOLCBTUC5PVCIsInRva2VuX3R5cGUiOiJyZWZyZXNoIiwiaG9tZV91cmwiOiIvIiwic3R1ZHlfbGlzdCI6ImVuYWJsZWQiLCJmaWx0ZXJfdXJsIjoic3R1ZGllcz9saW1pdD0xMDFcdTAwMjZvZmZzZXQ9MFx1MDAyNmZ1enp5bWF0Y2hpbmc9ZmFsc2VcdTAwMjZpbmNsdWRlZmllbGQ9MDAwODEwMzAsMDAwODAwNjAsMDAwODAwOTBcdTAwMjYwMDA4MDA5MD1EUi4rSEVSV0lORE8rUklEV0FOJTJDK1NQLk9UIiwiZXhwIjoxNzQ3OTAxMzI5LCJpYXQiOjE3NDcyOTY1Mjl9.M7khgg8eZHk20qGsdPDmUojdBvItXOE-834R6t_AF9Q" + ### Refresh TOken POST {{baseUrl}}/auth/refresh Content-Type: application/json { - "refresh_token": {{ refresh_token}} + "refresh_token" : {{refresh_token}} } \ No newline at end of file