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

Issue-75, handlers and service layers created, bugs fixed, new SQL sсripts created #97

Merged
merged 1 commit into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 9 additions & 4 deletions api/ims/ims-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ info:
paths:
/signup:
post:
description: This endpoint handles user registartion requests, sent from sign-up form
operationId: signUp
description: This endpoint handles user registration requests, sent from sign-up form
operationId: SignUp
requestBody:
content:
application/json:
Expand Down Expand Up @@ -70,7 +70,7 @@ paths:
/email/available:
get:
description: This endpoint compares user's entered email with existing emails and displays an error if there is the same in DB
operationId: emailAvailable
operationId: EmailAvailable
parameters:
- in: query
name: email
Expand All @@ -90,7 +90,7 @@ paths:
/users/emailVerificationToken/verify:
get:
description: To complete the registration you need to confirm your email with token
operationId: emailVerificationToken
operationId: EmailVerificationToken
parameters:
- in: query
name: token
Expand Down Expand Up @@ -275,17 +275,22 @@ components:
firstName:
type: string
maxLength: 255
minLength: 1
description: User's first name
lastName:
type: string
maxLength: 255
minLength: 1
description: User's last name
email:
type: string
maxLength: 255
format: email
description: User's email
password:
type: string
maxLength: 255
minLength: 1
format: password
description: User's password
required:
Expand Down
3 changes: 2 additions & 1 deletion cmd/ims/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

package main

import "fmt"

func main() {
fmt.Println("I'm identity management service")
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ require (
github.com/sirupsen/logrus v1.5.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.0
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 // indirect
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904
gopkg.in/yaml.v2 v2.2.8
)
139 changes: 139 additions & 0 deletions pkg/ims/api/handlers/signup_handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package handlers

import (
"fmt"
"github.com/sirupsen/logrus"

"encoding/json"
"errors"
"io/ioutil"
"net/http"

"github.com/gorilla/mux"

"github.com/omc-college/management-system/pkg/ims/validation"
"github.com/omc-college/management-system/pkg/ims/models"
"github.com/omc-college/management-system/pkg/ims/repository/postgresql"
"github.com/omc-college/management-system/pkg/ims/service"
)

type SignUpHandler struct {
SignUpService *service.SignUpService
}

func NewSignUpHandler(service *service.SignUpService) *SignUpHandler {
return &SignUpHandler{
SignUpService: service,
}
}

// handleError handles existing error in handlers
func handleError(err error, w http.ResponseWriter) {
var error models.Error
var queryErr *postgresql.QueryError
var scanErr *postgresql.ScanError

if errors.As(err, &queryErr) {
error = models.Error{http.StatusInternalServerError, queryErr.Message}
} else if errors.As(err, &scanErr) {
error = models.Error{http.StatusInternalServerError, scanErr.Message}
} else if errors.Is(err, validation.ErrNoSymbols) || errors.Is(err, validation.ErrToMuchSymbols) {
error = models.Error{http.StatusBadRequest, err.Error()}
} else if errors.Is(err, validation.ErrEmailExists) || errors.Is(err, validation.ErrInvalidEmail){
error = models.Error{http.StatusBadRequest, err.Error()}
} else {
error = models.Error{http.StatusInternalServerError, err.Error()}
}

logrus.Errorf(error.Message)
w.WriteHeader(error.Code)
json.NewEncoder(w).Encode(error)
}

func (h *SignUpHandler)SignUp(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

var request models.SignupRequest
var err error

body, err := ioutil.ReadAll(r.Body)
if err != nil {
handleError(err, w)
return
}

err = json.Unmarshal(body, &request)
if err != nil {
handleError(err, w)
return
}

err = r.Body.Close()
if err != nil {
handleError(err, w)
return
}

err = validation.Data(&request)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make an issue to refactor validate function to make it reusable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created: #100

if err != nil {
handleError(err, w)
return
}

err = h.SignUpService.SignUp(&request)
if err != nil {
handleError(err, w)
return
}

}

func (h *SignUpHandler)EmailAvailable(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

var err error

params := mux.Vars(r)

if params["email"] == "" {
err = validation.ErrNoSymbols
handleError(err, w)
return
}

result, err := h.SignUpService.EmailAvailable(params["email"])
if err != nil {
handleError(err, w)
return
} else if result == true {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if result is not true?

err = validation.ErrEmailExists
handleError(err, w)
return
} else {
fmt.Fprintf(w, "email is not occupied")
}
}

func (h *SignUpHandler) CheckEmailVerificationToken(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

var tok models.EmailVerificationTokens
var err error

params := mux.Vars(r)

if params["verification_token"] == "" {
err = validation.ErrNoSymbols
handleError(err, w)
return
}

tok.VerificationToken = params["verification_token"]

err = h.SignUpService.EmailVerificationToken(&tok)
if err != nil {
handleError(err, w)
return
}

}
24 changes: 24 additions & 0 deletions pkg/ims/api/routers/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package routers

import (
"net/http"

"github.com/gorilla/mux"

"github.com/omc-college/management-system/pkg/ims/service"
"github.com/omc-college/management-system/pkg/ims/api/handlers"
)

//NewSignUpRouter inits Sign Up router
func NewSignUpRouter (service *service.SignUpService) *mux.Router {

signUpHandler := handlers.NewSignUpHandler(service)

router := mux.NewRouter()

router.HandleFunc("/signup", signUpHandler.SignUp).Methods(http.MethodPost)
router.HandleFunc("/email/available/{email}", signUpHandler.EmailAvailable).Methods(http.MethodGet)
router.HandleFunc("/users/emailVerificationToken/verify/{verification_token}", signUpHandler.CheckEmailVerificationToken).Methods(http.MethodGet)

return router
}
43 changes: 26 additions & 17 deletions pkg/ims/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ package models

import (
"github.com/dgrijalva/jwt-go"
"time"
)

type Users struct {
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
MobilePhone string `json:"mobile_phone"`
CreatedAt string `json:"created_at"`
ModifiedAt string `json:"modified_at"`
Roles []string `json:"roles"`
Verified bool `json:"verified"`
type User struct {
ID int `json:"id" db:"id"`
FirstName string `json:"first_name" db:"first_name"`
LastName string `json:"last_name" db:"last_name"`
Email string `json:"email" db:"email"`
MobilePhone string `json:"mobile_phone" db:"mobile_phone"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
ModifiedAt time.Time `json:"modified_at" db:"modified_at"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make an issue to make this field pointer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created: #110

Roles []string `json:"roles" db:"roles"`
Verified bool `json:"verified" db:"verified"`
}

type Claims struct {
Expand All @@ -24,19 +25,27 @@ type Claims struct {
}

type Credentials struct {
ID string `json:"id"`
PasswordHash string `json:"password_hash"`
Salt string `json:"salt"`
UpdatedAt string `json:"updated_at"`
ID int `json:"id" db:"id"`
PasswordHash string `json:"password_hash" db:"password_hash"`
Salt string `json:"salt" db:"salt"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

type EmailVerificationTokens struct {
ID string `json:"id"`
VerificationToken string `json:"verification_token"`
GeneretedAt string `json:"generated_at"`
ID int `json:"id" db:"id"`
VerificationToken string `json:"verification_token" db:"verification_token"`
GeneretedAt string `json:"generated_at" db:"generated_at"`
}

type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}

type SignupRequest struct {
FirstName string `json:"first_name" db:"first_name"`
LastName string `json:"last_name" db:"last_name"`
Email string `json:"email" db:"email"`
Password string `json:"password" db:"password"`
}

28 changes: 14 additions & 14 deletions pkg/ims/repository/postgresql/credentials_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,13 @@ import (
)

type CredRepository struct {
db *sqlx.DB
db sqlx.Ext
}

func NewCredentialsRepository(dbConnURL string) (*CredRepository, error) {
db, err := sqlx.Connect("pgx", dbConnURL)
if err != nil {
return nil, err
}
err = db.Ping()
if err != nil {
return nil, err
}
func NewCredentialsRepository(db sqlx.Ext) *CredRepository {
return &CredRepository{
db: db,
}, nil
}
}

//UpdateCredentials
Expand All @@ -35,22 +27,30 @@ func (cr *CredRepository) UpdateCredentials(c *models.Credentials) error {
}

func (cr *CredRepository) GetCredentialByUserID(usersId string) (*models.Credentials, error) {

c := &models.Credentials{}
err := cr.db.Get(&c, "SELECT * FROM credentials WHERE id=$1", usersId)

result, err := cr.db.Query("SELECT * FROM credentials WHERE id=$1", usersId)
if err != nil {
return nil, err
}

for result.Next() {
err = result.Scan(&c.ID, &c.PasswordHash, &c.Salt, &c.UpdatedAt)
if err != nil {
return nil, err
}
}

return c, nil
}

//InsertCredentials inserts user's credentials into DB
func (cr *CredRepository) InsertCredentials(c *models.Credentials) error {
_, err := cr.db.Exec("INSERT INTO credentials(id, password_hash, salt, updated_at) VALUES($1, $2, $3, CURRENT_TIMESTAMP)", c.ID, c.PasswordHash, c.Salt)
if err != nil {
return err
return QueryError{queryErrorMessage, err}
}

return nil
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BEGIN;

ALTER TABLE users
DROP COLUMN IF EXISTS roles,
DROP COLUMN IF EXISTS roles;

COMMIT;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ ALTER TABLE users
DROP COLUMN role;

ALTER TABLE users
ADD COLUMN roles []text;

ADD COLUMN roles text[];

COMMIT;
16 changes: 16 additions & 0 deletions pkg/ims/repository/postgresql/migrations/000006_change_id.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BEGIN;

SELECT MAX(id)+1 FROM credentials;
CREATE SEQUENCE cred_id_seq MINVALUE 1;
ALTER TABLE credentials ALTER id SET DEFAULT nextval('cred_id_seq');
ALTER SEQUENCE cred_id_seq OWNED BY credentials.id;

SELECT MAX(id)+1 FROM email_verification_tokens;
CREATE SEQUENCE token_id_seq MINVALUE 1;
ALTER TABLE email_verification_tokens ALTER id SET DEFAULT nextval('token_id_seq');
ALTER SEQUENCE token_id_seq OWNED BY email_verification_tokens.id;

UPDATE users SET mobile_phone = NULL WHERE mobile_phone = '';
CREATE UNIQUE INDEX mobile_phone ON users (mobile_phone);

COMMIT;
Loading