Skip to content

Commit

Permalink
add common and model
Browse files Browse the repository at this point in the history
  • Loading branch information
aorticweb committed Jan 28, 2022
1 parent d67b8bd commit eff25da
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 3 deletions.
20 changes: 20 additions & 0 deletions app/common/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package common

import "net/http"

type APIResponse struct {
Code int
Data interface{}
Message string
Err error
}

func NewBadResponse(code int, message string, err error) *APIResponse {
return &APIResponse{code, nil, message, err}
}

func NewGoodResponse(code int, data interface{}) *APIResponse {
return &APIResponse{code, data, "", nil}
}

var InvalidRequestResponse APIResponse = *NewBadResponse(http.StatusBadRequest, "invalid request", nil)
23 changes: 23 additions & 0 deletions app/common/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package common

import (
"errors"
"fmt"
"net/http"
"strconv"

"github.com/gorilla/mux"
)

func GetIDFromRequest(r *http.Request) (int64, error) {
vars := mux.Vars(r)
id, ok := vars["id"]
if !ok {
return 0, errors.New("id not found in request")
}
return strconv.ParseInt(id, 10, 64)
}

func WrapError(context string, e error) error {
return fmt.Errorf("%s: %s", context, e)
}
6 changes: 6 additions & 0 deletions app/model/group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package model

type GroupPost struct {
Groupname string `json:"groupname" validate:"required"`
Usernames []string `json:"usernames" validate:"required"`
}
126 changes: 126 additions & 0 deletions app/model/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package model

import (
"net/http"
"time"

c "github.com/aorticweb/msg-app/app/common"
"github.com/aorticweb/msg-app/app/crud"
"gorm.io/gorm"
)

type ReplyMessage struct {
Sender string `json:"sender" validate:"required"`
Subject string `json:"subject" validate:"required"`
Body string `json:"body" validate:"required"`
}

func (rm *ReplyMessage) ValidateSender(db *gorm.DB) (*int64, *c.APIResponse) {
sender, exist, err := crud.FindUser(db, rm.Sender)
if err != nil {
return nil, c.NewBadResponse(http.StatusInternalServerError, "", c.WrapError("failed to query users", err))
}
if !exist {
return nil, c.NewBadResponse(http.StatusNotFound, "user with given username does not exist", nil)
}
return &sender.ID, nil
}

func (rm *ReplyMessage) Validate(db *gorm.DB, reID int64) (*crud.Message, *c.APIResponse) {
msg := crud.Message{
Subject: rm.Subject,
Body: rm.Body,
SentAt: time.Now().UTC(),
}
senderID, badResp := rm.ValidateSender(db)
if badResp != nil {
return nil, badResp
}
msg.SenderID = senderID
reMessage, exist, err := crud.GetMessage(db, reID)
if err != nil {
return nil, c.NewBadResponse(http.StatusInternalServerError, "", c.WrapError("failed to query message", err))
}
if !exist {
return nil, c.NewBadResponse(http.StatusNotFound, "message with given id does not exist", nil)
}
msg.REID = &reMessage.ID
return &msg, nil
}

type ComposedMessage struct {
ReplyMessage
Recipient map[string]string `json:"recipient"` // Either crud.User or crud.Group
}

func (m *ComposedMessage) Validate(db *gorm.DB) (*crud.Message, *c.APIResponse) {
msg := crud.Message{
Subject: m.Subject,
Body: m.Body,
SentAt: time.Now().UTC(),
}
username, usernameFound := m.Recipient["username"]
groupname, groupnameFound := m.Recipient["groupname"]
if usernameFound && groupnameFound {
return nil, c.NewBadResponse(http.StatusBadRequest, "invalid request", nil)
}
senderID, badResp := m.ValidateSender(db)
if badResp != nil {
return nil, badResp
}
msg.SenderID = senderID
if usernameFound {
user, exist, err := crud.FindUser(db, username)
if err != nil {
return nil, c.NewBadResponse(http.StatusInternalServerError, "", c.WrapError("failed to query user", err))
}
if !exist {
return nil, c.NewBadResponse(http.StatusNotFound, "recipient user with given username does not exist", nil)
}
msg.RecipientID = &user.ID
return &msg, nil
}
if groupnameFound {
group, exist, err := crud.FindGroup(db, groupname)
if err != nil {
return nil, c.NewBadResponse(http.StatusInternalServerError, "", c.WrapError("failed to query group", err))
}
if !exist {
return nil, c.NewBadResponse(http.StatusNotFound, "recipient group with given groupname does not exist", nil)
}
msg.GroupID = &group.ID
return &msg, nil
}
return nil, c.NewBadResponse(http.StatusBadRequest, "invalid request", nil)
}

type Message struct {
ComposedMessage
ID int64 `json:"id" validate:"required"`
RE int64 `json:"re"`
SentAt time.Time `json:"sent_at" validate:"required"`
}

func ResponseMessageFromDBMessage(m *crud.Message) *Message {
rv := Message{
ID: m.ID,
RE: *m.REID,
ComposedMessage: ComposedMessage{
ReplyMessage: ReplyMessage{
Subject: m.Subject,
Body: m.Body,
Sender: m.Sender.Username,
},
Recipient: make(map[string]string),
},
SentAt: m.SentAt,
}
// Purposefully not raising an error here if
// both user and group are missing because of db constraint
if m.Group != nil {
rv.Recipient["groupname"] = m.Group.Groupname
} else if m.Recipient != nil {
rv.Recipient["username"] = m.Recipient.Username
}
return &rv
}
8 changes: 5 additions & 3 deletions db/migrations/20220125181413_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ create table if not exists user_group (
id SERIAL primary key,
user_id int references public.user(id) not null,
group_id int references public.group(id) not null,
CONSTRAINT unique_user_group UNIQUE (user_id, group_id)
CONSTRAINT user_group_unique_user_group UNIQUE (user_id, group_id)
);
create table if not exists message (
id SERIAL primary key,
re_id int references message(id),
sender_id int references public.user(id) not null,
recipient_id int references public.user(id) not null,
recipient_id int references public.user(id) null,
group_id int references public.group(id) null,
subject text,
body text,
sent_at timestamp without time zone not null
sent_at timestamp without time zone not null,
CHECK (recipient_id is not null OR group_id is not null)
)
-- migrate:down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.17

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
Expand Down

0 comments on commit eff25da

Please sign in to comment.