From 23ea9981473774eb5910f723aaf72e4635e94fc7 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Sat, 5 Aug 2017 15:02:26 -0700 Subject: [PATCH] Create hash id from feed parameters --- web/pkg/database/pg.go | 2 +- web/pkg/database/pg_test.go | 35 +++++++++++++++++++++++++---------- web/pkg/id/hashids.go | 30 ++++++++++++++++++++++-------- web/pkg/id/hashids_test.go | 26 +++++++++++++++++++++++--- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/web/pkg/database/pg.go b/web/pkg/database/pg.go index 16b8d28..fe8b26a 100644 --- a/web/pkg/database/pg.go +++ b/web/pkg/database/pg.go @@ -21,7 +21,7 @@ type PgStorage struct { func (p *PgStorage) CreateFeed(feed *Feed) error { feed.LastAccess = time.Now().UTC() - _, err := p.db.Model(feed).Insert() + _, err := p.db.Model(feed).OnConflict("DO NOTHING").Insert() if err != nil { return errors.Wrap(err, "failed to create feed") } diff --git a/web/pkg/database/pg_test.go b/web/pkg/database/pg_test.go index aab017b..19e2235 100644 --- a/web/pkg/database/pg_test.go +++ b/web/pkg/database/pg_test.go @@ -18,6 +18,31 @@ func TestCreate(t *testing.T) { require.True(t, feed.Id > 0) } +func TestCreateDuplicate(t *testing.T) { + feed := &Feed{ + HashId: "123", + URL: "http://youtube.com", + } + + client := createClient(t) + err := client.CreateFeed(feed) + require.NoError(t, err) + + // Ensure 1 record + count, err := client.db.Model(&Feed{}).Count() + require.NoError(t, err) + require.Equal(t, 1, count) + + // Insert duplicated feed + err = client.CreateFeed(feed) + require.NoError(t, err) + + // Check no duplicates inserted + count, err = client.db.Model(&Feed{}).Count() + require.NoError(t, err) + require.Equal(t, 1, count) +} + func TestGetFeed(t *testing.T) { feed := &Feed{ HashId: "xyz", @@ -57,16 +82,6 @@ func TestUpdateLastAccess(t *testing.T) { require.True(t, last.LastAccess.Unix() > lastAccess.Unix()) } -func TestUniqueHashId(t *testing.T) { - client := createClient(t) - - err := client.CreateFeed(&Feed{HashId: "xyz", URL: "url"}) - require.NoError(t, err) - - err = client.CreateFeed(&Feed{HashId: "xyz", URL: "url"}) - require.Error(t, err) -} - const TestDatabaseConnectionUrl = "postgres://postgres:@localhost/podsync?sslmode=disable" func createClient(t *testing.T) *PgStorage { diff --git a/web/pkg/id/hashids.go b/web/pkg/id/hashids.go index 0f364d4..7287c9b 100644 --- a/web/pkg/id/hashids.go +++ b/web/pkg/id/hashids.go @@ -1,6 +1,9 @@ package id import ( + "hash/fnv" + + "github.com/mxpv/podsync/web/pkg/database" hd "github.com/speps/go-hashids" ) @@ -14,9 +17,24 @@ type hashId struct { hid *hd.HashID } -func (h *hashId) Encode(x ...int) (string, error) { - var d []int - return h.hid.Encode(append(d, x...)) +func hashString(s string) int { + h := fnv.New32a() + h.Write([]byte(s)) + return int(h.Sum32()) +} + +func (h *hashId) Encode(feed *database.Feed) (string, error) { + // Don't create duplicate urls for same playlist/settings + // https://github.com/podsync/issues/issues/6 + numbers := []int{ + hashString(feed.UserId), + hashString(feed.URL), + feed.PageSize, + hashString(string(feed.Quality)), + hashString(string(feed.Format)), + } + + return h.hid.Encode(numbers) } func NewIdGenerator() (*hashId, error) { @@ -24,10 +42,6 @@ func NewIdGenerator() (*hashId, error) { data.MinLength = minLength data.Salt = salt data.Alphabet = alphabet - hid, err := hd.NewWithData(data) - if err != nil { - return nil, err - } - + hid := hd.NewWithData(data) return &hashId{hid}, nil } diff --git a/web/pkg/id/hashids_test.go b/web/pkg/id/hashids_test.go index 237791f..0d95eb9 100644 --- a/web/pkg/id/hashids_test.go +++ b/web/pkg/id/hashids_test.go @@ -1,15 +1,35 @@ package id import ( - "github.com/stretchr/testify/require" "testing" + + "github.com/mxpv/podsync/web/pkg/database" + "github.com/stretchr/testify/require" ) func TestEncode(t *testing.T) { hid, err := NewIdGenerator() require.NoError(t, err) - hash, err := hid.Encode(1) + feed := &database.Feed{ + UserId: "1", + URL: "https://www.youtube.com/channel/UC2yTVSttx7lxAOAzx1opjoA", + PageSize: 10, + Quality: database.HighQuality, + Format: database.AudioFormat, + } + + hash1, err := hid.Encode(feed) require.NoError(t, err) - require.NotEmpty(t, hash) + require.NotEmpty(t, hash1) + + // Ensure we have same hash for same feed/parameters + hash2, err := hid.Encode(feed) + require.NoError(t, err) + require.Equal(t, hash1, hash2) + + feed.UserId = "" + hash3, err := hid.Encode(feed) + require.NoError(t, err) + require.NotEqual(t, hash1, hash3) }