[gold] Refactor ignorestore to use string ids

They were effectively strings in the frontend before (due to the 64 bit
ints not fitting in a JS number), and if we migrate to firestore, I want
the flexibility of having strings as the IDs.

Change-Id: I2dbbfb3b81e2f03f552e7dd9a2d6a45024a25a76
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/251456
Commit-Queue: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
diff --git a/go/ds/ds.go b/go/ds/ds.go
index 46f258f..90c9df8 100644
--- a/go/ds/ds.go
+++ b/go/ds/ds.go
@@ -42,9 +42,6 @@
 	ALERT      Kind = "Alert"
 
 	// Gold
-	ISSUE              Kind = "Issue"
-	TRYJOB             Kind = "Tryjob"
-	TRYJOB_RESULT      Kind = "TryjobResult"
 	IGNORE_RULE        Kind = "IgnoreRule"
 	HELPER_RECENT_KEYS Kind = "HelperRecentKeys"
 
diff --git a/golden/go/ignore/ds_ignorestore/ds_ignorestore.go b/golden/go/ignore/ds_ignorestore/ds_ignorestore.go
index d724d91..7d93320 100644
--- a/golden/go/ignore/ds_ignorestore/ds_ignorestore.go
+++ b/golden/go/ignore/ds_ignorestore/ds_ignorestore.go
@@ -3,6 +3,7 @@
 import (
 	"context"
 	"sort"
+	"strconv"
 
 	"cloud.google.com/go/datastore"
 	"go.skia.org/infra/go/ds"
@@ -39,7 +40,7 @@
 func (c *DSIgnoreStore) Create(ctx context.Context, ignoreRule *ignore.Rule) error {
 	createFn := func(tx *datastore.Transaction) error {
 		key := dsutil.TimeSortableKey(ds.IGNORE_RULE, 0)
-		ignoreRule.ID = key.ID
+		ignoreRule.ID = strconv.FormatInt(key.ID, 10)
 
 		// Add the new rule and put its key with the recently added keys.
 		if _, err := tx.Put(key, ignoreRule); err != nil {
@@ -93,15 +94,23 @@
 }
 
 // Update implements the IgnoreStore interface.
-func (c *DSIgnoreStore) Update(ctx context.Context, id int64, rule *ignore.Rule) error {
+func (c *DSIgnoreStore) Update(ctx context.Context, id string, rule *ignore.Rule) error {
 	key := ds.NewKey(ds.IGNORE_RULE)
-	key.ID = id
-	_, err := c.client.Mutate(ctx, datastore.NewUpdate(key, rule))
+	var err error
+	key.ID, err = strconv.ParseInt(id, 10, 64)
+	if err != nil {
+		return skerr.Wrapf(err, "id must be int64: %q", id)
+	}
+	_, err = c.client.Mutate(ctx, datastore.NewUpdate(key, rule))
 	return skerr.Wrap(err)
 }
 
 // Delete implements the IgnoreStore interface.
-func (c *DSIgnoreStore) Delete(ctx context.Context, id int64) (int, error) {
+func (c *DSIgnoreStore) Delete(ctx context.Context, idStr string) (int, error) {
+	id, err := strconv.ParseInt(idStr, 10, 64)
+	if err != nil {
+		return 0, skerr.Wrapf(err, "id must be int64: %q", idStr)
+	}
 	if id <= 0 {
 		return 0, skerr.Fmt("Given id does not exist: %d", id)
 	}
@@ -123,7 +132,7 @@
 	}
 
 	// Run the relevant updates in a transaction.
-	_, err := c.client.RunInTransaction(ctx, deleteFn)
+	_, err = c.client.RunInTransaction(ctx, deleteFn)
 	if err != nil {
 		// Don't report an error if the item did not exist.
 		if err == datastore.ErrNoSuchEntity {
diff --git a/golden/go/ignore/ds_ignorestore/ds_ignorestore_test.go b/golden/go/ignore/ds_ignorestore/ds_ignorestore_test.go
index 51ac5e1..fa083bf 100644
--- a/golden/go/ignore/ds_ignorestore/ds_ignorestore_test.go
+++ b/golden/go/ignore/ds_ignorestore/ds_ignorestore_test.go
@@ -81,7 +81,7 @@
 
 	// Try to update a non-existent rule.
 	updatedRule = *allRules[0]
-	err = store.Update(context.Background(), 100001, &updatedRule)
+	err = store.Update(context.Background(), "100001", &updatedRule)
 	require.Error(t, err, "Update should fail for a bad id.")
 
 	delCount, err = store.Delete(context.Background(), r2.ID)
@@ -93,7 +93,7 @@
 	require.Equal(t, 0, len(allRules))
 
 	// This id doesn't exist, so we shouldn't be able to delete it.
-	delCount, err = store.Delete(context.Background(), 1000000)
+	delCount, err = store.Delete(context.Background(), "1000000")
 	require.NoError(t, err)
 	require.Equal(t, delCount, 0)
 	allRules, err = store.List(context.Background())
diff --git a/golden/go/ignore/ignorestore.go b/golden/go/ignore/ignorestore.go
index 25b105c..e9be168 100644
--- a/golden/go/ignore/ignorestore.go
+++ b/golden/go/ignore/ignorestore.go
@@ -20,16 +20,16 @@
 	List(context.Context) ([]*Rule, error)
 
 	// Update sets a Rule.
-	Update(ctx context.Context, id int64, rule *Rule) error
+	Update(ctx context.Context, id string, rule *Rule) error
 
 	// Delete removes a Rule from the store. The return value is the number of
 	// records that were deleted (either 0 or 1).
-	Delete(ctx context.Context, id int64) (int, error)
+	Delete(ctx context.Context, id string) (int, error)
 }
 
 // Rule is the GUI struct for dealing with Ignore rules.
 type Rule struct {
-	ID        int64     `json:"id,string"`
+	ID        string    `json:"id"`
 	Name      string    `json:"name"`
 	UpdatedBy string    `json:"updatedBy"`
 	Expires   time.Time `json:"expires"`
@@ -43,7 +43,7 @@
 	for _, ignore := range ignores {
 		v, err := url.ParseQuery(ignore.Query)
 		if err != nil {
-			return nil, skerr.Wrapf(err, "invalid ignore rule %d %s", ignore.ID, ignore.Query)
+			return nil, skerr.Wrapf(err, "invalid ignore rule id %q; query %q", ignore.ID, ignore.Query)
 		}
 		ret = append(ret, v)
 	}
diff --git a/golden/go/ignore/mocks/Store.go b/golden/go/ignore/mocks/Store.go
index ee390f4..72f6dfd 100644
--- a/golden/go/ignore/mocks/Store.go
+++ b/golden/go/ignore/mocks/Store.go
@@ -29,18 +29,18 @@
 }
 
 // Delete provides a mock function with given fields: ctx, id
-func (_m *Store) Delete(ctx context.Context, id int64) (int, error) {
+func (_m *Store) Delete(ctx context.Context, id string) (int, error) {
 	ret := _m.Called(ctx, id)
 
 	var r0 int
-	if rf, ok := ret.Get(0).(func(context.Context, int64) int); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, string) int); ok {
 		r0 = rf(ctx, id)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
 	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, id)
 	} else {
 		r1 = ret.Error(1)
@@ -73,11 +73,11 @@
 }
 
 // Update provides a mock function with given fields: ctx, id, rule
-func (_m *Store) Update(ctx context.Context, id int64, rule *ignore.Rule) error {
+func (_m *Store) Update(ctx context.Context, id string, rule *ignore.Rule) error {
 	ret := _m.Called(ctx, id, rule)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, int64, *ignore.Rule) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, string, *ignore.Rule) error); ok {
 		r0 = rf(ctx, id, rule)
 	} else {
 		r0 = ret.Error(0)
diff --git a/golden/go/web/web.go b/golden/go/web/web.go
index 13ee22d..f94a609 100644
--- a/golden/go/web/web.go
+++ b/golden/go/web/web.go
@@ -8,7 +8,6 @@
 	"net/http"
 	"net/url"
 	"sort"
-	"strconv"
 	"strings"
 	"time"
 
@@ -619,13 +618,6 @@
 	sendJSONResponse(w, ret)
 }
 
-// IgnoresRequest encapsulates a single ignore rule that is submitted for addition or update.
-type IgnoresRequest struct {
-	Duration string `json:"duration"`
-	Filter   string `json:"filter"`
-	Note     string `json:"note"`
-}
-
 // IgnoresHandler returns the current ignore rules in JSON format.
 func (wh *Handlers) IgnoresHandler(w http.ResponseWriter, r *http.Request) {
 	defer metrics2.FuncTimer().Stop()
@@ -637,45 +629,51 @@
 	w.Header().Set("Content-Type", "application/json")
 
 	// TODO(kjlubick): these ignore structs used to have counts of how often they were applied
-	// in the file - Fix that after the Storages refactoring.
+	//   in the file - Fix that after the Storages refactoring.
 	ignores, err := wh.IgnoreStore.List(r.Context())
 	if err != nil {
 		httputils.ReportError(w, err, "Failed to retrieve ignore rules, there may be none.", http.StatusInternalServerError)
 		return
 	}
 
-	// TODO(stephana): Wrap in response envelope if it makes sense !
 	enc := json.NewEncoder(w)
 	if err := enc.Encode(ignores); err != nil {
 		sklog.Errorf("Failed to write or encode result: %s", err)
 	}
 }
 
+// IgnoresRequest encapsulates a single ignore rule that is submitted for addition or update.
+type IgnoresRequest struct {
+	Duration string `json:"duration"`
+	Filter   string `json:"filter"`
+	Note     string `json:"note"`
+}
+
 // IgnoresUpdateHandler updates an existing ignores rule.
 func (wh *Handlers) IgnoresUpdateHandler(w http.ResponseWriter, r *http.Request) {
 	defer metrics2.FuncTimer().Stop()
 	user := login.LoggedInAs(r)
 	if user == "" {
-		httputils.ReportError(w, fmt.Errorf("Not logged in."), "You must be logged in to update an ignore rule.", http.StatusInternalServerError)
+		http.Error(w, "You must be logged in to update an ignore rule.", http.StatusUnauthorized)
 		return
 	}
-	id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 0)
-	if err != nil {
-		httputils.ReportError(w, err, "ID must be valid integer.", http.StatusInternalServerError)
+	id := mux.Vars(r)["id"]
+	if id == "" {
+		http.Error(w, "ID must be non-empty.", http.StatusBadRequest)
 		return
 	}
 	req := &IgnoresRequest{}
 	if err := parseJSON(r, req); err != nil {
-		httputils.ReportError(w, err, "Failed to parse submitted data.", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Failed to parse submitted data", http.StatusBadRequest)
 		return
 	}
 	if req.Filter == "" {
-		httputils.ReportError(w, fmt.Errorf("Invalid Filter: %q", req.Filter), "Filters can't be empty.", http.StatusInternalServerError)
+		http.Error(w, "Filter can't be empty", http.StatusBadRequest)
 		return
 	}
 	d, err := human.ParseDuration(req.Duration)
 	if err != nil {
-		httputils.ReportError(w, err, "Failed to parse duration", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Failed to parse duration", http.StatusBadRequest)
 		return
 	}
 	ignoreRule := ignore.NewRule(user, time.Now().Add(d), req.Filter, req.Note)
@@ -683,7 +681,7 @@
 
 	err = wh.IgnoreStore.Update(r.Context(), id, ignoreRule)
 	if err != nil {
-		httputils.ReportError(w, err, "Unable to update ignore rule.", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Unable to update ignore rule", http.StatusInternalServerError)
 		return
 	}
 
@@ -696,23 +694,24 @@
 	defer metrics2.FuncTimer().Stop()
 	user := login.LoggedInAs(r)
 	if user == "" {
-		httputils.ReportError(w, fmt.Errorf("Not logged in."), "You must be logged in to add an ignore rule.", http.StatusInternalServerError)
+		http.Error(w, "You must be logged in to delete an ignore rule", http.StatusUnauthorized)
 		return
 	}
-	id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 0)
-	if err != nil {
-		httputils.ReportError(w, err, "ID must be valid integer.", http.StatusInternalServerError)
+	id := mux.Vars(r)["id"]
+	if id == "" {
+		http.Error(w, "ID must be non-empty.", http.StatusBadRequest)
 		return
 	}
 
 	if numDeleted, err := wh.IgnoreStore.Delete(r.Context(), id); err != nil {
-		httputils.ReportError(w, err, "Unable to delete ignore rule.", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Unable to delete ignore rule", http.StatusInternalServerError)
+		return
 	} else if numDeleted == 1 {
-		sklog.Infof("Successfully deleted ignore with id %d", id)
+		sklog.Infof("Successfully deleted ignore with id %s", id)
 		// If delete worked just list the current ignores and return them.
 		wh.IgnoresHandler(w, r)
 	} else {
-		sklog.Infof("Deleting ignore with id %d from ignorestore failed", id)
+		sklog.Infof("Deleting ignore with id %s from ignorestore failed", id)
 		http.Error(w, "Could not delete ignore - try again later", http.StatusInternalServerError)
 		return
 	}
@@ -723,27 +722,27 @@
 	defer metrics2.FuncTimer().Stop()
 	user := login.LoggedInAs(r)
 	if user == "" {
-		httputils.ReportError(w, fmt.Errorf("Not logged in."), "You must be logged in to add an ignore rule.", http.StatusInternalServerError)
+		http.Error(w, "You must be logged in to add an ignore rule", http.StatusUnauthorized)
 		return
 	}
 	req := &IgnoresRequest{}
 	if err := parseJSON(r, req); err != nil {
-		httputils.ReportError(w, err, "Failed to parse submitted data.", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Failed to parse submitted data", http.StatusBadRequest)
 		return
 	}
 	if req.Filter == "" {
-		httputils.ReportError(w, fmt.Errorf("Invalid Filter: %q", req.Filter), "Filters can't be empty.", http.StatusInternalServerError)
+		http.Error(w, "Filter can't be empty", http.StatusBadRequest)
 		return
 	}
 	d, err := human.ParseDuration(req.Duration)
 	if err != nil {
-		httputils.ReportError(w, err, "Failed to parse duration", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Failed to parse duration", http.StatusBadRequest)
 		return
 	}
 	ignoreRule := ignore.NewRule(user, time.Now().Add(d), req.Filter, req.Note)
 
 	if err = wh.IgnoreStore.Create(r.Context(), ignoreRule); err != nil {
-		httputils.ReportError(w, err, "Failed to create ignore rule.", http.StatusInternalServerError)
+		httputils.ReportError(w, err, "Failed to create ignore rule", http.StatusInternalServerError)
 		return
 	}