Context

Context adalah salah satu konkuresi pattern yang bertujuan untuk mengcancel jika menemui sebuah routine yang waktu eksekusinya lama. Karena operasi yang berjalan lama memang seharusnya diberi deadline. Jalan untuk meng-handle pembatalan adalah dengan melempar context.Context to fungsi yang mengetahui proses untuk mengecek pembatalan terminasi dini.

  • Tambahkan argumen context.Context ke semua fungsi di models/user.go

  • Passing ctx variable ke db.QueryContext, db.QueryRowContext, db.PrepareContext and stmt.ExecContext di file models/user.go

package models

import (
    "context"
    "database/sql"
    "essentials/libraries/api"
)

// User : struct of User
type User struct {
    ID       uint64
    Username string
    Password string
    Email    string
    IsActive bool
}

// List of users
func (u *User) List(ctx context.Context, db *sql.DB) ([]User, error) {
    var list []User
    const q = `SELECT id, username, password, email, is_active FROM users`

    rows, err := db.QueryContext(ctx, q)
    if err != nil {
        return list, err
    }

    defer rows.Close()

    for rows.Next() {
        var user User
        if err := rows.Scan(&user.ID, &user.Username, &user.Password, &user.Email, &user.IsActive); err != nil {
            return list, err
        }
        list = append(list, user)
    }

    return list, rows.Err()
}

// Create new user
func (u *User) Create(ctx context.Context, db *sql.DB) error {
    const query = `
        INSERT INTO users (username, password, email, is_active, created, updated)
        VALUES (?, ?, ?, 0, NOW(), NOW())
    `
    stmt, err := db.PrepareContext(ctx, query)

    if err != nil {
        return err
    }

    defer stmt.Close()

    res, err := stmt.ExecContext(ctx, u.Username, u.Password, u.Email)
    if err != nil {
        return err
    }

    id, err := res.LastInsertId()
    if err != nil {
        return err
    }

    u.ID = uint64(id)

    return nil
}

// Get user by id
func (u *User) Get(ctx context.Context, db *sql.DB) error {
    const q string = `SELECT id, username, password, email, is_active FROM users`
    err := db.QueryRowContext(ctx, q+" WHERE id=?", u.ID).Scan(&u.ID, &u.Username, &u.Password, &u.Email, &u.IsActive)

    if err == sql.ErrNoRows {
        err = api.ErrNotFound(err, "")
    }

    return err
}

// Update user by id
func (u *User) Update(ctx context.Context, db *sql.DB) error {
    const q string = `UPDATE users SET is_active = ? WHERE id = ?`
    stmt, err := db.PrepareContext(ctx, q)
    if err != nil {
        return err
    }

    defer stmt.Close()

    _, err = stmt.ExecContext(ctx, u.IsActive, u.ID)
    return err
}

// Delete user by id
func (u *User) Delete(ctx context.Context, db *sql.DB) error {
    const q string = `DELETE FROM users WHERE id = ?`
    stmt, err := db.PrepareContext(ctx, q)
    if err != nil {
        return err
    }

    defer stmt.Close()

    _, err = stmt.ExecContext(ctx, u.ID)
    return err
}
  • Pass nilai dari r.Context() dari file controllers/users.go pada setiap kali memanggil method di models/user.go

  • Pass nilai dari r.Context() dari file usecases/user_usecase.go pada setiap kali memanggil method di models/user.go

  • Pada unit test, pass nilai dari context.Background() di file tests/user_tes.go setiap memanggil method di models/user.go

  • Kita bisa mengetesnya dengan membuat time.Sleep()

Last updated

Was this helpful?