Skip to content

Commit 7efcb99

Browse files
authored
Merge pull request #37 from SanctumLabs/develop
No redirects for Expired URLs
2 parents 880b483 + 3fdfa5d commit 7efcb99

File tree

20 files changed

+355
-47
lines changed

20 files changed

+355
-47
lines changed

.env.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ DATABASE_PASSWORD=curtzPassword
1616
DATABASE_PORT=27017
1717
DATABASE_USES_SRV=true
1818
CACHE_HOST=localhost
19-
CACHE_PORT=6370
19+
CACHE_PORT=6379
2020
CACHE_USERNAME=curtzUser
2121
CACHE_PASSWORD=curtzPassword
2222
CACHE_REQUIRE_AUTH=false

app/api/v1/url/handlers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func (hdl *urlRouter) updateUrl(c *gin.Context) {
117117
keywords := payload.Keywords
118118
expiresOn := payload.ExpiresOn
119119

120-
updateCmd, err := contracts.NewUpdateUrlCommand(userId.(string), urlId, customAlias, keywords, expiresOn)
120+
updateCmd, err := contracts.NewUpdateUrlRequest(userId.(string), urlId, customAlias, keywords, expiresOn)
121121
if err != nil {
122122
c.AbortWithStatusJSON(http.StatusBadRequest, map[string]any{"message": err.Error()})
123123
return

app/cmd/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/sanctumlabs/curtz/app/api/v1/url"
1212
"github.com/sanctumlabs/curtz/app/config"
1313
"github.com/sanctumlabs/curtz/app/internal/core/urlsvc"
14+
urlReadSvc "github.com/sanctumlabs/curtz/app/internal/core/urlsvc/read"
15+
urlWriteSvc "github.com/sanctumlabs/curtz/app/internal/core/urlsvc/write"
1416
"github.com/sanctumlabs/curtz/app/internal/core/usersvc"
1517
"github.com/sanctumlabs/curtz/app/internal/repositories"
1618
"github.com/sanctumlabs/curtz/app/internal/services/auth"
@@ -187,8 +189,8 @@ func main() {
187189

188190
userService := usersvc.NewUserSvc(repository.GetUserRepo(), notificationSvc)
189191
urlService := urlsvc.NewUrlSvc(repository.GetUrlReadRepo(), repository.GetUrlWriteRepo(), userService, cache)
190-
urlReadService := urlsvc.NewUrlReadSvc(repository.GetUrlReadRepo(), userService, cache)
191-
urlWriteService := urlsvc.NewUrlWriteSvc(repository.GetUrlWriteRepo(), userService)
192+
urlReadService := urlReadSvc.NewUrlReadSvc(repository.GetUrlReadRepo(), userService, cache)
193+
urlWriteService := urlWriteSvc.NewUrlWriteSvc(repository.GetUrlWriteRepo(), userService)
192194

193195
baseUri := "/api/v1/curtz"
194196

app/internal/core/contracts/commands.go

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,36 @@ import (
66
"github.com/sanctumlabs/curtz/app/pkg/validators"
77
)
88

9-
type CreateUrlCommand struct {
10-
userID string
11-
originalUrl string
12-
customAlias string
13-
expiresOn time.Time
14-
keywords []string
15-
}
16-
17-
type UpdateUrlCommand struct {
9+
type UpdateUrlRequest struct {
1810
UserId string
1911
UrlId string
2012
CustomAlias string
2113
Keywords []string
2214
ExpiresOn *time.Time
2315
}
2416

25-
func NewUpdateUrlCommand(userId, urlId, customAlias string, keywords []string, expiresOn *time.Time) (UpdateUrlCommand, error) {
17+
func NewUpdateUrlRequest(userId, urlId, customAlias string, keywords []string, expiresOn *time.Time) (UpdateUrlRequest, error) {
2618
if err := validators.IsValidUserId(userId); err != nil {
27-
return UpdateUrlCommand{}, err
19+
return UpdateUrlRequest{}, err
2820
}
2921

3022
if err := validators.IsValidUrlId(urlId); err != nil {
31-
return UpdateUrlCommand{}, err
23+
return UpdateUrlRequest{}, err
3224
}
3325

3426
if len(customAlias) != 0 {
3527
if err := validators.IsValidCustomAlias(customAlias); err != nil {
36-
return UpdateUrlCommand{}, err
28+
return UpdateUrlRequest{}, err
3729
}
3830
}
3931

4032
if expiresOn != nil {
4133
if err := validators.IsValidExpirationTime(*expiresOn); err != nil {
42-
return UpdateUrlCommand{}, err
34+
return UpdateUrlRequest{}, err
4335
}
4436
}
4537

46-
return UpdateUrlCommand{
38+
return UpdateUrlRequest{
4739
UserId: userId,
4840
UrlId: urlId,
4941
CustomAlias: customAlias,

app/internal/core/contracts/services.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ type SmsService interface {
2929
// CacheService interface to be used by services that implement cache like functionality
3030
type CacheService interface {
3131
LookupUrl(shortCode string) (string, error)
32-
SaveUrl(shortCode, originalUrl string) (string, error)
32+
SaveURL(shortCode, originalUrl string, expiryTime time.Duration) (string, error)
3333
}

app/internal/core/contracts/update_url_command_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ var testCases = []testCase{
5757
},
5858
}
5959

60-
func TestNewUpdateUrlCommand(t *testing.T) {
60+
func TestNewUpdateUrlRequest(t *testing.T) {
6161
for _, tc := range testCases {
6262
t.Run(tc.name, func(t *testing.T) {
63-
actual, err := NewUpdateUrlCommand(tc.userId, tc.urlId, tc.customAlias, tc.keywords, &tc.expiresOn)
63+
actual, err := NewUpdateUrlRequest(tc.userId, tc.urlId, tc.customAlias, tc.keywords, &tc.expiresOn)
6464
if err != tc.expectedErr {
65-
t.Errorf("NewUpdateUrlCommand(%s, %s, %s, %s, %v) = %v expected error %v, got %v", tc.userId, tc.urlId, tc.customAlias, tc.keywords, tc.expiresOn, tc.expectedErr, tc.expectedErr, err)
65+
t.Errorf("NewUpdateUrlRequest(%s, %s, %s, %s, %v) = %v expected error %v, got %v", tc.userId, tc.urlId, tc.customAlias, tc.keywords, tc.expiresOn, tc.expectedErr, tc.expectedErr, err)
6666
} else if tc.expectedErr == nil {
6767
assert.Equal(t, tc.userId, actual.UserId)
6868
assert.Equal(t, tc.urlId, actual.UrlId)
@@ -74,14 +74,14 @@ func TestNewUpdateUrlCommand(t *testing.T) {
7474
}
7575
}
7676

77-
func BenchmarkNewUpdateUrlCommand(b *testing.B) {
77+
func BenchmarkNewUpdateUrlRequest(b *testing.B) {
7878
if testing.Short() {
7979
b.Skip("skipping benchmark")
8080
}
8181

8282
for i := 0; i < b.N; i++ {
8383
for _, tc := range testCases {
84-
_, _ = NewUpdateUrlCommand(tc.userId, tc.urlId, tc.customAlias, tc.keywords, &tc.expiresOn)
84+
_, _ = NewUpdateUrlRequest(tc.userId, tc.urlId, tc.customAlias, tc.keywords, &tc.expiresOn)
8585
}
8686
}
8787
}

app/internal/core/contracts/url_service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ type UrlReadService interface {
2424
// UrlWriteService performs write operations on URLs
2525
type UrlWriteService interface {
2626
CreateUrl(userID string, originalUrl string, customAlias string, expiresOn time.Time, keywords []string) (entities.URL, error)
27-
UpdateUrl(url UpdateUrlCommand) (entities.URL, error)
27+
UpdateUrl(url UpdateUrlRequest) (entities.URL, error)
2828
Remove(id string) error
2929
}

app/internal/core/entities/url.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ func NewUrl(userId identifier.ID, originalUrl string, customAlias string, expire
109109
}, nil
110110
}
111111

112-
// IsActive checks if the url model is active
113-
// It returns true if url is not marked deleted or expired, false otherwise.
112+
// IsActive checks if the url is active or not expired.
114113
func (url URL) IsActive() bool {
115114
return url.ExpiresOn.In(time.UTC).After(time.Now().In(time.UTC))
116115
}
@@ -152,6 +151,16 @@ func (url URL) SetCustomAlias(customAlias string) error {
152151
return nil
153152
}
154153

154+
// GetExpiryDuration returns as a time.Duration how long before the url expires
155+
// This returns an absolute value after subtracting time.Now()
156+
func (url URL) GetExpiryDuration() time.Duration {
157+
duration := time.Until(url.ExpiresOn)
158+
if duration >= 0 {
159+
return duration
160+
}
161+
return -duration
162+
}
163+
155164
// Prefix returns the url prefix for logging
156165
func (url URL) Prefix() string {
157166
return fmt.Sprintf("url-%s-%s", url.ID, url.ShortCode)

app/internal/core/entities/url_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55
"time"
66

7+
"bou.ke/monkey"
78
"github.com/sanctumlabs/curtz/app/pkg/errdefs"
89
"github.com/sanctumlabs/curtz/app/pkg/identifier"
910
)
@@ -99,3 +100,42 @@ func BenchmarkNewUrl(b *testing.B) {
99100
})
100101
}
101102
}
103+
104+
type urlExpiryTestCase struct {
105+
urlTestCase
106+
duration time.Duration
107+
}
108+
109+
func TestGetExpiryDuration(t *testing.T) {
110+
monkey.Patch(time.Now, func() time.Time {
111+
return time.Date(2022, 9, 19, 16, 20, 00, 651387237, time.UTC)
112+
})
113+
114+
urlExpiryTestCases := []urlExpiryTestCase{
115+
{
116+
urlTestCase: urlTestCase{
117+
name: "should return duration expiry for given URL",
118+
userId: identifier.New(),
119+
url: "http://example.com",
120+
alias: "",
121+
expiresOn: time.Now().Add(time.Hour * 1),
122+
keywords: []string{},
123+
expectedError: nil,
124+
},
125+
duration: time.Until(time.Now().Add(time.Hour * 1)),
126+
},
127+
}
128+
129+
for _, tc := range urlExpiryTestCases {
130+
t.Run(tc.name, func(t *testing.T) {
131+
url, err := NewUrl(tc.userId, tc.url, tc.alias, tc.expiresOn, tc.keywords)
132+
if err != tc.expectedError {
133+
t.Errorf("NewUrl(%v, %s, %s, %s, %v) = (%v, %v) expected error: %v, got: %v", tc.userId, tc.url, tc.alias, tc.expiresOn, tc.keywords, url, err, tc.expectedError, err)
134+
}
135+
actualExpiry := url.GetExpiryDuration()
136+
if tc.duration != actualExpiry {
137+
t.Errorf("url.GetExpiryDuration() = %d expected = %d", actualExpiry, tc.duration)
138+
}
139+
})
140+
}
141+
}

app/internal/core/urlsvc/url_readsvc.go renamed to app/internal/core/urlsvc/read/url_readsvc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package urlsvc
1+
package read
22

33
import (
44
"github.com/sanctumlabs/curtz/app/internal/core/contracts"

0 commit comments

Comments
 (0)