Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cart ops jwt #5

Open
wants to merge 56 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
dc2890a
implemented basic functions of cart module
yama11299 Sep 7, 2020
7670aa4
Updated Migration For User Table
sagar23sj Sep 7, 2020
986fe29
Added GetUserByMobile method in Storer Interface
sagar23sj Sep 7, 2020
39734b4
Called GetUserByMobile in Storer Interface
sagar23sj Sep 7, 2020
adcd53d
Updated Users Table
sagar23sj Sep 8, 2020
3560764
Updated User Table
sagar23sj Sep 9, 2020
51f2149
Added Routes and JWT Middleware
sagar23sj Sep 9, 2020
1f192ce
Added JWT Token Generate and Logout Methods
sagar23sj Sep 9, 2020
568b0c0
Added Necessary Comments
sagar23sj Sep 9, 2020
15c11c6
Added Necessary Comments
sagar23sj Sep 9, 2020
967cd62
Imported New Libraries
sagar23sj Sep 9, 2020
dbb75ae
Added Methods to Fetch JWT SecretKey and ExpirationTime
sagar23sj Sep 9, 2020
dcbaa01
Added getUserHandler
sagar23sj Sep 9, 2020
b7e61b8
Added Necessary Comments
sagar23sj Sep 9, 2020
31a01aa
Added Necessary methods for user sql operations
sagar23sj Sep 9, 2020
52edc97
Defined Necessary SQL methods in Interface
sagar23sj Sep 9, 2020
f142949
File for Handling Error Messages
sagar23sj Sep 9, 2020
ff41a90
Added Database Migrations for Handling Blaclisted Tokens
sagar23sj Sep 9, 2020
6744375
Created file for managing user_blacklisted_tokens methods
sagar23sj Sep 9, 2020
931af3e
Added Necessary Comments
sagar23sj Sep 9, 2020
4f9b70d
Updated CreateBlacklistedToken
sagar23sj Sep 9, 2020
166d880
Added Necessary Comments
sagar23sj Sep 9, 2020
54d9345
Updated jwtMiddleware function
sagar23sj Sep 9, 2020
80cebad
Added Necessary Comments
sagar23sj Sep 9, 2020
f8d75a8
Updated CheckBlacklistedToken Function
sagar23sj Sep 9, 2020
4027941
Refactored JWTMiddleWare function and added necessary comments
sagar23sj Sep 9, 2020
a5e96ed
Refactored the userLoginHandle and userLogoutHandle function and adde…
sagar23sj Sep 9, 2020
e3152fe
Added necessary comments
sagar23sj Sep 9, 2020
8ee4898
Removed Unused Error Messages
sagar23sj Sep 9, 2020
d1ba6e5
Handled Errors uding Messages From apperrors file
sagar23sj Sep 9, 2020
97b3619
Handled Error Messages from apperrors file
sagar23sj Sep 9, 2020
3dbe5a0
Changes in Commenting
sagar23sj Sep 9, 2020
6fb9221
Changes in Commenting
sagar23sj Sep 9, 2020
08589e0
Changes in Error Handling for GenerateJWTToken
sagar23sj Sep 9, 2020
340650c
Removed NOT NULL constraint from Mobile from User
sagar23sj Sep 9, 2020
8d13a62
Added Regex for id validation in routes
sagar23sj Sep 9, 2020
944ea05
Added Necessary Error Handling
sagar23sj Sep 9, 2020
c2073ab
Added necessary Error Handling
Sep 10, 2020
b36411e
Added Json responce object as AuthToken
Sep 10, 2020
9e705dd
Updated constraint from user migration
Sep 10, 2020
c8c5758
Changes in route and jwtMiddleWare function
sagar23sj Sep 10, 2020
84eebc6
Added getDataFromToken function and updated userLogoutHandler
sagar23sj Sep 10, 2020
faa0d43
Updated user_id fteching in getUserHandler function
sagar23sj Sep 10, 2020
9319a24
Removed Unnecessary Comments
sagar23sj Sep 10, 2020
f7e29b0
improved error handling
yama11299 Sep 10, 2020
cbb000f
Handled CORS
sagar23sj Sep 10, 2020
acc94d3
resolved merge conflict
yama11299 Sep 10, 2020
d037b9f
cart operations using JWT
yama11299 Sep 10, 2020
69de9e3
Removed Unnecessary Error Handling
Sep 11, 2020
3f0adae
Updated Response Body Structure
Sep 11, 2020
5c1a4ec
Fixed minor error
Sep 11, 2020
8545a3c
Updated Setter And Getter Method For Header
Sep 11, 2020
6e7a6f9
Updated User struct
Sep 11, 2020
d28894b
Added Necessary Error Handling
Sep 11, 2020
0ffa275
Merge branch 'User_Operations' of github.com:joshsoftware/go-e-commer…
yama11299 Sep 12, 2020
5ddea53
review changes completed
yama11299 Sep 15, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions apperrors/apperrors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package apperrors

import (
"encoding/json"
"errors"
l "github.com/sirupsen/logrus"
"net/http"
)

// ErrorStruct - struct used to convert error messages into required JSON format
type ErrorStruct struct {
Message string `json:"message,omitempty"` //Error Message
Status int `json:"status,omitempty"` //HTTP Response status code
}

// Error - prints out an error
func Error(appError error, msg string, triggeringError error) {
l.WithFields(l.Fields{"appError": appError, "message": msg}).Error(triggeringError)
}

// Warn - for warnings
func Warn(appError error, msg string, triggeringError error) {
l.WithFields(l.Fields{"appError": appError, "message": msg}).Warn(triggeringError)
}

// JSONError - This function writes out an error response with the status
// header passed in
func JSONError(rw http.ResponseWriter, status int, err error) {

errObj := ErrorStruct{
Message: err.Error(),
Status: status,
}

errJSON, err := json.Marshal(&errObj)
if err != nil {
Warn(err, "Error in AppErrors marshalling JSON", err)
}
rw.WriteHeader(status)
rw.Header().Set("Content-Type", "application/json")
rw.Write(errJSON)
return
}

// ErrRecordNotFound - for when a database record isn't found
var ErrRecordNotFound = errors.New("Database record not found")

// ErrInvalidToken - used when a JSON Web Token ("JWT") cannot be validated
// by the JWT library
var ErrInvalidToken = errors.New("Invalid Token")

// ErrSignedString - failed to sign the token string
var ErrSignedString = errors.New("Failed to sign token string")

// ErrMissingAuthHeader - When the HTTP request doesn't contain an 'Authorization' header
var ErrMissingAuthHeader = errors.New("Missing Auth header")

// ErrJSONParseFail - If json.Unmarshal or json.Marshal returns an error
var ErrJSONParseFail = errors.New("Failed to parse JSON response (likely not valid JSON)")

// ErrNoSigningKey - there isn't a signing key defined in the app configuration
var ErrNoSigningKey = errors.New("no JWT signing key specified; cannot authenticate users. Define JWT_SECRET in application.yml and restart")

// ErrFailedToCreate - Record Creation Failed
var ErrFailedToCreate = errors.New("Failed to create database record")

// ErrUnknown - Generic Error For Unknown Errors
var ErrUnknown = errors.New("unknown/unexpected error has occurred")
2 changes: 1 addition & 1 deletion application.yml.default
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
APP_NAME: "samplemgr"
APP_NAME: "app"
APP_PORT: "33001"

# MongoDB URI
Expand Down
32 changes: 27 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package config

import (
"errors"
"fmt"
"strconv"

"github.com/spf13/viper"
)

var (
appName string
appPort int
appName string
appPort int
jwtKey string
jwtExpiryDurationHours int
)

// Load - loads all the environment variables and/or params in application.yml
func Load() {
viper.SetDefault("APP_NAME", "app")
viper.SetDefault("APP_NAME", "e-commerce")
viper.SetDefault("APP_PORT", "8002")

viper.SetConfigName("application")
Expand All @@ -24,22 +26,39 @@ func Load() {
viper.AddConfigPath("./../..")
viper.ReadInConfig()
viper.AutomaticEnv()

// Check for the presence of JWT_KEY and JWT_EXPIRY_DURATION_HOURS
JWTKey()
JWTExpiryDurationHours()
}

// AppName - returns the app name
func AppName() string {
if appName == "" {
appName = ReadEnvString("APP_NAME")
}
return appName
}

// AppPort - returns application http port
func AppPort() int {
if appPort == 0 {
appPort = ReadEnvInt("APP_PORT")
}
return appPort
}

// JWTKey - returns the JSON Web Token key
func JWTKey() []byte {
return []byte(ReadEnvString("JWT_SECRET"))
}

// JWTExpiryDurationHours - returns duration for jwt expiry in int
func JWTExpiryDurationHours() int {
return int(ReadEnvInt("JWT_EXPIRY_DURATION_HOURS"))
}

// ReadEnvInt - reads an environment variable as an integer
func ReadEnvInt(key string) int {
checkIfSet(key)
v, err := strconv.Atoi(viper.GetString(key))
Expand All @@ -49,19 +68,22 @@ func ReadEnvInt(key string) int {
return v
}

// ReadEnvString - reads an environment variable as a string
func ReadEnvString(key string) string {
checkIfSet(key)
return viper.GetString(key)
}

// ReadEnvBool - reads environment variable as a boolean
func ReadEnvBool(key string) bool {
checkIfSet(key)
return viper.GetBool(key)
}

//CheckIfSet checks if all the necessary keys are set
func checkIfSet(key string) {
if !viper.IsSet(key) {
err := errors.New(fmt.Sprintf("Key %s is not set", key))
err := fmt.Errorf("Key %s is not set", key)
panic(err)
}
}
56 changes: 56 additions & 0 deletions db/cart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package db

import(
"context"
logger "github.com/sirupsen/logrus"
)

func (s *pgStore) AddToCart(ctx context.Context, cartId, productId int) (rowsAffected int64, err error) {
insert := `INSERT INTO cart (id, product_id, quantity) VALUES ($1, $2, 1)`
result, err := s.db.Exec(insert, cartId, productId)

yama11299 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.WithField("err", err.Error()).Error("Error adding to cart")
return
}

rowsAffected, err = result.RowsAffected()
if err != nil {
logger.WithField("err", err.Error()).Error("Error while fetching affected rows")
}

return
}

func (s *pgStore) RemoveFromCart(ctx context.Context, cartId, productId int) (rowsAffected int64, err error) {
delete := `DELETE FROM cart WHERE id = $1 AND product_id = $2`
result, err := s.db.Exec(delete, cartId, productId)

yama11299 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.WithField("err", err.Error()).Error("Error while removing from cart")
return
}

rowsAffected, err = result.RowsAffected()
if err != nil {
logger.WithField("err", err.Error()).Error("Error while fetching affected rows")
}

return
}

func (s *pgStore) UpdateIntoCart(ctx context.Context, quantity, cartId, productId int) (rowsAffected int64, err error) {
update := `UPDATE cart SET quantity = $1 WHERE id = $2 AND product_id = $3`
result, err := s.db.Exec(update, quantity, cartId, productId)

yama11299 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.WithField("err", err.Error()).Error("Error while updating into cart")
return
}

rowsAffected, err = result.RowsAffected()
if err != nil {
logger.WithField("err", err.Error()).Error("Error while fetching affected rows")
}
return
}
8 changes: 8 additions & 0 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@ import (
"context"
)

// Storer - an interface we use to expose methods that do stuff to the underlying database
type Storer interface {
ListUsers(context.Context) ([]User, error)
AuthenticateUser(context.Context, User) (User, error)
GetUser(context.Context, int) (User, error)
CreateBlacklistedToken(context.Context, BlacklistedToken) error
CheckBlacklistedToken(context.Context, string) (bool, int)
AddToCart(context.Context, int, int) (int64, error)
RemoveFromCart(context.Context, int, int) (int64, error)
UpdateIntoCart(context.Context, int, int, int) (int64, error)
//Create(context.Context, User) error
//GetUser(context.Context) (User, error)
//Delete(context.Context, string) error
Expand Down
1 change: 1 addition & 0 deletions db/pg.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/jmoiron/sqlx"
//lib/pq internally configures with database/sql library"
_ "github.com/lib/pq"
"github.com/mattes/migrate"
"github.com/mattes/migrate/database/postgres"
Expand Down
54 changes: 50 additions & 4 deletions db/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,67 @@ package db

import (
"context"

"database/sql"
logger "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
ae "joshsoftware/go-e-commerce/apperrors"
"time"
)

//User Struct for declaring attributes of User
type User struct {
Name string `db:"name" json:"full_name"`
Age int `db:"age" json:"age"`
ID int `db:"id" json:"id"`
FirstName string `db:"first_name" json:"first_name"`
LastName string `db:"last_name" json:"last_name"`
Email string `db:"email" json:"email"`
Mobile string `db:"mobile" json:"mobile"`
Address string `db:"address" json:"address"`
Password string `db:"password" json:"password"`
Country string `db:"country" json:"country"`
State string `db:"state" json:"state"`
City string `db:"city" json:"city"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}

//ListUsers function to fetch all Users From Database
func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) {
err = s.db.Select(&users, "SELECT * FROM users ORDER BY name ASC")
err = s.db.Select(&users, "SELECT * FROM users ORDER BY first_name ASC")
if err != nil {
logger.WithField("err", err.Error()).Error("Error listing users")
return
}

return
}

//GetUser function is used to Get a Particular User
func (s *pgStore) GetUser(ctx context.Context, id int) (user User, err error) {

err = s.db.Get(&user, "SELECT * FROM users WHERE id=$1", id)
if err != nil {
if err == sql.ErrNoRows {
err = ae.ErrRecordNotFound
}
logger.WithField("err", err.Error()).Error("Query Failed")
return
}

return
}

//AuthenticateUser Function checks if User has Registered before Login
// and Has Entered Correct Credentials
func (s *pgStore) AuthenticateUser(ctx context.Context, u User) (user User, err error) {

err = s.db.Get(&user, "SELECT * FROM users where email = $1", u.Email)
if err != nil {
logger.WithField("err", err.Error()).Error("No such User Available")
return
}

if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(u.Password)); err != nil {
// If the two passwords don't match, return a 401 status
logger.WithField("Error", err.Error())
}
return
}
48 changes: 48 additions & 0 deletions db/user_blacklisted_tokens.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package db

import (
"context"
"fmt"
"time"

logger "github.com/sirupsen/logrus"
)

//BlacklistedToken - struct representing a token to be blacklisted (logout)
type BlacklistedToken struct {
ID int `db:"id" json:"id"`
UserID float64 `db:"user_id" json:"user_id"`
Token string `db:"token" json:"token"`
ExpirationDate time.Time `db:"expiration_date" json:"expiration_date"`
}

const (
insertBlacklistedToken = `INSERT INTO user_blacklisted_tokens
(user_id, token, expiration_date)
VALUES ($1, $2, $3)`
)

//CreateBlacklistedToken function to insert the blacklisted token in database
func (s *pgStore) CreateBlacklistedToken(ctx context.Context, token BlacklistedToken) (err error) {
_, err = s.db.Exec(insertBlacklistedToken, token.UserID, token.Token, token.ExpirationDate)

if err != nil {
errMsg := fmt.Sprintf("Error inserting the blacklisted token for user with id %v", token.UserID)
logger.WithField("err", err.Error()).Error(errMsg)
return
}
return
}

//CheckBlacklistedToken function to check if token is blacklisted earlier
func (s *pgStore) CheckBlacklistedToken(ctx context.Context, token string) (bool, int) {

var userID int
query1 := fmt.Sprintf("SELECT user_id FROM user_blacklisted_tokens WHERE token='%s'", token)
err := s.db.QueryRow(query1).Scan(&userID)

if err != nil {
return false, -1
}
return true, userID
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ module joshsoftware/go-e-commerce
go 1.14

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gorilla/mux v1.8.0
github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.8.0
github.com/mattes/migrate v3.0.1+incompatible
github.com/rs/cors v1.7.0
github.com/sirupsen/logrus v1.6.0
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
github.com/urfave/cli v1.22.4
github.com/urfave/negroni v1.0.0
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
)
Loading