Skip to content

Commit 489a1ad

Browse files
committed
Implement create shortcuts endpoint
Add generic UUID setter Add ON UPDATE constraint Implement create method for Shortcut struct Add SetID() to DBModel interface Add HTTP tests Improve log output on -serve Signed-off-by: David Kröll <[email protected]>
1 parent f9889ed commit 489a1ad

File tree

8 files changed

+110
-21
lines changed

8 files changed

+110
-21
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
# Output of the go coverage tool, specifically when used with LiteIDE
1414
*.out
1515

16-
/.idea
16+
.idea
1717
.env

api.http

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
POST http://localhost:9999/api/shortcuts
2+
Content-Type: application/json
3+
Accept: application/json
4+
5+
{
6+
"shortIdentifier": "htl2",
7+
"redirectURL": "http://htl-villach.at",
8+
"redirectStatus": 301,
9+
"validThru": "2019-04-01T08:00:00.000+01:00"
10+
}
11+
12+
###

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func main() {
4141
}
4242

4343
if *serve {
44-
log.Println("Starting HTTP Server")
44+
log.Println("HTTP Server listening on port " + os.Getenv("PORT"))
4545
router := routes.InitRouter()
4646
log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), &router))
4747
} else if !*isMigrate && !*isFresh && *seedFile == "" {

models/database.go

+11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ type Relateable interface {
2121
type DBModel interface {
2222
Save() (err error)
2323
Delete() (err error)
24+
SetID(id string)
25+
}
26+
27+
func setUUIDAsID(m DBModel) error {
28+
row := db.QueryRow("SELECT UUID();")
29+
var id string
30+
if err := row.Scan(&id); err != nil {
31+
return err
32+
}
33+
m.SetID(id)
34+
return nil
2435
}
2536

2637
type DBConfig struct {

models/migrations.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const (
1919
Lastname VARCHAR(50) NOT NULL,
2020
Password BINARY(60) NOT NULL,
2121
CreatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
22-
UpdatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
22+
UpdatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2323
LastLogin DATETIME,
2424
CHECK (UpdatedAt >= CreatedAt)
2525
)
@@ -33,7 +33,7 @@ const (
3333
RedirectURL VARCHAR(1024) NOT NULL,
3434
RedirectStatus INT(3) NOT NULL,
3535
CreatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
36-
UpdatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
36+
UpdatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
3737
ValidThru DATETIME NOT NULL,
3838
UserID CHAR(36) NOT NULL,
3939

models/shortcuts.go

+49-10
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,27 @@ import (
77
)
88

99
type Shortcut struct {
10-
ID string
11-
ShortIdentifer string
12-
RedirectURL string
13-
RedirectStatus int
14-
CreatedAt time.Time
15-
UpdatedAt time.Time
16-
ValidThru time.Time
17-
User *User
10+
ID string `json:"id"`
11+
ShortIdentifer string `json:"shortIdentifier"`
12+
RedirectURL string `json:"redirectURL"`
13+
RedirectStatus int `json:"redirectStatus"`
14+
CreatedAt time.Time `json:"createdAt"`
15+
UpdatedAt time.Time `json:"updatedAt"`
16+
ValidThru time.Time `json:"validThru"`
17+
User *User `json:"user"`
1818
userID string
1919
}
2020

2121
const (
22-
selectShortcut string = `SELECT * FROM Shortcuts WHERE %s = ?;`
23-
loadRelatedShortcut string = `SELECT * FROM Users WHERE ID = ?;`
22+
selectShortcut = `SELECT * FROM Shortcuts WHERE %s = ?;`
23+
loadRelatedShortcut = `SELECT * FROM Users WHERE ID = ?;`
24+
insertShortcut = `INSERT INTO Shortcuts (ID, ShortIdentifier, RedirectURL, RedirectStatus, CreatedAt, UpdatedAt, ValidThru, UserID) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?);`
25+
updateshortcut = `UPDATE Shortcuts
26+
SET ShortIdentifier = ?
27+
AND RedirectURL = ?
28+
AND RedirectStatus = ?
29+
AND ValidThru = ?
30+
WHERE ID = ?;`
2431
)
2532

2633
func ShortcutBy(matcher MatchingField, value string) (*Shortcut, error) {
@@ -48,3 +55,35 @@ func (s *Shortcut) LoadRelated() (err error) {
4855

4956
return nil
5057
}
58+
59+
func (s *Shortcut) SetID(id string) {
60+
s.ID = id
61+
}
62+
63+
func (s *Shortcut) Save() error {
64+
if s.ID == "" {
65+
return s.create()
66+
}
67+
return s.update()
68+
}
69+
70+
func (s *Shortcut) create() error {
71+
if err := setUUIDAsID(s); err != nil {
72+
return err
73+
}
74+
75+
// TODO remove
76+
s.userID = "00366fb9-cd76-47e0-b3c6-cbd611650d9e"
77+
78+
_, err := db.Exec(insertShortcut, s.ID, s.ShortIdentifer, s.RedirectURL, s.RedirectStatus, s.ValidThru, s.userID)
79+
return err
80+
}
81+
82+
func (s *Shortcut) update() error {
83+
_, err := db.Exec(updateshortcut, s.ShortIdentifer, s.RedirectURL, s.RedirectStatus, s.ValidThru, s.ID)
84+
return err
85+
}
86+
87+
func (s *Shortcut) Delete() (err error) {
88+
panic("implement me")
89+
}

models/users.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
)
66

77
const (
8-
getUUID string = `SELECT UUID()`
98
insertUser string = `INSERT INTO Users (ID, Email, Firstname, Lastname, Password) VALUES (?, ?, ?, ?, ?);`
109
selectUserById string = `SELECT * FROM Users WHERE ID = ?;`
1110
selectUserLimitOffset string = `SELECT * FROM Users LIMIT ?, ?;`
@@ -22,6 +21,10 @@ type User struct {
2221
LastLogin time.Time
2322
}
2423

24+
func (u *User) SetID(id string) {
25+
u.ID = id
26+
}
27+
2528
func (u *User) Save() error {
2629
if u.ID == "" {
2730
return u.create()
@@ -30,15 +33,11 @@ func (u *User) Save() error {
3033
}
3134

3235
func (u *User) create() error {
33-
result, err := db.Query(getUUID)
34-
if err != nil {
35-
return err
36-
}
37-
if err := result.Scan(&u.ID); err != nil {
36+
if err := setUUIDAsID(u); err != nil {
3837
return err
3938
}
4039

41-
_, err = db.Exec(insertUser, u.ID, u.Email, u.Firstname, u.LastLogin, u.Password)
40+
_, err := db.Exec(insertUser, u.ID, u.Email, u.Firstname, u.LastLogin, u.Password)
4241
return err
4342
}
4443

routes/shortcuts.go

+28
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
11
package routes
22

33
import (
4+
"encoding/json"
45
"github.com/davidkroell/shortcut/models"
56
"github.com/gorilla/mux"
67
"net/http"
78
)
89

910
func createShortcut(w http.ResponseWriter, r *http.Request) {
11+
s := models.Shortcut{}
1012

13+
if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
14+
Response{
15+
Success: false,
16+
Code: 1002,
17+
Message: "Malformed JSON body",
18+
}.JSON(w, 400)
19+
return
20+
}
21+
22+
if err := s.Save(); err != nil {
23+
Response{
24+
Success: false,
25+
Code: 1003,
26+
Message: "Internal server error. " + err.Error(),
27+
}.JSON(w, 500)
28+
return
29+
}
30+
31+
Response{
32+
Success: true,
33+
Message: "created",
34+
}.JSON(w, 201)
1135
}
1236

1337
func listShortcuts(w http.ResponseWriter, r *http.Request) {
@@ -27,6 +51,7 @@ func deleteShortcut(w http.ResponseWriter, r *http.Request) {
2751
}
2852

2953
func forwardShortcut(w http.ResponseWriter, r *http.Request) {
54+
//start := time.Now()
3055
vars := mux.Vars(r)
3156
shortId := vars["shortId"]
3257

@@ -53,5 +78,8 @@ func forwardShortcut(w http.ResponseWriter, r *http.Request) {
5378
return
5479
}
5580

81+
//end := time.Since(start)
82+
//fmt.Println(end.Nanoseconds() / 1000)
83+
5684
http.Redirect(w, r, shortcut.RedirectURL, shortcut.RedirectStatus)
5785
}

0 commit comments

Comments
 (0)