Skip to content

Commit a9f2b0e

Browse files
committed
refactor with clean code
1 parent d886b14 commit a9f2b0e

File tree

6 files changed

+194
-152
lines changed

6 files changed

+194
-152
lines changed

addURLHandler.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
"sync"
6+
7+
"github.com/gin-gonic/gin"
8+
)
9+
10+
type POSTData struct {
11+
LongURL string `json:"URL"`
12+
}
13+
14+
type POSTresponse struct {
15+
Status string `json:"status"`
16+
ShortName string `json:"short"`
17+
}
18+
19+
func AddURLHandler(c *gin.Context) {
20+
var requestBody POSTData
21+
c.ShouldBindJSON(&requestBody)
22+
23+
var shortName string
24+
if db.URLExist(requestBody.LongURL) {
25+
shortName = db.GetShortName(requestBody.LongURL)
26+
} else {
27+
shortName = GenerateShortName(requestBody.LongURL)
28+
storeIntoDBRedis(requestBody.LongURL, shortName)
29+
}
30+
31+
c.JSON(http.StatusOK, POSTresponse{
32+
Status: "Exist",
33+
ShortName: shortName,
34+
})
35+
}
36+
37+
func storeIntoDBRedis(LongURL, shortName string) {
38+
var wg sync.WaitGroup
39+
wg.Add(2)
40+
// add in database
41+
go func(LongURL string, shortName string) {
42+
defer wg.Done()
43+
db.AddURLPair(LongURL, shortName)
44+
}(LongURL, shortName)
45+
// add in redis
46+
go func(LongURL string, shortName string) {
47+
defer wg.Done()
48+
RedisAdd(LongURL, shortName)
49+
}(LongURL, shortName)
50+
51+
wg.Wait()
52+
}

database.go

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,73 +11,93 @@ import (
1111
"go.mongodb.org/mongo-driver/mongo/options"
1212
)
1313

14-
/* Used to create a singleton object of MongoDB client.
15-
Initialized and exposed through GetMongoClient().*/
16-
var clientInstance *mongo.Client
17-
18-
//Used during creation of singleton client object in GetMongoClient().
19-
var clientInstanceError error
20-
21-
//Used to execute client creation procedure only once.
22-
var mongoOnce sync.Once
23-
24-
var collection *mongo.Collection
25-
26-
//I have used below constants just to hold required database config's.
27-
var (
28-
CONNECTIONSTRING = "mongodb://" + os.Getenv("DB_URL")
29-
)
14+
type DBStruct struct {
15+
Client *mongo.Client
16+
Collection *mongo.Collection
17+
}
3018

31-
type data struct {
32-
LongURL string `json:"longurl"`
33-
ShortName string `json:"shortname"`
19+
type DB interface {
20+
GetURL(string) string
21+
GetShortName(string) string
22+
AddURLPair(string, string)
23+
URLExist(string) bool
24+
ShortNameExist(string) bool
3425
}
3526

27+
var db DB
28+
3629
//GetMongoClient - Return mongodb connection to work with
3730
func InitDatabase() {
31+
client := connectDB()
32+
_db := DBStruct{
33+
Client: client,
34+
Collection: client.Database("url").Collection("url"),
35+
}
36+
db = _db
37+
}
38+
39+
func connectDB() *mongo.Client {
40+
var mongoOnce sync.Once
41+
var client *mongo.Client
42+
var clientInstanceError error
43+
var CONNECTIONSTRING = "mongodb://" + os.Getenv("DB_URL")
3844
//Perform connection creation operation only once.
3945
mongoOnce.Do(func() {
4046
// Set client options
4147
clientOptions := options.Client().ApplyURI(CONNECTIONSTRING)
4248
// Connect to MongoDB
43-
client, err := mongo.Connect(context.TODO(), clientOptions)
49+
_client, err := mongo.Connect(context.TODO(), clientOptions)
4450
if err != nil {
4551
clientInstanceError = err
4652
}
4753
// Check the connection
48-
err = client.Ping(context.TODO(), nil)
54+
err = _client.Ping(context.TODO(), nil)
4955
if err != nil {
5056
clientInstanceError = err
5157
}
52-
clientInstance = client
58+
client = _client
5359
})
5460
if clientInstanceError != nil {
55-
fmt.Println("Connection error")
61+
panic("DB connection error")
5662
}
57-
collection = clientInstance.Database("url").Collection("url")
58-
//return clientInstance, clientInstanceError
63+
return client
5964
}
6065

61-
func DatabaseAdd(LongURL string, shortName string) {
66+
type data struct {
67+
LongURL string `json:"longurl"`
68+
ShortName string `json:"shortname"`
69+
}
70+
71+
func (dbs DBStruct) AddURLPair(LongURL string, shortName string) {
6272
toInsert := data{LongURL: LongURL, ShortName: shortName}
63-
_, err := collection.InsertOne(context.TODO(), toInsert)
73+
_, err := dbs.Collection.InsertOne(context.TODO(), toInsert)
6474
if err != nil {
6575
fmt.Println("Insert Error")
6676
}
67-
//id := res.InsertedID
68-
//fmt.Println("Insert " + fmt.Sprintf("%v", id) + " record !")
6977
}
7078

71-
func DatabaseGet(shortName string) (string, error) {
72-
var result data
79+
func (dbs DBStruct) GetURL(shortName string) string {
80+
result, _ := dbs.find("shortname", shortName)
81+
return result.LongURL
82+
}
7383

74-
err := collection.FindOne(context.TODO(), bson.M{"shortname": shortName}).Decode(&result)
75-
return result.LongURL, err
84+
func (dbs DBStruct) URLExist(LongURL string) bool {
85+
_, err := dbs.find("longurl", LongURL)
86+
return err == nil
7687
}
7788

78-
func DatabaseURLExist(LongURL string) (string, bool) {
79-
var result data
89+
func (dbs DBStruct) ShortNameExist(shortName string) bool {
90+
_, err := dbs.find("shortname", shortName)
91+
return err == nil
92+
}
8093

81-
err := collection.FindOne(context.TODO(), bson.M{"longurl": LongURL}).Decode(&result)
82-
return result.ShortName, (err == nil)
94+
func (dbs DBStruct) GetShortName(LongURL string) string {
95+
result, _ := dbs.find("longurl", LongURL)
96+
return result.ShortName
97+
}
98+
99+
func (dbs DBStruct) find(columnName, value string) (data, error) {
100+
var result data
101+
err := dbs.Collection.FindOne(context.TODO(), bson.M{columnName: value}).Decode(&result)
102+
return result, err
83103
}

getURLHandler.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
)
8+
9+
type GETresponse struct {
10+
Status string `json:"status"`
11+
URL string `json:"url"`
12+
}
13+
14+
func GetURLHandler(c *gin.Context) {
15+
shortNameQuery := c.Param("shortName")
16+
17+
LongURL, err := RedisGet(shortNameQuery)
18+
if err != nil {
19+
DatabaseLongURL := db.GetURL(shortNameQuery)
20+
if DatabaseLongURL == "" {
21+
c.JSON(http.StatusNotFound, GETresponse{
22+
Status: "Not OK",
23+
URL: "",
24+
})
25+
} else {
26+
RedisAdd(DatabaseLongURL, shortNameQuery)
27+
c.JSON(http.StatusOK, GETresponse{
28+
Status: "(Database)OK",
29+
URL: DatabaseLongURL,
30+
})
31+
}
32+
} else {
33+
c.JSON(http.StatusOK, GETresponse{
34+
Status: "(Redis)OK",
35+
URL: LongURL,
36+
})
37+
}
38+
}

main.go

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package main
22

33
import (
4-
"net/http"
5-
"sync"
6-
74
"github.com/gin-gonic/gin"
85
)
96

@@ -13,85 +10,14 @@ func init() {
1310
InitZookeeper()
1411
}
1512

16-
type POSTData struct {
17-
LongURL string `json:"URL"`
18-
}
19-
20-
type POSTresponse struct {
21-
Status string `json:"status"`
22-
ShortName string `json:"short"`
23-
}
24-
type GETresponse struct {
25-
Status string `json:"status"`
26-
URL string `json:"url"`
27-
}
28-
2913
func main() {
3014
r := setupRouter()
3115
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
3216
}
17+
3318
func setupRouter() *gin.Engine {
3419
r := gin.Default()
35-
r.POST("/add", func(c *gin.Context) {
36-
var body POSTData
37-
c.ShouldBindJSON(&body)
38-
// validate it
39-
// generate the corresponding short name
40-
shortName, isExist := DatabaseURLExist(body.LongURL)
41-
if isExist {
42-
c.JSON(http.StatusOK, POSTresponse{
43-
Status: "Exist",
44-
ShortName: shortName,
45-
})
46-
} else {
47-
var wg sync.WaitGroup
48-
wg.Add(2)
49-
shortName := GetShortName(body.LongURL)
50-
// add in database
51-
go func(LongURL string, shortName string) {
52-
defer wg.Done()
53-
DatabaseAdd(LongURL, shortName)
54-
}(body.LongURL, shortName)
55-
// add in redis
56-
go func(LongURL string, shortName string) {
57-
defer wg.Done()
58-
RedisAdd(LongURL, shortName)
59-
}(body.LongURL, shortName)
60-
61-
wg.Wait()
62-
63-
c.JSON(http.StatusOK, POSTresponse{
64-
Status: "OK",
65-
ShortName: shortName,
66-
})
67-
}
68-
})
69-
r.GET("/:shortName", func(c *gin.Context) {
70-
shortName := c.Param("shortName")
71-
// find in redis first
72-
LongURL, err := RedisGet(shortName)
73-
//LongURL, err := DatabaseGet(shortName)
74-
if err != nil {
75-
// if not found, find in database
76-
DatabaseLongURL, err_ := DatabaseGet(shortName)
77-
if err_ != nil {
78-
c.JSON(http.StatusNotFound, GETresponse{
79-
Status: "Not OK",
80-
URL: "",
81-
})
82-
} else {
83-
RedisAdd(DatabaseLongURL, shortName)
84-
c.JSON(http.StatusOK, GETresponse{
85-
Status: "(Database)OK",
86-
URL: DatabaseLongURL,
87-
})
88-
}
89-
} else {
90-
c.JSON(http.StatusOK, GETresponse{
91-
Status: "(Redis)OK",
92-
URL: LongURL,
93-
})
94-
}
95-
})
20+
r.POST("/add", AddURLHandler)
21+
r.GET("/:shortName", GetURLHandler)
9622
return r
9723
}

main_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,33 @@ type testingSuite struct {
2626
Router *gin.Engine
2727
}
2828

29+
type tinyURLTesting interface {
30+
Test(string)
31+
}
32+
33+
var tb tinyURLTesting
34+
2935
func TestShortURLService(t *testing.T) {
30-
tb := testingSuite{
36+
_tb := testingSuite{
3137
TestBed: t,
3238
Router: setupRouter(),
3339
}
40+
tb = _tb
41+
42+
tb.Test("https://www.google.com")
3443
for i := 0; i < 10; i++ {
3544
originalURL := "https://" + randomString(30)
36-
shortURL := tb.requestShortURL(originalURL)
37-
URL := tb.requestLongURL(shortURL)
38-
assert.Equal(t, originalURL, URL)
45+
tb.Test(originalURL)
3946
}
47+
tb.Test("https://www.google.com")
48+
}
4049

50+
func (tb testingSuite) Test(originalURL string) {
51+
shortURL := tb.requestShortURL(originalURL)
52+
URL := tb.requestLongURL(shortURL)
53+
assert.Equal(tb.TestBed, originalURL, URL)
4154
}
55+
4256
func (tb testingSuite) requestShortURL(originalURL string) string {
4357
gin.SetMode(gin.TestMode)
4458
postBody, _ := json.Marshal(map[string]string{

0 commit comments

Comments
 (0)