mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Refectory of shorturl service, move models into service (#61295)
Chore: refectory of shorturl service, move models into service
This commit is contained in:
31
pkg/services/shorturls/models.go
Normal file
31
pkg/services/shorturls/models.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package shorturls
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrShortURLBadRequest = errutil.NewBase(errutil.StatusBadRequest, "shorturl.bad-request")
|
||||
ErrShortURLNotFound = errutil.NewBase(errutil.StatusNotFound, "shorturl.not-found")
|
||||
ErrShortURLAbsolutePath = errutil.NewBase(errutil.StatusValidationFailed, "shorturl.absolute-path", errutil.WithPublicMessage("Path should be relative"))
|
||||
ErrShortURLInvalidPath = errutil.NewBase(errutil.StatusValidationFailed, "shorturl.invalid-path", errutil.WithPublicMessage("Invalid short URL path"))
|
||||
ErrShortURLInternal = errutil.NewBase(errutil.StatusInternal, "shorturl.internal")
|
||||
)
|
||||
|
||||
type ShortUrl struct {
|
||||
Id int64
|
||||
OrgId int64
|
||||
Uid string
|
||||
Path string
|
||||
CreatedBy int64
|
||||
CreatedAt int64
|
||||
LastSeenAt int64
|
||||
}
|
||||
|
||||
type DeleteShortUrlCommand struct {
|
||||
OlderThan time.Time
|
||||
|
||||
NumDeleted int64
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package shorturls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var getTime = time.Now
|
||||
|
||||
func ProvideService(sqlStore db.DB) *ShortURLService {
|
||||
return &ShortURLService{
|
||||
SQLStore: sqlStore,
|
||||
}
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
GetShortURLByUID(ctx context.Context, user *user.SignedInUser, uid string) (*models.ShortUrl, error)
|
||||
CreateShortURL(ctx context.Context, user *user.SignedInUser, path string) (*models.ShortUrl, error)
|
||||
UpdateLastSeenAt(ctx context.Context, shortURL *models.ShortUrl) error
|
||||
DeleteStaleShortURLs(ctx context.Context, cmd *models.DeleteShortUrlCommand) error
|
||||
}
|
||||
|
||||
type ShortURLService struct {
|
||||
SQLStore db.DB
|
||||
}
|
||||
|
||||
func (s ShortURLService) GetShortURLByUID(ctx context.Context, user *user.SignedInUser, uid string) (*models.ShortUrl, error) {
|
||||
var shortURL models.ShortUrl
|
||||
err := s.SQLStore.WithDbSession(ctx, func(dbSession *db.Session) error {
|
||||
exists, err := dbSession.Where("org_id=? AND uid=?", user.OrgID, uid).Get(&shortURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return models.ErrShortURLNotFound.Errorf("short URL not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &shortURL, nil
|
||||
}
|
||||
|
||||
func (s ShortURLService) UpdateLastSeenAt(ctx context.Context, shortURL *models.ShortUrl) error {
|
||||
shortURL.LastSeenAt = getTime().Unix()
|
||||
return s.SQLStore.WithTransactionalDbSession(ctx, func(dbSession *db.Session) error {
|
||||
_, err := dbSession.ID(shortURL.Id).Update(shortURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s ShortURLService) CreateShortURL(ctx context.Context, user *user.SignedInUser, relPath string) (*models.ShortUrl, error) {
|
||||
relPath = strings.TrimSpace(relPath)
|
||||
|
||||
if path.IsAbs(relPath) {
|
||||
return nil, models.ErrShortURLAbsolutePath.Errorf("expected relative path: %s", relPath)
|
||||
}
|
||||
if strings.Contains(relPath, "../") {
|
||||
return nil, models.ErrShortURLInvalidPath.Errorf("path cannot contain '../': %s", relPath)
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
shortURL := models.ShortUrl{
|
||||
OrgId: user.OrgID,
|
||||
Uid: util.GenerateShortUID(),
|
||||
Path: relPath,
|
||||
CreatedBy: user.UserID,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
err := s.SQLStore.WithDbSession(ctx, func(session *db.Session) error {
|
||||
_, err := session.Insert(&shortURL)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, models.ErrShortURLInternal.Errorf("failed to insert shorturl: %w", err)
|
||||
}
|
||||
|
||||
return &shortURL, nil
|
||||
}
|
||||
|
||||
func (s ShortURLService) DeleteStaleShortURLs(ctx context.Context, cmd *models.DeleteShortUrlCommand) error {
|
||||
return s.SQLStore.WithTransactionalDbSession(ctx, func(session *db.Session) error {
|
||||
var rawSql = "DELETE FROM short_url WHERE created_at <= ? AND (last_seen_at IS NULL OR last_seen_at = 0)"
|
||||
|
||||
if result, err := session.Exec(rawSql, cmd.OlderThan.Unix()); err != nil {
|
||||
return err
|
||||
} else if cmd.NumDeleted, err = result.RowsAffected(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ Service = &ShortURLService{}
|
||||
14
pkg/services/shorturls/shorturl.go
Normal file
14
pkg/services/shorturls/shorturl.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package shorturls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
GetShortURLByUID(ctx context.Context, user *user.SignedInUser, uid string) (*ShortUrl, error)
|
||||
CreateShortURL(ctx context.Context, user *user.SignedInUser, path string) (*ShortUrl, error)
|
||||
UpdateLastSeenAt(ctx context.Context, shortURL *ShortUrl) error
|
||||
DeleteStaleShortURLs(ctx context.Context, cmd *DeleteShortUrlCommand) error
|
||||
}
|
||||
65
pkg/services/shorturls/shorturlimpl/shorturl.go
Normal file
65
pkg/services/shorturls/shorturlimpl/shorturl.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package shorturlimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/shorturls"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var getTime = time.Now
|
||||
|
||||
type ShortURLService struct {
|
||||
SQLStore store
|
||||
}
|
||||
|
||||
func ProvideService(db db.DB) *ShortURLService {
|
||||
return &ShortURLService{
|
||||
SQLStore: &sqlStore{
|
||||
db: db,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s ShortURLService) GetShortURLByUID(ctx context.Context, user *user.SignedInUser, uid string) (*shorturls.ShortUrl, error) {
|
||||
return s.SQLStore.Get(ctx, user, uid)
|
||||
}
|
||||
|
||||
func (s ShortURLService) UpdateLastSeenAt(ctx context.Context, shortURL *shorturls.ShortUrl) error {
|
||||
return s.SQLStore.Update(ctx, shortURL)
|
||||
}
|
||||
|
||||
func (s ShortURLService) CreateShortURL(ctx context.Context, user *user.SignedInUser, relPath string) (*shorturls.ShortUrl, error) {
|
||||
relPath = strings.TrimSpace(relPath)
|
||||
|
||||
if path.IsAbs(relPath) {
|
||||
return nil, shorturls.ErrShortURLAbsolutePath.Errorf("expected relative path: %s", relPath)
|
||||
}
|
||||
if strings.Contains(relPath, "../") {
|
||||
return nil, shorturls.ErrShortURLInvalidPath.Errorf("path cannot contain '../': %s", relPath)
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
shortURL := shorturls.ShortUrl{
|
||||
OrgId: user.OrgID,
|
||||
Uid: util.GenerateShortUID(),
|
||||
Path: relPath,
|
||||
CreatedBy: user.UserID,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
if err := s.SQLStore.Insert(ctx, &shortURL); err != nil {
|
||||
return nil, shorturls.ErrShortURLInternal.Errorf("failed to insert shorturl: %w", err)
|
||||
}
|
||||
|
||||
return &shortURL, nil
|
||||
}
|
||||
|
||||
func (s ShortURLService) DeleteStaleShortURLs(ctx context.Context, cmd *shorturls.DeleteShortUrlCommand) error {
|
||||
return s.SQLStore.Delete(ctx, cmd)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package shorturls
|
||||
package shorturlimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -8,18 +8,18 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/shorturls"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
func TestShortURLService(t *testing.T) {
|
||||
user := &user.SignedInUser{UserID: 1}
|
||||
sqlStore := db.InitTestDB(t)
|
||||
store := db.InitTestDB(t)
|
||||
|
||||
t.Run("User can create and read short URLs", func(t *testing.T) {
|
||||
const refPath = "mock/path?test=true"
|
||||
|
||||
service := ShortURLService{SQLStore: sqlStore}
|
||||
service := ShortURLService{SQLStore: &sqlStore{db: store}}
|
||||
|
||||
newShortURL, err := service.CreateShortURL(context.Background(), user, refPath)
|
||||
require.NoError(t, err)
|
||||
@@ -57,7 +57,7 @@ func TestShortURLService(t *testing.T) {
|
||||
require.NotEmpty(t, staleShortURL.Uid)
|
||||
require.Equal(t, int64(0), staleShortURL.LastSeenAt)
|
||||
|
||||
cmd := models.DeleteShortUrlCommand{OlderThan: time.Unix(staleShortURL.CreatedAt, 0)}
|
||||
cmd := shorturls.DeleteShortUrlCommand{OlderThan: time.Unix(staleShortURL.CreatedAt, 0)}
|
||||
err = service.DeleteStaleShortURLs(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), cmd.NumDeleted)
|
||||
@@ -69,7 +69,7 @@ func TestShortURLService(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("and no action when no stale short urls exist", func(t *testing.T) {
|
||||
cmd := models.DeleteShortUrlCommand{OlderThan: time.Unix(existingShortURL.CreatedAt, 0)}
|
||||
cmd := shorturls.DeleteShortUrlCommand{OlderThan: time.Unix(existingShortURL.CreatedAt, 0)}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), cmd.NumDeleted)
|
||||
})
|
||||
@@ -77,11 +77,11 @@ func TestShortURLService(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("User cannot look up nonexistent short URLs", func(t *testing.T) {
|
||||
service := ShortURLService{SQLStore: sqlStore}
|
||||
service := ShortURLService{SQLStore: &sqlStore{db: store}}
|
||||
|
||||
shortURL, err := service.GetShortURLByUID(context.Background(), user, "testnotfounduid")
|
||||
require.Error(t, err)
|
||||
require.True(t, models.ErrShortURLNotFound.Is(err))
|
||||
require.True(t, shorturls.ErrShortURLNotFound.Is(err))
|
||||
require.Nil(t, shortURL)
|
||||
})
|
||||
}
|
||||
71
pkg/services/shorturls/shorturlimpl/store.go
Normal file
71
pkg/services/shorturls/shorturlimpl/store.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package shorturlimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/shorturls"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type store interface {
|
||||
Get(ctx context.Context, user *user.SignedInUser, uid string) (*shorturls.ShortUrl, error)
|
||||
Update(ctx context.Context, shortURL *shorturls.ShortUrl) error
|
||||
Insert(ctx context.Context, shortURL *shorturls.ShortUrl) error
|
||||
Delete(ctx context.Context, cmd *shorturls.DeleteShortUrlCommand) error
|
||||
}
|
||||
|
||||
type sqlStore struct {
|
||||
db db.DB
|
||||
}
|
||||
|
||||
func (s sqlStore) Get(ctx context.Context, user *user.SignedInUser, uid string) (*shorturls.ShortUrl, error) {
|
||||
var shortURL shorturls.ShortUrl
|
||||
err := s.db.WithDbSession(ctx, func(dbSession *db.Session) error {
|
||||
exists, err := dbSession.Where("org_id=? AND uid=?", user.OrgID, uid).Get(&shortURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return shorturls.ErrShortURLNotFound.Errorf("short URL not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &shortURL, nil
|
||||
}
|
||||
|
||||
func (s sqlStore) Update(ctx context.Context, shortURL *shorturls.ShortUrl) error {
|
||||
shortURL.LastSeenAt = getTime().Unix()
|
||||
return s.db.WithTransactionalDbSession(ctx, func(dbSession *db.Session) error {
|
||||
_, err := dbSession.ID(shortURL.Id).Update(shortURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s sqlStore) Insert(ctx context.Context, shortURL *shorturls.ShortUrl) error {
|
||||
return s.db.WithDbSession(ctx, func(session *db.Session) error {
|
||||
_, err := session.Insert(shortURL)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s sqlStore) Delete(ctx context.Context, cmd *shorturls.DeleteShortUrlCommand) error {
|
||||
return s.db.WithTransactionalDbSession(ctx, func(session *db.Session) error {
|
||||
var rawSql = "DELETE FROM short_url WHERE created_at <= ? AND (last_seen_at IS NULL OR last_seen_at = 0)"
|
||||
|
||||
if result, err := session.Exec(rawSql, cmd.OlderThan.Unix()); err != nil {
|
||||
return err
|
||||
} else if cmd.NumDeleted, err = result.RowsAffected(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user