Skip to content

Commit

Permalink
basics
Browse files Browse the repository at this point in the history
  • Loading branch information
bnkamalesh committed Feb 17, 2024
1 parent f4cb398 commit c2aeecb
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 50 deletions.
12 changes: 2 additions & 10 deletions cmd/server/http/handlers_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,7 @@ func (h *Handlers) CreateUser(w http.ResponseWriter, r *http.Request) error {
return err
}

b, err := json.Marshal(createdUser)
if err != nil {
return errors.InputBodyErr(err, "invalid input body provided")
}

w.Header().Set("Content-Type", "application/json")
_, err = w.Write(b)
if err != nil {
return errors.Wrap(err, "failed to respond")
}
webgo.R200(w, createdUser)

return nil
}
Expand All @@ -49,5 +40,6 @@ func (h *Handlers) ReadUserByEmail(w http.ResponseWriter, r *http.Request) error
}

webgo.R200(w, out)

return nil
}
31 changes: 18 additions & 13 deletions cmd/server/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,27 @@ func NewService(cfg *Config, apis api.Server) (*HTTP, error) {
}
router.Use(panicRecoverer)

// in this app, /-/ prefixed routes are used for healthchecks, readiness checks etc.
_ = otelhttp.WithFilter(func(req *http.Request) bool {
return !strings.HasPrefix(req.URL.Path, "/-/")
})
otelopts := []otelhttp.Option{
// in this app, /-/ prefixed routes are used for healthchecks, readiness checks etc.
otelhttp.WithFilter(func(req *http.Request) bool {
return !strings.HasPrefix(req.URL.Path, "/-/")
}),
// the span name formatter is used to reduce the cardinality of metrics generated
// when using URIs with variables in it
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
wctx := webgo.Context(r)
if wctx == nil {
return r.URL.Path
}
return wctx.Route.Pattern
}),
}

// the span name formatter is used to reduce the cardinality of metrics generated
// when using URIs with variables in it
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
wctx := webgo.Context(r)
if wctx == nil {
return r.URL.Path
}
return wctx.Route.Pattern
apmMw := apm.NewHTTPMiddleware(otelopts...)
router.Use(func(w http.ResponseWriter, r *http.Request, hf http.HandlerFunc) {
apmMw(hf).ServeHTTP(w, r)
})

_ = apm.NewHTTPMiddleware()
return &HTTP{
server: router,
listener: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
Expand Down
37 changes: 22 additions & 15 deletions internal/users/store_postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package users
import (
"context"
"database/sql"
"strings"

"github.com/Masterminds/squirrel"
"github.com/bnkamalesh/errors"
Expand All @@ -20,10 +21,10 @@ type pgstore struct {
func (ps *pgstore) GetUserByEmail(ctx context.Context, email string) (*User, error) {
query, args, err := ps.qbuilder.Select(
"id",
"uname",
"full_name",
"email",
"phone",
"uaddress",
"contact_address",
).From(
ps.tableName,
).Where(
Expand All @@ -34,17 +35,20 @@ func (ps *pgstore) GetUserByEmail(ctx context.Context, email string) (*User, err
}

user := new(User)
uid := new(uuid.NullUUID)
address := new(sql.NullString)
phone := new(sql.NullString)

row := ps.pqdriver.QueryRow(ctx, query, args...)
err = row.Scan(user.ID, user.Name, phone, address)
err = row.Scan(uid, &user.FullName, &user.Email, phone, address)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, errors.NotFoundErr(ErrUserEmailNotFound, email)
}
return nil, errors.Wrap(err, "failed getting user info")
}
user.Address = address.String
user.ID = uid.UUID.String()
user.ContactAddress = address.String
user.Phone = phone.String

return user, nil
Expand All @@ -57,28 +61,31 @@ func (ps *pgstore) SaveUser(ctx context.Context, user *User) (string, error) {
ps.tableName,
).Columns(
"id",
"uname",
"full_name",
"email",
"phone",
"uaddress",
"contact_address",
).Values(
user.ID,
user.Name,
user.FullName,
user.Email,
sql.NullString{
String: user.Phone,
Valid: len(user.Phone) == 0,
Valid: len(user.Phone) != 0,
},
sql.NullString{
String: user.Address,
Valid: len(user.Address) == 0,
String: user.ContactAddress,
Valid: len(user.ContactAddress) != 0,
},
).ToSql()
if err != nil {
return "", errors.Wrap(err, "failed preparing query")
}
_, err = ps.pqdriver.Exec(ctx, query, args...)
if err != nil {
if strings.Contains(err.Error(), "violates unique constraint \"users_email_key\"") {
return "", errors.DuplicateErr(ErrUserEmailAlreadyExists, user.Email)
}
return "", errors.Wrap(err, "failed storing user info")
}

Expand All @@ -91,23 +98,23 @@ func (ps *pgstore) BulkSaveUser(ctx context.Context, users []User) error {
for _, user := range users {
rows = append(rows, []any{
user.ID,
user.Name,
user.FullName,
user.Email,
sql.NullString{
String: user.Phone,
Valid: len(user.Phone) == 0,
Valid: len(user.Phone) != 0,
},
sql.NullString{
String: user.Address,
Valid: len(user.Address) == 0,
String: user.ContactAddress,
Valid: len(user.ContactAddress) != 0,
},
})
}

inserted, err := ps.pqdriver.CopyFrom(
ctx,
pgx.Identifier{ps.tableName},
[]string{"id", "uname", "email", "phone", "uaddress"},
[]string{"id", "full_name", "email", "phone", "contact_address"},
pgx.CopyFromRows(rows),
)
if err != nil {
Expand Down
21 changes: 11 additions & 10 deletions internal/users/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ import (
)

var (
ErrUserEmailNotFound = errors.New("user with the email not found")
ErrUserEmailNotFound = errors.New("user with the email not found")
ErrUserEmailAlreadyExists = errors.New("user with the email already exists")
)

type User struct {
ID string
Name string
Email string
Phone string
Address string
ID string
FullName string
Email string
Phone string
ContactAddress string
}

// ValidateForCreate runs the validation required for when a user is being created. i.e. ID is not available
func (us *User) ValidateForCreate() error {
if us.Name == "" {
return errors.Validation("name cannot be empty")
if us.FullName == "" {
return errors.Validation("full name cannot be empty")
}

if us.Email == "" {
Expand All @@ -35,8 +36,8 @@ func (us *User) ValidateForCreate() error {

func (us *User) Sanitize() {
us.ID = strings.TrimSpace(us.ID)
us.Name = strings.TrimSpace(us.Name)
us.Address = strings.TrimSpace(us.Address)
us.FullName = strings.TrimSpace(us.FullName)
us.ContactAddress = strings.TrimSpace(us.ContactAddress)
us.Phone = strings.TrimSpace(us.Phone)
}

Expand Down
4 changes: 2 additions & 2 deletions schemas/users.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY,
email TEXT UNIQUE,
uname TEXT,
full_name TEXT,
phone TEXT,
uaddress TEXT,
contact_address TEXT,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now()
);
Expand Down

0 comments on commit c2aeecb

Please sign in to comment.