diff --git a/changes/issue-1984-label-count-on-edit b/changes/issue-1984-label-count-on-edit new file mode 100644 index 0000000000..44e8a302d8 --- /dev/null +++ b/changes/issue-1984-label-count-on-edit @@ -0,0 +1 @@ +* Do not reset label counts when modifying them. diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index ca5482e033..9b46ac0714 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -217,17 +217,12 @@ func (d *Datastore) NewLabel(ctx context.Context, label *fleet.Label, opts ...fl } func (d *Datastore) SaveLabel(ctx context.Context, label *fleet.Label) (*fleet.Label, error) { - query := ` - UPDATE labels SET - name = ?, - description = ? - WHERE id = ? - ` + query := `UPDATE labels SET name = ?, description = ? WHERE id = ?` _, err := d.writer.ExecContext(ctx, query, label.Name, label.Description, label.ID) if err != nil { return nil, errors.Wrap(err, "saving label") } - return label, nil + return labelDB(ctx, label.ID, d.writer) } // DeleteLabel deletes a fleet.Label @@ -237,13 +232,20 @@ func (d *Datastore) DeleteLabel(ctx context.Context, name string) error { // Label returns a fleet.Label identified by lid if one exists. func (d *Datastore) Label(ctx context.Context, lid uint) (*fleet.Label, error) { + return labelDB(ctx, lid, d.reader) +} + +func labelDB(ctx context.Context, lid uint, q sqlx.QueryerContext) (*fleet.Label, error) { sql := ` - SELECT * FROM labels - WHERE id = ? + SELECT + l.*, + (SELECT COUNT(1) FROM label_membership lm JOIN hosts h ON (lm.host_id = h.id) WHERE label_id = l.id) AS host_count + FROM labels l + WHERE id = ? ` label := &fleet.Label{} - if err := sqlx.GetContext(ctx, d.reader, label, sql, lid); err != nil { + if err := sqlx.GetContext(ctx, q, label, sql, lid); err != nil { return nil, errors.Wrap(err, "selecting label") } diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go index d1a7d77b71..3aa2ee7f8b 100644 --- a/server/datastore/mysql/labels_test.go +++ b/server/datastore/mysql/labels_test.go @@ -594,22 +594,37 @@ func testLabelsIDsByName(t *testing.T, ds *Datastore) { } func testLabelsSave(t *testing.T, db *Datastore) { + h1, err := db.NewHost(context.Background(), &fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + SeenTime: time.Now(), + OsqueryHostID: "1", + NodeKey: "1", + UUID: "1", + Hostname: "foo.local", + }) + require.NoError(t, err) + label := &fleet.Label{ Name: "my label", Description: "a label", Query: "select 1 from processes;", Platform: "darwin", } - label, err := db.NewLabel(context.Background(), label) - require.Nil(t, err) + label, err = db.NewLabel(context.Background(), label) + require.NoError(t, err) label.Name = "changed name" label.Description = "changed description" + + require.NoError(t, db.RecordLabelQueryExecutions(context.Background(), h1, map[uint]*bool{label.ID: ptr.Bool(true)}, time.Now())) + _, err = db.SaveLabel(context.Background(), label) - require.Nil(t, err) + require.NoError(t, err) saved, err := db.Label(context.Background(), label.ID) - require.Nil(t, err) + require.NoError(t, err) assert.Equal(t, label.Name, saved.Name) assert.Equal(t, label.Description, saved.Description) + assert.Equal(t, 1, saved.HostCount) } func testLabelsQueriesForCentOSHost(t *testing.T, db *Datastore) {