diff --git a/cmd/ketchup/templates/ketchup.html b/cmd/ketchup/templates/ketchup.html index 3bffc2cc..e22fef77 100644 --- a/cmd/ketchup/templates/ketchup.html +++ b/cmd/ketchup/templates/ketchup.html @@ -40,6 +40,15 @@
+ + +
+@@ -96,6 +105,15 @@
+ + +
+diff --git a/pkg/ketchup/ketchups.go b/pkg/ketchup/ketchups.go index 4fcc3e8a..e160284d 100644 --- a/pkg/ketchup/ketchups.go +++ b/pkg/ketchup/ketchups.go @@ -45,6 +45,12 @@ func (a app) handleCreate(w http.ResponseWriter, r *http.Request) { return } + ketchupFrequency, err := model.ParseKetchupFrequency(r.FormValue("frequency")) + if err != nil { + a.rendererApp.Error(w, httpModel.WrapInvalid(err)) + return + } + var repository model.Repository name := r.FormValue("name") @@ -57,7 +63,7 @@ func (a app) handleCreate(w http.ResponseWriter, r *http.Request) { a.rendererApp.Error(w, httpModel.WrapInternal(fmt.Errorf("unhandled repository kind `%s`", repositoryKind))) } - item := model.NewKetchup(r.FormValue("pattern"), r.FormValue("version"), repository) + item := model.NewKetchup(r.FormValue("pattern"), r.FormValue("version"), ketchupFrequency, repository) created, err := a.ketchupService.Create(r.Context(), item) if err != nil { @@ -75,7 +81,13 @@ func (a app) handleUpdate(w http.ResponseWriter, r *http.Request) { return } - item := model.NewKetchup(r.FormValue("pattern"), r.FormValue("version"), model.NewGithubRepository(id, "")) + ketchupFrequency, err := model.ParseKetchupFrequency(r.FormValue("frequency")) + if err != nil { + a.rendererApp.Error(w, httpModel.WrapInvalid(err)) + return + } + + item := model.NewKetchup(r.FormValue("pattern"), r.FormValue("version"), ketchupFrequency, model.NewGithubRepository(id, "")) updated, err := a.ketchupService.Update(r.Context(), item) if err != nil { @@ -93,7 +105,7 @@ func (a app) handleDelete(w http.ResponseWriter, r *http.Request) { return } - item := model.NewKetchup("", "", model.NewGithubRepository(id, "")) + item := model.NewKetchup("", "", model.Daily, model.NewGithubRepository(id, "")) if err := a.ketchupService.Delete(r.Context(), item); err != nil { a.rendererApp.Error(w, err) diff --git a/pkg/model/ketchup.go b/pkg/model/ketchup.go index a865a7a3..e578fd0d 100644 --- a/pkg/model/ketchup.go +++ b/pkg/model/ketchup.go @@ -1,14 +1,47 @@ package model import ( + "fmt" + "strings" + "github.com/ViBiOh/ketchup/pkg/semver" ) +// KetchupFrequency defines constant for ketchup frequency +type KetchupFrequency int + +const ( + // None frequency + None KetchupFrequency = iota + // Daily frequency + Daily + // Weekly frequency (on Monday) + Weekly +) + var ( + // KetchupFrequencyValues string values + KetchupFrequencyValues = []string{"None", "Daily", "Weekly"} + // NoneKetchup is an undefined ketchup NoneKetchup = Ketchup{} ) +// ParseKetchupFrequency parse raw string into a KetchupFrequency +func ParseKetchupFrequency(value string) (KetchupFrequency, error) { + for i, short := range KetchupFrequencyValues { + if strings.EqualFold(short, value) { + return KetchupFrequency(i), nil + } + } + + return Daily, fmt.Errorf("invalid value `%s` for ketchup frequency", value) +} + +func (r KetchupFrequency) String() string { + return KetchupFrequencyValues[r] +} + // Ketchup of app type Ketchup struct { Semver string @@ -16,13 +49,15 @@ type Ketchup struct { Version string User User Repository Repository + Frequency KetchupFrequency } // NewKetchup creates new instance -func NewKetchup(pattern, version string, repo Repository) Ketchup { +func NewKetchup(pattern, version string, frequency KetchupFrequency, repo Repository) Ketchup { return Ketchup{ Pattern: pattern, Version: version, + Frequency: frequency, Repository: repo, } } diff --git a/pkg/model/ketchup_test.go b/pkg/model/ketchup_test.go index bf87c6a6..61cdc1cf 100644 --- a/pkg/model/ketchup_test.go +++ b/pkg/model/ketchup_test.go @@ -1,13 +1,75 @@ package model import ( + "errors" "reflect" "sort" + "strings" "testing" "github.com/ViBiOh/ketchup/pkg/semver" ) +func TestParseKetchupFrequency(t *testing.T) { + type args struct { + value string + } + + var cases = []struct { + intention string + args args + want KetchupFrequency + wantErr error + }{ + { + "UpperCase", + args{ + value: "NONE", + }, + None, + nil, + }, + { + "equal", + args{ + value: "Weekly", + }, + Weekly, + nil, + }, + { + "not found", + args{ + value: "wrong", + }, + Daily, + errors.New("invalid value `wrong` for ketchup frequency"), + }, + } + + for _, tc := range cases { + t.Run(tc.intention, func(t *testing.T) { + got, gotErr := ParseKetchupFrequency(tc.args.value) + + failed := false + + if tc.wantErr == nil && gotErr != nil { + failed = true + } else if tc.wantErr != nil && gotErr == nil { + failed = true + } else if tc.wantErr != nil && !strings.Contains(gotErr.Error(), tc.wantErr.Error()) { + failed = true + } else if got != tc.want { + failed = true + } + + if failed { + t.Errorf("ParseRepositoryKind() = (`%s`, `%s`), want (`%s`, `%s`)", got, gotErr, tc.want, tc.wantErr) + } + }) + } +} + func TestKetchupByRepositoryID(t *testing.T) { type args struct { array []Ketchup @@ -22,15 +84,15 @@ func TestKetchupByRepositoryID(t *testing.T) { "simple", args{ array: []Ketchup{ - NewKetchup(DefaultPattern, "", NewGithubRepository(10, "")), - NewKetchup(DefaultPattern, "", NewGithubRepository(1, "")), - NewKetchup("latest", "", NewGithubRepository(1, "")), + NewKetchup(DefaultPattern, "", Daily, NewGithubRepository(10, "")), + NewKetchup(DefaultPattern, "", Daily, NewGithubRepository(1, "")), + NewKetchup("latest", "", Daily, NewGithubRepository(1, "")), }, }, []Ketchup{ - NewKetchup("latest", "", NewGithubRepository(1, "")), - NewKetchup(DefaultPattern, "", NewGithubRepository(1, "")), - NewKetchup(DefaultPattern, "", NewGithubRepository(10, "")), + NewKetchup("latest", "", Daily, NewGithubRepository(1, "")), + NewKetchup(DefaultPattern, "", Daily, NewGithubRepository(1, "")), + NewKetchup(DefaultPattern, "", Daily, NewGithubRepository(10, "")), }, }, } @@ -59,17 +121,17 @@ func TestKetchupByPriority(t *testing.T) { "alphabetic", args{ array: []Ketchup{ - NewKetchup("", "", NewGithubRepository(0, "abc")), - NewKetchup("", "", NewGithubRepository(0, "ghi")), - NewKetchup("", "", NewGithubRepository(0, "jkl")), - NewKetchup("", "", NewGithubRepository(0, "def")), + NewKetchup("", "", Daily, NewGithubRepository(0, "abc")), + NewKetchup("", "", Daily, NewGithubRepository(0, "ghi")), + NewKetchup("", "", Daily, NewGithubRepository(0, "jkl")), + NewKetchup("", "", Daily, NewGithubRepository(0, "def")), }, }, []Ketchup{ - NewKetchup("", "", NewGithubRepository(0, "abc")), - NewKetchup("", "", NewGithubRepository(0, "def")), - NewKetchup("", "", NewGithubRepository(0, "ghi")), - NewKetchup("", "", NewGithubRepository(0, "jkl")), + NewKetchup("", "", Daily, NewGithubRepository(0, "abc")), + NewKetchup("", "", Daily, NewGithubRepository(0, "def")), + NewKetchup("", "", Daily, NewGithubRepository(0, "ghi")), + NewKetchup("", "", Daily, NewGithubRepository(0, "jkl")), }, }, { diff --git a/pkg/model/repository.go b/pkg/model/repository.go index 57725fa9..effdcc00 100644 --- a/pkg/model/repository.go +++ b/pkg/model/repository.go @@ -5,9 +5,6 @@ import ( "strings" ) -// RepositoryKind defines constant for repository types -type RepositoryKind int - const ( // DefaultPattern is the latest but non-beta version DefaultPattern = "stable" @@ -15,6 +12,9 @@ const ( githubURL = "https://github.com" ) +// RepositoryKind defines constant for repository types +type RepositoryKind int + const ( // Github repository kind Github RepositoryKind = iota @@ -25,13 +25,22 @@ const ( var ( // RepositoryKindValues string values RepositoryKindValues = []string{"github", "helm"} -) -var ( // NoneRepository is an undefined repository NoneRepository = Repository{} ) +// ParseRepositoryKind parse raw string into a RepositoryKind +func ParseRepositoryKind(value string) (RepositoryKind, error) { + for i, short := range RepositoryKindValues { + if strings.EqualFold(short, value) { + return RepositoryKind(i), nil + } + } + + return Github, fmt.Errorf("invalid value `%s` for repository kind", value) +} + func (r RepositoryKind) String() string { return RepositoryKindValues[r] } @@ -95,17 +104,6 @@ func (r Repository) CompareURL(version string, pattern string) string { return fmt.Sprintf("%s/%s/compare/%s...%s", githubURL, r.Name, r.Versions[pattern], version) } -// ParseRepositoryKind parse raw string into a RepositoryKind -func ParseRepositoryKind(value string) (RepositoryKind, error) { - for i, short := range RepositoryKindValues { - if strings.EqualFold(short, value) { - return RepositoryKind(i), nil - } - } - - return Github, fmt.Errorf("invalid value `%s` for repository kind", value) -} - // RepositoryByID sort repository by ID type RepositoryByID []Repository diff --git a/pkg/semver/pattern_test.go b/pkg/semver/pattern_test.go index 959a33ef..b5d43b99 100644 --- a/pkg/semver/pattern_test.go +++ b/pkg/semver/pattern_test.go @@ -1,7 +1,6 @@ package semver import ( - "fmt" "testing" ) @@ -159,8 +158,6 @@ func TestCheck(t *testing.T) { }, } - fmt.Println(safeParsePattern("^1.0").Check(safeParse("2.0.0"))) - for _, tc := range cases { t.Run(tc.intention, func(t *testing.T) { if got := tc.instance.Check(tc.args.version); got != tc.want { diff --git a/pkg/service/ketchup/ketchup.go b/pkg/service/ketchup/ketchup.go index 8a9deeca..7b4f8a5a 100644 --- a/pkg/service/ketchup/ketchup.go +++ b/pkg/service/ketchup/ketchup.go @@ -100,6 +100,7 @@ func (a app) Update(ctx context.Context, item model.Ketchup) (model.Ketchup, err current := model.Ketchup{ Pattern: item.Pattern, Version: item.Version, + Frequency: item.Frequency, Repository: old.Repository, User: old.User, } diff --git a/pkg/service/ketchup/ketchup_test.go b/pkg/service/ketchup/ketchup_test.go index 1c4713b2..4e522518 100644 --- a/pkg/service/ketchup/ketchup_test.go +++ b/pkg/service/ketchup/ketchup_test.go @@ -38,15 +38,15 @@ func TestList(t *testing.T) { { "simple", New(ketchuptest.New().SetList([]model.Ketchup{ - model.NewKetchup(model.DefaultPattern, "1.2.3", model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")), - model.NewKetchup(model.DefaultPattern, "1.0.0", model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")), + model.NewKetchup(model.DefaultPattern, "1.2.3", model.Daily, model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")), + model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")), }, 2, nil), nil), args{ page: 1, }, []model.Ketchup{ - {Pattern: model.DefaultPattern, Version: "1.0.0", Semver: "Patch", Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")}, - {Pattern: model.DefaultPattern, Version: "1.2.3", Repository: model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")}, + {Pattern: model.DefaultPattern, Version: "1.0.0", Frequency: model.Daily, Semver: "Patch", Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")}, + {Pattern: model.DefaultPattern, Version: "1.2.3", Frequency: model.Daily, Repository: model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")}, }, 2, nil, @@ -99,8 +99,8 @@ func TestListForRepositories(t *testing.T) { { "simple", New(ketchuptest.New().SetListByRepositoriesID([]model.Ketchup{ - model.NewKetchup(model.DefaultPattern, "1.0.0", model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")), - model.NewKetchup(model.DefaultPattern, "1.2.3", model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")), + model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")), + model.NewKetchup(model.DefaultPattern, "1.2.3", model.Daily, model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")), }, nil), nil), args{ repositories: []model.Repository{ @@ -109,8 +109,8 @@ func TestListForRepositories(t *testing.T) { }, }, []model.Ketchup{ - {Pattern: model.DefaultPattern, Version: "1.0.0", Semver: "Patch", Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")}, - {Pattern: model.DefaultPattern, Version: "1.2.3", Repository: model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")}, + {Pattern: model.DefaultPattern, Version: "1.0.0", Frequency: model.Daily, Semver: "Patch", Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.2")}, + {Pattern: model.DefaultPattern, Version: "1.2.3", Frequency: model.Daily, Repository: model.NewGithubRepository(2, viwsRepository).AddVersion(model.DefaultPattern, "1.2.3")}, }, nil, }, @@ -180,7 +180,7 @@ func TestCreate(t *testing.T) { New(ketchuptest.New(), repositorytest.New()), args{ ctx: context.Background(), - item: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(1, ketchupRepository)), + item: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(1, ketchupRepository)), }, model.NoneKetchup, httpModel.ErrInvalid, @@ -190,7 +190,7 @@ func TestCreate(t *testing.T) { New(ketchuptest.New().SetCreate(0, errors.New("failed")), repositorytest.New()), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup(model.DefaultPattern, "0.0.0", model.NewGithubRepository(1, ketchupRepository)), + item: model.NewKetchup(model.DefaultPattern, "0.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), }, model.NoneKetchup, httpModel.ErrInternalError, @@ -200,9 +200,9 @@ func TestCreate(t *testing.T) { New(ketchuptest.New(), repositorytest.New().SetGetOrCreate(model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.0"), nil)), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup(model.DefaultPattern, "1.0.0", model.NewGithubRepository(1, ketchupRepository)), + item: model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), }, - model.NewKetchup(model.DefaultPattern, "1.0.0", model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.0")), + model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.0.0")), nil, }, } @@ -261,20 +261,20 @@ func TestUpdate(t *testing.T) { }, { "check error", - New(ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "0.9.0", model.NewGithubRepository(1, ketchupRepository)), nil), repositorytest.New()), + New(ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "0.9.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), nil), repositorytest.New()), args{ ctx: context.Background(), - item: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(1, ketchupRepository)), + item: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(1, ketchupRepository)), }, model.NoneKetchup, httpModel.ErrInvalid, }, { "pattern change error", - New(ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "0.9.0", model.NewGithubRepository(1, ketchupRepository)), nil), repositorytest.New().SetGetOrCreate(model.NoneRepository, errors.New("failed"))), + New(ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "0.9.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), nil), repositorytest.New().SetGetOrCreate(model.NoneRepository, errors.New("failed"))), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup("latest", "1.0.0", model.NewGithubRepository(1, ketchupRepository)), + item: model.NewKetchup("latest", "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), }, model.NoneKetchup, httpModel.ErrInternalError, @@ -284,16 +284,18 @@ func TestUpdate(t *testing.T) { New(ketchuptest.New().SetGetByRepositoryID(model.Ketchup{ Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ketchupRepository), User: model.NewUser(1, "", authModel.NewUser(0, "")), }, nil), repositorytest.New().SetGetOrCreate(model.NewGithubRepository(1, ketchupRepository).AddVersion("latest", "1.0.1"), nil)), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup("latest", "1.0.0", model.NewGithubRepository(1, ketchupRepository)), + item: model.NewKetchup("latest", "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), }, model.Ketchup{ Pattern: "latest", Version: "1.0.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion("latest", "1.0.1"), User: model.NewUser(1, "", authModel.NewUser(0, "")), }, @@ -301,10 +303,10 @@ func TestUpdate(t *testing.T) { }, { "update error", - New(ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "0.9.0", model.NewGithubRepository(1, ketchupRepository)), nil).SetUpdate(errors.New("failed")), repositorytest.New()), + New(ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "0.9.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), nil).SetUpdate(errors.New("failed")), repositorytest.New()), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup(model.DefaultPattern, "0.0.0", model.NewGithubRepository(2, "")), + item: model.NewKetchup(model.DefaultPattern, "0.0.0", model.Daily, model.NewGithubRepository(2, "")), }, model.NoneKetchup, httpModel.ErrInternalError, @@ -314,16 +316,18 @@ func TestUpdate(t *testing.T) { New(ketchuptest.New().SetGetByRepositoryID(model.Ketchup{ Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.2.3"), User: model.NewUser(1, "", authModel.NewUser(0, "")), }, nil), repositorytest.New()), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup(model.DefaultPattern, "1.0.0", model.NewGithubRepository(1, "")), + item: model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NewGithubRepository(1, "")), }, model.Ketchup{ Pattern: model.DefaultPattern, Version: "1.0.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ketchupRepository).AddVersion(model.DefaultPattern, "1.2.3"), User: model.NewUser(1, "", authModel.NewUser(0, "")), }, @@ -376,7 +380,7 @@ func TestDelete(t *testing.T) { New(ketchuptest.New().SetGetByRepositoryID(model.NoneKetchup, errors.New("failed")), repositorytest.New()), args{ ctx: context.Background(), - item: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(0, "")), + item: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(0, "")), }, httpModel.ErrInternalError, }, @@ -385,7 +389,7 @@ func TestDelete(t *testing.T) { New(ketchuptest.New(), repositorytest.New()), args{ ctx: context.Background(), - item: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(1, "")), + item: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(1, "")), }, httpModel.ErrInvalid, }, @@ -394,7 +398,7 @@ func TestDelete(t *testing.T) { New(ketchuptest.New().SetDelete(errors.New("failed")), repositorytest.New()), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(3, "")), + item: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(3, "")), }, httpModel.ErrInternalError, }, @@ -403,7 +407,7 @@ func TestDelete(t *testing.T) { New(ketchuptest.New(), repositorytest.New()), args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - item: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(1, "")), + item: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(1, "")), }, nil, }, @@ -452,7 +456,7 @@ func TestCheck(t *testing.T) { app{ketchupStore: ketchuptest.New()}, args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - old: model.NewKetchup(model.DefaultPattern, "1.0.0", model.NoneRepository), + old: model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NoneRepository), new: model.NoneKetchup, }, nil, @@ -463,7 +467,7 @@ func TestCheck(t *testing.T) { args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), old: model.NoneKetchup, - new: model.NewKetchup("", "", model.NewGithubRepository(1, "")), + new: model.NewKetchup("", "", model.Daily, model.NewGithubRepository(1, "")), }, errors.New("pattern is required"), }, @@ -473,7 +477,7 @@ func TestCheck(t *testing.T) { args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), old: model.NoneKetchup, - new: model.NewKetchup("test", "", model.NewGithubRepository(1, "")), + new: model.NewKetchup("test", "", model.Daily, model.NewGithubRepository(1, "")), }, errors.New("pattern is invalid"), }, @@ -482,8 +486,8 @@ func TestCheck(t *testing.T) { app{ketchupStore: ketchuptest.New()}, args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), - old: model.NewKetchup(model.DefaultPattern, "1.0.0", model.NoneRepository), - new: model.NewKetchup(model.DefaultPattern, "", model.NewGithubRepository(1, "")), + old: model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NoneRepository), + new: model.NewKetchup(model.DefaultPattern, "", model.Daily, model.NewGithubRepository(1, "")), }, errors.New("version is required"), }, @@ -498,7 +502,7 @@ func TestCheck(t *testing.T) { }, { "create already exists", - app{ketchupStore: ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "1.0.0", model.NewGithubRepository(1, ketchupRepository)), nil)}, + app{ketchupStore: ketchuptest.New().SetGetByRepositoryID(model.NewKetchup(model.DefaultPattern, "1.0.0", model.Daily, model.NewGithubRepository(1, ketchupRepository)), nil)}, args{ ctx: model.StoreUser(context.Background(), model.NewUser(1, "", authModel.NewUser(0, ""))), new: model.Ketchup{Pattern: model.DefaultPattern, Version: "1.0.0", Repository: model.NewGithubRepository(2, ketchupRepository), User: model.NewUser(1, "", authModel.NewUser(0, ""))}, diff --git a/pkg/store/ketchup/ketchup.go b/pkg/store/ketchup/ketchup.go index 11ba9fdd..0e77481c 100644 --- a/pkg/store/ketchup/ketchup.go +++ b/pkg/store/ketchup/ketchup.go @@ -42,6 +42,7 @@ const listQuery = ` SELECT k.pattern, k.version, + k.frequency, k.repository_id, r.name, r.part, @@ -66,17 +67,24 @@ func (a app) List(ctx context.Context, page, pageSize uint) ([]model.Ketchup, ui list := make([]model.Ketchup, 0) scanner := func(rows *sql.Rows) error { - item := model.NewKetchup("", "", model.NewRepository(0, 0, "", "")) + item := model.NewKetchup("", "", model.Daily, model.NewRepository(0, 0, "", "")) item.User = user var rawRepositoryKind string + var rawKetchupFrequency string var repositoryVersion string - if err := rows.Scan(&item.Pattern, &item.Version, &item.Repository.ID, &item.Repository.Name, &item.Repository.Part, &rawRepositoryKind, &repositoryVersion, &totalCount); err != nil { + if err := rows.Scan(&item.Pattern, &item.Version, &rawKetchupFrequency, &item.Repository.ID, &item.Repository.Name, &item.Repository.Part, &rawRepositoryKind, &repositoryVersion, &totalCount); err != nil { return err } item.Repository.AddVersion(item.Pattern, repositoryVersion) + ketchupFrequency, err := model.ParseKetchupFrequency(rawKetchupFrequency) + if err != nil { + return err + } + item.Frequency = ketchupFrequency + repositoryKind, err := model.ParseRepositoryKind(rawRepositoryKind) if err != nil { return err @@ -102,6 +110,7 @@ const listByRepositoriesIDQuery = ` SELECT k.pattern, k.version, + k.frequency, k.repository_id, k.user_id, u.email @@ -119,11 +128,18 @@ func (a app) ListByRepositoriesID(ctx context.Context, ids []uint64) ([]model.Ke scanner := func(rows *sql.Rows) error { var item model.Ketchup item.Repository = model.NewRepository(0, 0, "", "") + var rawKetchupFrequency string - if err := rows.Scan(&item.Pattern, &item.Version, &item.Repository.ID, &item.User.ID, &item.User.Email); err != nil { + if err := rows.Scan(&item.Pattern, &item.Version, &rawKetchupFrequency, &item.Repository.ID, &item.User.ID, &item.User.Email); err != nil { return err } + ketchupFrequency, err := model.ParseKetchupFrequency(rawKetchupFrequency) + if err != nil { + return err + } + item.Frequency = ketchupFrequency + list = append(list, item) return nil } @@ -135,6 +151,7 @@ const getQuery = ` SELECT k.pattern, k.version, + k.frequency, k.repository_id, k.user_id, r.name, @@ -163,13 +180,25 @@ func (a app) GetByRepositoryID(ctx context.Context, id uint64, forUpdate bool) ( scanner := func(row *sql.Row) error { var rawRepositoryKind string - err := row.Scan(&item.Pattern, &item.Version, &item.Repository.ID, &item.User.ID, &item.Repository.Name, &item.Repository.Part, &rawRepositoryKind) + var rawKetchupFrequency string + + err := row.Scan(&item.Pattern, &item.Version, &rawKetchupFrequency, &item.Repository.ID, &item.User.ID, &item.Repository.Name, &item.Repository.Part, &rawRepositoryKind) if errors.Is(err, sql.ErrNoRows) { item = model.NoneKetchup return nil } - item.Repository.Kind, err = model.ParseRepositoryKind(rawRepositoryKind) + ketchupFrequency, err := model.ParseKetchupFrequency(rawKetchupFrequency) + if err != nil { + return err + } + item.Frequency = ketchupFrequency + + repositoryKind, err := model.ParseRepositoryKind(rawRepositoryKind) + if err != nil { + return err + } + item.Repository.Kind = repositoryKind return err } @@ -183,18 +212,20 @@ INSERT INTO ( pattern, version, + frequency, repository_id, user_id ) VALUES ( $1, $2, $3, - $4 + $4, + $5 ) RETURNING 1 ` func (a app) Create(ctx context.Context, o model.Ketchup) (uint64, error) { - return db.Create(ctx, insertQuery, o.Pattern, o.Version, o.Repository.ID, model.ReadUser(ctx).ID) + return db.Create(ctx, insertQuery, o.Pattern, o.Version, strings.ToLower(o.Frequency.String()), o.Repository.ID, model.ReadUser(ctx).ID) } const updateQuery = ` @@ -202,14 +233,15 @@ UPDATE ketchup.ketchup SET pattern = $3, - version = $4 + version = $4, + frequency = $5 WHERE repository_id = $1 AND user_id = $2 ` func (a app) Update(ctx context.Context, o model.Ketchup) error { - return db.Exec(ctx, updateQuery, o.Repository.ID, model.ReadUser(ctx).ID, o.Pattern, o.Version) + return db.Exec(ctx, updateQuery, o.Repository.ID, model.ReadUser(ctx).ID, o.Pattern, o.Version, strings.ToLower(o.Frequency.String())) } const deleteQuery = ` diff --git a/pkg/store/ketchup/ketchup_test.go b/pkg/store/ketchup/ketchup_test.go index 17819dca..01004b45 100644 --- a/pkg/store/ketchup/ketchup_test.go +++ b/pkg/store/ketchup/ketchup_test.go @@ -74,12 +74,14 @@ func TestList(t *testing.T) { { Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, repositoryName).AddVersion(model.DefaultPattern, repositoryVersion), User: model.NewUser(3, testEmail, authModel.NewUser(0, "")), }, { Pattern: model.DefaultPattern, Version: repositoryVersion, + Frequency: model.Daily, Repository: model.NewHelmRepository(2, chartRepository, "app").AddVersion(model.DefaultPattern, repositoryVersion), User: model.NewUser(3, testEmail, authModel.NewUser(0, "")), }, @@ -122,12 +124,12 @@ func TestList(t *testing.T) { for _, tc := range cases { t.Run(tc.intention, func(t *testing.T) { testWithMock(t, func(mockDb *sql.DB, mock sqlmock.Sqlmock) { - rows := sqlmock.NewRows([]string{"pattern", "version", "repository_id", "name", "part", "kind", "repository_version", "full_count"}) - expectedQuery := mock.ExpectQuery("SELECT k.pattern, k.version, k.repository_id, r.name, r.part, r.kind, rv.version, .+ AS full_count FROM ketchup.ketchup k, ketchup.repository r, ketchup.repository_version rv WHERE user_id = .+ AND k.repository_id = r.id AND rv.repository_id = r.id AND rv.pattern = k.pattern").WithArgs(3, 20, 0).WillReturnRows(rows) + rows := sqlmock.NewRows([]string{"pattern", "version", "frequency", "repository_id", "name", "part", "kind", "repository_version", "full_count"}) + expectedQuery := mock.ExpectQuery("SELECT k.pattern, k.version, k.frequency, k.repository_id, r.name, r.part, r.kind, rv.version, .+ AS full_count FROM ketchup.ketchup k, ketchup.repository r, ketchup.repository_version rv WHERE user_id = .+ AND k.repository_id = r.id AND rv.repository_id = r.id AND rv.pattern = k.pattern").WithArgs(3, 20, 0).WillReturnRows(rows) switch tc.intention { case "simple": - rows.AddRow(model.DefaultPattern, "0.9.0", 1, repositoryName, "", "github", repositoryVersion, 2).AddRow(model.DefaultPattern, repositoryVersion, 2, chartRepository, "app", "helm", repositoryVersion, 2) + rows.AddRow(model.DefaultPattern, "0.9.0", "Daily", 1, repositoryName, "", "github", repositoryVersion, 2).AddRow(model.DefaultPattern, repositoryVersion, "Daily", 2, chartRepository, "app", "helm", repositoryVersion, 2) case "timeout": savedSQLTimeout := db.SQLTimeout @@ -138,10 +140,10 @@ func TestList(t *testing.T) { expectedQuery.WillDelayFor(db.SQLTimeout * 2) case "invalid rows": - rows.AddRow(model.DefaultPattern, "0.9.0", "a", repositoryName, "", "github", "0.9.0", 2) + rows.AddRow(model.DefaultPattern, "0.9.0", "Daily", "a", repositoryName, "", "github", "0.9.0", 2) case "invalid kind": - rows.AddRow(model.DefaultPattern, repositoryVersion, 2, viwsRepository, "", "wrong", repositoryVersion, 1) + rows.AddRow(model.DefaultPattern, repositoryVersion, "Daily", 2, viwsRepository, "", "wrong", repositoryVersion, 1) } got, gotCount, gotErr := New(mockDb).List(testCtx, tc.args.page, tc.args.pageSize) @@ -187,12 +189,14 @@ func TestListByRepositoriesID(t *testing.T) { { Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ""), User: model.NewUser(1, testEmail, authModel.NewUser(0, "")), }, { Pattern: model.DefaultPattern, Version: repositoryVersion, + Frequency: model.Daily, Repository: model.NewGithubRepository(2, ""), User: model.NewUser(2, "guest@domain", authModel.NewUser(0, "")), }, @@ -220,12 +224,12 @@ func TestListByRepositoriesID(t *testing.T) { for _, tc := range cases { t.Run(tc.intention, func(t *testing.T) { testWithMock(t, func(mockDb *sql.DB, mock sqlmock.Sqlmock) { - rows := sqlmock.NewRows([]string{"pattern", "version", "repository_id", "user_id", "email"}) - expectedQuery := mock.ExpectQuery("SELECT k.pattern, k.version, k.repository_id, k.user_id, u.email FROM ketchup.ketchup k, ketchup.user u WHERE repository_id = ANY .+ AND k.user_id = u.id").WithArgs(pq.Array(tc.args.ids)).WillReturnRows(rows) + rows := sqlmock.NewRows([]string{"pattern", "version", "frequency", "repository_id", "user_id", "email"}) + expectedQuery := mock.ExpectQuery("SELECT k.pattern, k.version, k.frequency, k.repository_id, k.user_id, u.email FROM ketchup.ketchup k, ketchup.user u WHERE repository_id = ANY .+ AND k.user_id = u.id").WithArgs(pq.Array(tc.args.ids)).WillReturnRows(rows) switch tc.intention { case "simple": - rows.AddRow(model.DefaultPattern, "0.9.0", 1, 1, testEmail).AddRow(model.DefaultPattern, repositoryVersion, 2, 2, "guest@domain") + rows.AddRow(model.DefaultPattern, "0.9.0", "Daily", 1, 1, testEmail).AddRow(model.DefaultPattern, repositoryVersion, "Daily", 2, 2, "guest@domain") case "timeout": savedSQLTimeout := db.SQLTimeout @@ -236,7 +240,7 @@ func TestListByRepositoriesID(t *testing.T) { expectedQuery.WillDelayFor(db.SQLTimeout * 2) case "invalid rows": - rows.AddRow(model.DefaultPattern, "0.9.0", "a", 1, testEmail) + rows.AddRow(model.DefaultPattern, "0.9.0", "Daily", "a", 1, testEmail) } got, gotErr := New(mockDb).ListByRepositoriesID(testCtx, tc.args.ids) @@ -278,10 +282,11 @@ func TestGetByRepositoryID(t *testing.T) { args{ id: 1, }, - "SELECT k.pattern, k.version, k.repository_id, k.user_id, r.name, r.part, r.kind FROM ketchup.ketchup k, ketchup.repository r WHERE k.repository_id = .+ AND k.user_id = .+ AND k.repository_id = r.id", + "SELECT k.pattern, k.version, k.frequency, k.repository_id, k.user_id, r.name, r.part, r.kind FROM ketchup.ketchup k, ketchup.repository r WHERE k.repository_id = .+ AND k.user_id = .+ AND k.repository_id = r.id", model.Ketchup{ Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, repositoryName), User: model.NewUser(3, testEmail, authModel.NewUser(0, "")), }, @@ -292,7 +297,7 @@ func TestGetByRepositoryID(t *testing.T) { args{ id: 1, }, - "SELECT k.pattern, k.version, k.repository_id, k.user_id, r.name, r.part, r.kind FROM ketchup.ketchup k, ketchup.repository r WHERE k.repository_id = .+ AND k.user_id = .+ AND k.repository_id = r.id", + "SELECT k.pattern, k.version, k.frequency, k.repository_id, k.user_id, r.name, r.part, r.kind FROM ketchup.ketchup k, ketchup.repository r WHERE k.repository_id = .+ AND k.user_id = .+ AND k.repository_id = r.id", model.NoneKetchup, nil, }, @@ -302,10 +307,11 @@ func TestGetByRepositoryID(t *testing.T) { id: 1, forUpdate: true, }, - "SELECT k.pattern, k.version, k.repository_id, k.user_id, r.name, r.part, r.kind FROM ketchup.ketchup k, ketchup.repository r WHERE k.repository_id = .+ AND k.user_id = .+ AND k.repository_id = r.id FOR UPDATE", + "SELECT k.pattern, k.version, k.frequency, k.repository_id, k.user_id, r.name, r.part, r.kind FROM ketchup.ketchup k, ketchup.repository r WHERE k.repository_id = .+ AND k.user_id = .+ AND k.repository_id = r.id FOR UPDATE", model.Ketchup{ Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, repositoryName), User: model.NewUser(3, testEmail, authModel.NewUser(0, "")), }, @@ -316,7 +322,7 @@ func TestGetByRepositoryID(t *testing.T) { for _, tc := range cases { t.Run(tc.intention, func(t *testing.T) { testWithMock(t, func(mockDb *sql.DB, mock sqlmock.Sqlmock) { - rows := sqlmock.NewRows([]string{"patter", "version", "repository_id", "user_id", "name", "part", "kind"}) + rows := sqlmock.NewRows([]string{"patter", "version", "frequency", "repository_id", "user_id", "name", "part", "kind"}) mock.ExpectQuery(tc.expectSQL).WithArgs(1, 3).WillReturnRows(rows) switch tc.intention { @@ -324,7 +330,7 @@ func TestGetByRepositoryID(t *testing.T) { fallthrough case "simple": - rows.AddRow(model.DefaultPattern, "0.9.0", 1, 3, repositoryName, "", "github") + rows.AddRow(model.DefaultPattern, "0.9.0", "Daily", 1, 3, repositoryName, "", "github") } got, gotErr := New(mockDb).GetByRepositoryID(testCtx, tc.args.id, tc.args.forUpdate) @@ -362,6 +368,7 @@ func TestCreate(t *testing.T) { o: model.Ketchup{ Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ""), }, }, @@ -375,7 +382,7 @@ func TestCreate(t *testing.T) { testWithMock(t, func(mockDb *sql.DB, mock sqlmock.Sqlmock) { ctx := testWithTransaction(t, mockDb, mock) - mock.ExpectQuery("INSERT INTO ketchup.ketchup").WithArgs(model.DefaultPattern, "0.9.0", 1, 3).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) + mock.ExpectQuery("INSERT INTO ketchup.ketchup").WithArgs(model.DefaultPattern, "0.9.0", "daily", 1, 3).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) got, gotErr := New(mockDb).Create(ctx, tc.args.o) @@ -411,6 +418,7 @@ func TestUpdate(t *testing.T) { o: model.Ketchup{ Pattern: model.DefaultPattern, Version: "0.9.0", + Frequency: model.Daily, Repository: model.NewGithubRepository(1, ""), }, }, @@ -423,7 +431,7 @@ func TestUpdate(t *testing.T) { testWithMock(t, func(mockDb *sql.DB, mock sqlmock.Sqlmock) { ctx := testWithTransaction(t, mockDb, mock) - mock.ExpectExec("UPDATE ketchup.ketchup SET pattern = .+, version = .+").WithArgs(1, 3, model.DefaultPattern, "0.9.0").WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectExec("UPDATE ketchup.ketchup SET pattern = .+, version = .+").WithArgs(1, 3, model.DefaultPattern, "0.9.0", "daily").WillReturnResult(sqlmock.NewResult(0, 1)) gotErr := New(mockDb).Update(ctx, tc.args.o) diff --git a/sql/ddl.sql b/sql/ddl.sql index e962fb78..b438ece8 100644 --- a/sql/ddl.sql +++ b/sql/ddl.sql @@ -5,6 +5,7 @@ DROP TABLE IF EXISTS ketchup.repository; DROP TABLE IF EXISTS ketchup.user; DROP TYPE IF EXISTS ketchup.repository_kind; +DROP TYPE IF EXISTS ketchup.ketchup_frequency; DROP INDEX IF EXISTS ketchup_user; DROP INDEX IF EXISTS repository_id; @@ -59,12 +60,16 @@ CREATE TABLE ketchup.repository_version ( CREATE UNIQUE INDEX repository_version_id ON ketchup.repository_version(repository_id, pattern); +-- repository_kind +CREATE TYPE ketchup.ketchup_frequency AS ENUM ('none', 'daily', 'weekly'); + -- ketchup CREATE TABLE ketchup.ketchup ( user_id BIGINT NOT NULL REFERENCES ketchup.user(id) ON DELETE CASCADE, repository_id BIGINT NOT NULL REFERENCES ketchup.repository(id), - pattern TEXT NOT NULL default 'stable', + pattern TEXT NOT NULL DEFAULT 'stable', version TEXT NOT NULL, + frequency ketchup_frequency NOT NULL DEFAULT 'daily', creation_date TIMESTAMP WITH TIME ZONE DEFAULT now() ); diff --git a/sql/migration_2020_05_23_1.sql b/sql/migration_2020-05-23_1.sql similarity index 100% rename from sql/migration_2020_05_23_1.sql rename to sql/migration_2020-05-23_1.sql diff --git a/sql/migration_2020_12_23_1.sql b/sql/migration_2020-12-23_1.sql similarity index 100% rename from sql/migration_2020_12_23_1.sql rename to sql/migration_2020-12-23_1.sql diff --git a/sql/migration_2020_12_30_1.sql b/sql/migration_2020-12-30_1.sql similarity index 100% rename from sql/migration_2020_12_30_1.sql rename to sql/migration_2020-12-30_1.sql diff --git a/sql/migration_2021-05-04_1.sql b/sql/migration_2021-05-04_1.sql new file mode 100644 index 00000000..8fbb5a60 --- /dev/null +++ b/sql/migration_2021-05-04_1.sql @@ -0,0 +1,4 @@ +CREATE TYPE ketchup.ketchup_frequency AS ENUM ('none', 'daily', 'weekly'); + +ALTER TABLE ketchup.ketchup + ADD COLUMN frequency ketchup_frequency NOT NULL DEFAULT 'daily';