1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-01-11 18:13:52 +02:00

Standardize err not found (#2834)

* cleanup log levels

* Standardize on model.IsErrNotFound instead of the mix of error checking done previously.

* fix merge conflicts

* fix comment typo

* add description to asserts
This commit is contained in:
Doug Lauder 2022-04-20 11:02:12 -04:00 committed by GitHub
parent 99882bf197
commit 936cc820ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 113 additions and 183 deletions

View File

@ -1,7 +1,6 @@
package main
import (
"errors"
"fmt"
"github.com/mattermost/focalboard/server/services/config"
@ -13,7 +12,6 @@ import (
"github.com/mattermost/focalboard/server/ws"
pluginapi "github.com/mattermost/mattermost-plugin-api"
apierrors "github.com/mattermost/mattermost-plugin-api/errors"
"github.com/mattermost/mattermost-server/v6/model"
@ -123,7 +121,3 @@ func (da *pluginAPIAdapter) GetChannelByID(channelID string) (*model.Channel, er
func (da *pluginAPIAdapter) GetChannelMember(channelID string, userID string) (*model.ChannelMember, error) {
return da.client.Channel.GetMember(channelID, userID)
}
func (da *pluginAPIAdapter) IsErrNotFound(err error) bool {
return errors.Is(err, apierrors.ErrNotFound)
}

View File

@ -1,7 +1,6 @@
package app
import (
"database/sql"
"errors"
"fmt"
@ -17,7 +16,7 @@ var (
func (a *App) GetBoard(boardID string) (*model.Board, error) {
board, err := a.store.GetBoard(boardID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil, nil
}
if err != nil {
@ -214,7 +213,7 @@ func (a *App) PatchBoard(patch *model.BoardPatch, boardID, userID string) (*mode
func (a *App) DeleteBoard(boardID, userID string) error {
board, err := a.store.GetBoard(boardID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil
}
if err != nil {
@ -246,7 +245,7 @@ func (a *App) GetMemberForBoard(boardID string, userID string) (*model.BoardMemb
func (a *App) AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, error) {
board, err := a.store.GetBoard(member.BoardID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil, nil
}
if err != nil {
@ -254,7 +253,7 @@ func (a *App) AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, e
}
existingMembership, err := a.store.GetMemberForBoard(member.BoardID, member.UserID)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
if err != nil && !model.IsErrNotFound(err) {
return nil, err
}
@ -276,7 +275,7 @@ func (a *App) AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, e
func (a *App) UpdateBoardMember(member *model.BoardMember) (*model.BoardMember, error) {
board, bErr := a.store.GetBoard(member.BoardID)
if errors.Is(bErr, sql.ErrNoRows) {
if model.IsErrNotFound(bErr) {
return nil, nil
}
if bErr != nil {
@ -284,7 +283,7 @@ func (a *App) UpdateBoardMember(member *model.BoardMember) (*model.BoardMember,
}
oldMember, err := a.store.GetMemberForBoard(member.BoardID, member.UserID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil, nil
}
if err != nil {
@ -331,7 +330,7 @@ func (a *App) isLastAdmin(userID, boardID string) (bool, error) {
func (a *App) DeleteBoardMember(boardID, userID string) error {
board, bErr := a.store.GetBoard(boardID)
if errors.Is(bErr, sql.ErrNoRows) {
if model.IsErrNotFound(bErr) {
return nil
}
if bErr != nil {
@ -339,7 +338,7 @@ func (a *App) DeleteBoardMember(boardID, userID string) error {
}
oldMember, err := a.store.GetMemberForBoard(boardID, userID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil
}
if err != nil {

View File

@ -1,15 +1,12 @@
package app
import (
"database/sql"
"errors"
"github.com/mattermost/focalboard/server/model"
)
func (a *App) GetSharing(boardID string) (*model.Sharing, error) {
sharing, err := a.store.GetSharing(boardID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil, nil
}
if err != nil {

View File

@ -1,9 +1,6 @@
package app
import (
"database/sql"
"errors"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/utils"
@ -38,7 +35,7 @@ func (a *App) GetRootTeam() (*model.Team, error) {
func (a *App) GetTeam(id string) (*model.Team, error) {
team, err := a.store.GetTeam(id)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return nil, nil
}
if err != nil {

View File

@ -2,8 +2,6 @@
package auth
import (
"database/sql"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/config"
"github.com/mattermost/focalboard/server/services/permissions"
@ -49,7 +47,7 @@ func (a *Auth) GetSession(token string) (*model.Session, error) {
// IsValidReadToken validates the read token for a board.
func (a *Auth) IsValidReadToken(boardID string, readToken string) (bool, error) {
sharing, err := a.store.GetSharing(boardID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return false, nil
}
if err != nil {

View File

@ -10,9 +10,10 @@ import (
mockpermissions "github.com/mattermost/focalboard/server/services/permissions/mocks"
"github.com/mattermost/focalboard/server/services/store/mockstore"
"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
type TestHelper struct {

49
server/model/error.go Normal file
View File

@ -0,0 +1,49 @@
package model
import (
"database/sql"
"errors"
"fmt"
apierrors "github.com/mattermost/mattermost-plugin-api/errors"
)
// ErrNotFound is an error type that can be returned by store APIs when a query unexpectedly fetches no records.
type ErrNotFound struct {
resource string
}
// NewErrNotFound creates a new ErrNotFound instance.
func NewErrNotFound(resource string) *ErrNotFound {
return &ErrNotFound{
resource: resource,
}
}
func (nf *ErrNotFound) Error() string {
return fmt.Sprintf("{%s} not found", nf.resource)
}
// IsErrNotFound returns true if `err` is or wraps one of:
// - model.ErrNotFound
// - sql.ErrNoRows
// - mattermost-plugin-api/errors/ErrNotFound.
func IsErrNotFound(err error) bool {
if err == nil {
return false
}
// check if this is a sql.ErrNotFound
if errors.Is(err, sql.ErrNoRows) {
return true
}
// check if this is a model.ErrNotFound
var nf *ErrNotFound
if errors.As(err, &nf) {
return true
}
// check if this is a plugin API error
return errors.Is(err, apierrors.ErrNotFound)
}

View File

@ -15,5 +15,4 @@ import (
type MentionDelivery interface {
MentionDeliver(mentionedUser *mm_model.User, extract string, evt notify.BlockChangeEvent) (string, error)
UserByUsername(mentionUsername string) (*mm_model.User, error)
IsErrNotFound(err error) bool
}

View File

@ -163,7 +163,7 @@ func safeCallListener(listener MentionListener, userID string, evt notify.BlockC
func (b *Backend) deliverMentionNotification(username string, extract string, evt notify.BlockChangeEvent) (string, error) {
mentionedUser, err := b.delivery.UserByUsername(username)
if err != nil {
if b.delivery.IsErrNotFound(err) {
if model.IsErrNotFound(err) {
// not really an error; could just be someone typed "@sometext"
return "", nil
} else {
@ -186,7 +186,7 @@ func (b *Backend) deliverMentionNotification(username string, extract string, ev
}
// add mentioned user to board (if not already a member)
member, err := b.store.GetMemberForBoard(evt.Board.ID, mentionedUser.Id)
if member == nil || b.store.IsErrNotFound(err) {
if member == nil || model.IsErrNotFound(err) {
// currently all memberships are created as editors by default
newBoardMember := &model.BoardMember{
UserID: mentionedUser.Id,

View File

@ -11,6 +11,4 @@ type Store interface {
SaveMember(bm *model.BoardMember) (*model.BoardMember, error)
CreateSubscription(sub *model.Subscription) (*model.Subscription, error)
IsErrNotFound(err error) bool
}

View File

@ -11,7 +11,6 @@ import (
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
"github.com/wiggin77/merror"
@ -83,7 +82,7 @@ func (n *notifier) loop() {
for {
hint, err := n.store.GetNextNotificationHint(false)
switch {
case n.store.IsErrNotFound(err):
case model.IsErrNotFound(err):
// no hints in table; wait up to an hour or when `onNotifyHint` is called again
nextNotify = time.Now().Add(time.Hour * 1)
n.logger.Debug("notify loop - no hints in queue", mlog.Time("next_check", nextNotify))
@ -132,7 +131,7 @@ func (n *notifier) notify() {
hint, err = n.store.GetNextNotificationHint(true)
if err != nil {
if store.IsErrNotFound(err) {
if model.IsErrNotFound(err) {
// Expected when multiple nodes in a cluster try to process the same hint at the same time.
// This simply means the other node won. Returning here will simply try fetching another hint.
return

View File

@ -27,6 +27,4 @@ type Store interface {
UpsertNotificationHint(hint *model.NotificationHint, notificationFreq time.Duration) (*model.NotificationHint, error)
GetNextNotificationHint(remove bool) (*model.NotificationHint, error)
IsErrNotFound(err error) bool
}

View File

@ -29,10 +29,6 @@ type PluginAPI interface {
// GetChannelMember gets a channel member by userID.
GetChannelMember(channelID string, userID string) (*mm_model.ChannelMember, error)
// IsErrNotFound returns true if `err` or one of its wrapped children are the `ErrNotFound`
// as defined in the plugin API.
IsErrNotFound(err error) bool
}
// PluginDelivery provides ability to send notifications to direct message channels via Mattermost plugin API.
@ -49,9 +45,3 @@ func New(botID string, serverRoot string, api PluginAPI) *PluginDelivery {
api: api,
}
}
// IsErrNotFound returns true if `err` or one of its wrapped children are the `ErrNotFound`
// as defined in the plugin API.
func (pd *PluginDelivery) IsErrNotFound(err error) bool {
return pd.api.IsErrNotFound(err)
}

View File

@ -22,7 +22,7 @@ func (pd *PluginDelivery) SubscriptionDeliverSlackAttachments(subscriberID strin
// check subscriber is member of channel
_, err := pd.api.GetUserByID(subscriberID)
if err != nil {
if pd.api.IsErrNotFound(err) {
if model.IsErrNotFound(err) {
// subscriber is not a member of the channel; fail silently.
return nil
}

View File

@ -6,6 +6,8 @@ package plugindelivery
import (
"strings"
"github.com/mattermost/focalboard/server/model"
mm_model "github.com/mattermost/mattermost-server/v6/model"
)
@ -21,7 +23,7 @@ func (pd *PluginDelivery) UserByUsername(username string) (*mm_model.User, error
trimmed := username
for ok {
user, err = pd.api.GetUserByUsername(trimmed)
if err != nil && !isErrNotFound(err) {
if err != nil && !model.IsErrNotFound(err) {
return nil, err
}
@ -51,10 +53,3 @@ func trimUsernameSpecialChar(word string) (string, bool) {
return word, false
}
// isErrNotFound returns true if the error is a plugin.ErrNotFound. The pluginAPI converts
// AppError to the plugin.ErrNotFound var.
// TODO: add a `IsErrNotFound` method to the plugin API.
func isErrNotFound(err error) bool {
return err != nil && err.Error() == "not found"
}

View File

@ -7,6 +7,8 @@ import (
"reflect"
"testing"
"github.com/mattermost/focalboard/server/model"
mm_model "github.com/mattermost/mattermost-server/v6/model"
)
@ -90,7 +92,7 @@ func newPlugAPIMock(users map[string]*mm_model.User) pluginAPIMock {
func (m pluginAPIMock) GetUserByUsername(name string) (*mm_model.User, error) {
user, ok := m.users[name]
if !ok {
return nil, ErrNotFound{}
return nil, model.NewErrNotFound(name)
}
return user, nil
}
@ -109,7 +111,7 @@ func (m pluginAPIMock) GetUserByID(userID string) (*mm_model.User, error) {
return user, nil
}
}
return nil, ErrNotFound{}
return nil, model.NewErrNotFound(userID)
}
func (m pluginAPIMock) GetTeamMember(teamID string, userID string) (*mm_model.TeamMember, error) {
@ -119,7 +121,7 @@ func (m pluginAPIMock) GetTeamMember(teamID string, userID string) (*mm_model.Te
}
if teamID != defTeamID {
return nil, ErrNotFound{}
return nil, model.NewErrNotFound(teamID)
}
member := &mm_model.TeamMember{
@ -130,19 +132,9 @@ func (m pluginAPIMock) GetTeamMember(teamID string, userID string) (*mm_model.Te
}
func (m pluginAPIMock) GetChannelByID(channelID string) (*mm_model.Channel, error) {
return nil, ErrNotFound{}
return nil, model.NewErrNotFound(channelID)
}
func (m pluginAPIMock) GetChannelMember(channelID string, userID string) (*mm_model.ChannelMember, error) {
return nil, ErrNotFound{}
}
func (m pluginAPIMock) IsErrNotFound(err error) bool {
return false
}
type ErrNotFound struct{}
func (e ErrNotFound) Error() string {
return "not found"
return nil, model.NewErrNotFound(userID)
}

View File

@ -10,6 +10,7 @@ import (
permissionsMocks "github.com/mattermost/focalboard/server/services/permissions/mocks"
mmModel "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
@ -25,12 +26,11 @@ type TestHelper struct {
func SetupTestHelper(t *testing.T) *TestHelper {
ctrl := gomock.NewController(t)
mockStore := permissionsMocks.NewMockStore(ctrl)
return &TestHelper{
t: t,
ctrl: ctrl,
store: mockStore,
permissions: New(mockStore, nil),
permissions: New(mockStore, mlog.CreateConsoleTestLogger(false, mlog.LvlDebug)),
}
}

View File

@ -4,9 +4,6 @@
package localpermissions
import (
"database/sql"
"errors"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/permissions"
@ -39,7 +36,7 @@ func (s *Service) HasPermissionToBoard(userID, boardID string, permission *mmMod
}
member, err := s.store.GetMemberForBoard(boardID, userID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return false
}
if err != nil {

View File

@ -4,9 +4,6 @@
package mmpermissions
import (
"database/sql"
"errors"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/permissions"
@ -43,7 +40,7 @@ func (s *Service) HasPermissionToBoard(userID, boardID string, permission *mmMod
}
board, err := s.store.GetBoard(boardID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
var boards []*model.Board
boards, err = s.store.GetBoardHistory(boardID, model.QueryBoardHistoryOptions{Limit: 1, Descending: true})
if err != nil {
@ -69,7 +66,7 @@ func (s *Service) HasPermissionToBoard(userID, boardID string, permission *mmMod
}
member, err := s.store.GetMemberForBoard(boardID, userID)
if errors.Is(err, sql.ErrNoRows) {
if model.IsErrNotFound(err) {
return false
}
if err != nil {

View File

@ -80,9 +80,8 @@ type storeMetadata struct {
}
var blacklistedStoreMethodNames = map[string]bool{
"Shutdown": true,
"IsErrNotFound": true,
"DBType": true,
"Shutdown": true,
"DBType": true,
}
func extractMethodMetadata(method *ast.Field, src []byte) methodData {

View File

@ -198,7 +198,7 @@ func (s *MattermostAuthLayer) GetTeam(id string) (*model.Team, error) {
row := query.QueryRow()
var displayName string
err := row.Scan(&displayName)
if err != nil && !s.IsErrNotFound(err) {
if err != nil && !model.IsErrNotFound(err) {
s.logger.Error("GetTeam scan error",
mlog.String("team_id", id),
mlog.Err(err),

View File

@ -1014,20 +1014,6 @@ func (mr *MockStoreMockRecorder) InsertBoardWithAdmin(arg0, arg1 interface{}) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBoardWithAdmin", reflect.TypeOf((*MockStore)(nil).InsertBoardWithAdmin), arg0, arg1)
}
// IsErrNotFound mocks base method.
func (m *MockStore) IsErrNotFound(arg0 error) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsErrNotFound", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// IsErrNotFound indicates an expected call of IsErrNotFound.
func (mr *MockStoreMockRecorder) IsErrNotFound(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsErrNotFound", reflect.TypeOf((*MockStore)(nil).IsErrNotFound), arg0)
}
// PatchBlock mocks base method.
func (m *MockStore) PatchBlock(arg0 string, arg1 *model.BlockPatch, arg2 string) error {
m.ctrl.T.Helper()

View File

@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
sq "github.com/Masterminds/squirrel"
@ -632,7 +631,7 @@ func (s *SQLStore) getBoardAndCardByID(db sq.BaseRunner, blockID string) (board
}
if len(blocks) == 0 {
return nil, nil, store.NewErrNotFound(blockID)
return nil, nil, model.NewErrNotFound(blockID)
}
return s.getBoardAndCard(db, &blocks[0])

View File

@ -3,7 +3,6 @@ package sqlstore
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
@ -292,7 +291,7 @@ func (s *SQLStore) insertBoard(db sq.BaseRunner, board *model.Board, userID stri
}
existingBoard, err := s.getBoard(db, board.ID)
if err != nil && !s.IsErrNotFound(err) {
if err != nil && !model.IsErrNotFound(err) {
return nil, fmt.Errorf("insertBoard error occurred while fetching existing board %s: %w", board.ID, err)
}
@ -466,7 +465,7 @@ func (s *SQLStore) saveMember(db sq.BaseRunner, bm *model.BoardMember) (*model.B
}
oldMember, err := s.getMemberForBoard(db, bm.BoardID, bm.UserID)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
if err != nil && !model.IsErrNotFound(err) {
return nil, err
}

View File

@ -5,7 +5,6 @@ import (
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@ -30,7 +29,7 @@ func (s *SQLStore) getCategory(db sq.BaseRunner, id string) (*model.Category, er
}
if len(categories) == 0 {
return nil, store.NewErrNotFound(id)
return nil, model.NewErrNotFound(id)
}
return &categories[0], nil

View File

@ -557,7 +557,7 @@ func (s *SQLStore) getDMBoards(tx sq.BaseRunner) ([]*model.Board, error) {
}
boards, err := s.getBoardsByCondition(tx, conditions)
if err != nil && errors.Is(err, sql.ErrNoRows) {
if err != nil && model.IsErrNotFound(err) {
return []*model.Board{}, nil
}

View File

@ -10,7 +10,6 @@ import (
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@ -103,7 +102,7 @@ func (s *SQLStore) deleteNotificationHint(db sq.BaseRunner, blockID string) erro
}
if count == 0 {
return store.NewErrNotFound(blockID)
return model.NewErrNotFound(blockID)
}
return nil
@ -135,7 +134,7 @@ func (s *SQLStore) getNotificationHint(db sq.BaseRunner, blockID string) (*model
return nil, err
}
if len(hint) == 0 {
return nil, store.NewErrNotFound(blockID)
return nil, model.NewErrNotFound(blockID)
}
return hint[0], nil
}
@ -166,7 +165,7 @@ func (s *SQLStore) getNextNotificationHint(db sq.BaseRunner, remove bool) (*mode
return nil, err
}
if len(hints) == 0 {
return nil, store.NewErrNotFound("")
return nil, model.NewErrNotFound("")
}
hint := hints[0]
@ -187,7 +186,7 @@ func (s *SQLStore) getNextNotificationHint(db sq.BaseRunner, remove bool) (*mode
if rows == 0 {
// another node likely has grabbed this hint for processing concurrently; let that node handle it
// and we'll return an error here so we try again.
return nil, store.NewErrNotFound(hint.BlockID)
return nil, model.NewErrNotFound(hint.BlockID)
}
}

View File

@ -8,7 +8,6 @@ import (
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
@ -114,7 +113,7 @@ func (s *SQLStore) deleteSubscription(db sq.BaseRunner, blockID string, subscrib
}
if count == 0 {
return store.NewErrNotFound(blockID + "," + subscriberID)
return model.NewErrNotFound(blockID + "," + subscriberID)
}
return nil
@ -150,7 +149,7 @@ func (s *SQLStore) getSubscription(db sq.BaseRunner, blockID string, subscriberI
return nil, err
}
if len(subscriptions) == 0 {
return nil, store.NewErrNotFound(blockID + "," + subscriberID)
return nil, model.NewErrNotFound(blockID + "," + subscriberID)
}
return subscriptions[0], nil
}

View File

@ -1,9 +1,6 @@
package sqlstore
import (
"database/sql"
"errors"
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/focalboard/server/model"
)
@ -17,7 +14,7 @@ func (s *SQLStore) getSystemSetting(db sq.BaseRunner, key string) (string, error
var result string
err := scanner.Scan(&result)
if err != nil && !errors.Is(sql.ErrNoRows, err) {
if err != nil && !model.IsErrNotFound(err) {
return "", err
}

View File

@ -8,7 +8,6 @@ import (
"strings"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@ -21,7 +20,7 @@ func (s *SQLStore) CloseRows(rows *sql.Rows) {
}
func (s *SQLStore) IsErrNotFound(err error) bool {
return store.IsErrNotFound(err)
return model.IsErrNotFound(err)
}
func PrepareNewTestDatabase() (dbType string, connectionString string, err error) {

View File

@ -3,9 +3,6 @@
package store
import (
"database/sql"
"errors"
"fmt"
"time"
"github.com/mattermost/focalboard/server/model"
@ -136,42 +133,5 @@ type Store interface {
DBType() string
IsErrNotFound(err error) bool
GetLicense() *mmModel.License
}
// ErrNotFound is an error type that can be returned by store APIs when a query unexpectedly fetches no records.
type ErrNotFound struct {
resource string
}
// NewErrNotFound creates a new ErrNotFound instance.
func NewErrNotFound(resource string) *ErrNotFound {
return &ErrNotFound{
resource: resource,
}
}
func (nf *ErrNotFound) Error() string {
return fmt.Sprintf("{%s} not found", nf.resource)
}
// IsErrNotFound returns true if `err` is or wraps a ErrNotFound.
func IsErrNotFound(err error) bool {
if err == nil {
return false
}
// check if this is a store.ErrNotFound
var nf *ErrNotFound
if errors.As(err, &nf) {
return true
}
// check if this is a sql.ErrNotFound
if errors.Is(err, sql.ErrNoRows) {
return true
}
return false
}

View File

@ -1,7 +1,6 @@
package storetests
import (
"database/sql"
"testing"
"time"
@ -106,7 +105,7 @@ func testGetBoard(t *testing.T, store store.Store) {
t.Run("nonexisting board", func(t *testing.T) {
rBoard, err := store.GetBoard("nonexistent-id")
require.ErrorIs(t, err, sql.ErrNoRows)
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
require.Nil(t, rBoard)
})
}
@ -237,7 +236,7 @@ func testInsertBoard(t *testing.T, store store.Store) {
require.Error(t, err)
rBoard, err := store.GetBoard(board.ID)
require.ErrorIs(t, err, sql.ErrNoRows)
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
require.Nil(t, rBoard)
})
@ -479,7 +478,7 @@ func testDeleteBoard(t *testing.T, store store.Store) {
require.NoError(t, store.DeleteBoard(boardID, userID))
r2Board, err := store.GetBoard(boardID)
require.ErrorIs(t, err, sql.ErrNoRows)
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
require.Nil(t, r2Board)
})
}
@ -569,7 +568,7 @@ func testGetMemberForBoard(t *testing.T, store store.Store) {
t.Run("should return a no rows error for nonexisting membership", func(t *testing.T) {
bm, err := store.GetMemberForBoard(boardID, userID)
require.ErrorIs(t, err, sql.ErrNoRows)
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
require.Nil(t, bm)
})
@ -674,7 +673,7 @@ func testDeleteMember(t *testing.T, store store.Store) {
require.NoError(t, store.DeleteMember(boardID, userID))
rbm, err := store.GetMemberForBoard(boardID, userID)
require.ErrorIs(t, err, sql.ErrNoRows)
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
require.Nil(t, rbm)
memberHistory, err = store.GetBoardMemberHistory(boardID, userID, 0)

View File

@ -124,13 +124,13 @@ func testDeleteNotificationHint(t *testing.T, store store.Store) {
// check the notification hint was deleted
hint, err = store.GetNotificationHint(hintNew.BlockID)
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
require.True(t, model.IsErrNotFound(err), "error should be of type store.ErrNotFound")
assert.Nil(t, hint)
})
t.Run("delete non-existent notification hint", func(t *testing.T) {
err := store.DeleteNotificationHint("bogus")
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
require.True(t, model.IsErrNotFound(err), "error should be of type store.ErrNotFound")
})
}
@ -152,7 +152,7 @@ func testGetNotificationHint(t *testing.T, store store.Store) {
t.Run("get non-existent notification hint", func(t *testing.T) {
hint, err := store.GetNotificationHint("bogus")
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
require.True(t, model.IsErrNotFound(err), "error should be of type store.ErrNotFound")
assert.Nil(t, hint, "hint should be nil")
})
}
@ -199,7 +199,7 @@ func testGetNextNotificationHint(t *testing.T, store store.Store) {
for {
hint, err2 := store.GetNextNotificationHint(false)
if store.IsErrNotFound(err2) {
if model.IsErrNotFound(err2) {
break
}
require.NoError(t, err2, "get next notification hint should not error")
@ -209,7 +209,7 @@ func testGetNextNotificationHint(t *testing.T, store store.Store) {
}
_, err = store.GetNextNotificationHint(false)
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
require.True(t, model.IsErrNotFound(err), "error should be of type store.ErrNotFound")
})
t.Run("get next notification hint and remove", func(t *testing.T) {
@ -232,14 +232,14 @@ func testGetNextNotificationHint(t *testing.T, store store.Store) {
// should be no hint left
_, err = store.GetNextNotificationHint(false)
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
require.True(t, model.IsErrNotFound(err), "error should be of type store.ErrNotFound")
})
}
func emptyNotificationHintTable(store store.Store) error {
for {
hint, err := store.GetNextNotificationHint(false)
if store.IsErrNotFound(err) {
if model.IsErrNotFound(err) {
break
}

View File

@ -172,9 +172,7 @@ func testDeleteSubscription(t *testing.T, s store.Store) {
t.Run("delete non-existent subscription", func(t *testing.T) {
err := s.DeleteSubscription("bogus", "bogus")
require.Error(t, err, "delete non-existent subscription should error")
var nf *store.ErrNotFound
require.ErrorAs(t, err, &nf, "error should be of type store.ErrNotFound")
require.True(t, store.IsErrNotFound(err))
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
})
}
@ -243,9 +241,7 @@ func testGetSubscription(t *testing.T, s store.Store) {
t.Run("get non-existent subscription", func(t *testing.T) {
sub, err := s.GetSubscription("bogus", "bogus")
require.Error(t, err, "get non-existent subscription should error")
var nf *store.ErrNotFound
require.ErrorAs(t, err, &nf, "error should be of type store.ErrNotFound")
require.True(t, store.IsErrNotFound(err))
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
require.Nil(t, sub, "get subscription should return nil")
})
}

View File

@ -4,7 +4,6 @@
package storetests
import (
"database/sql"
"testing"
"time"
@ -50,7 +49,7 @@ func testGetTeamUsers(t *testing.T, store store.Store) {
t.Run("GetTeamUSers", func(t *testing.T) {
users, err := store.GetUsersByTeam("team_1")
require.Equal(t, 0, len(users))
require.Equal(t, sql.ErrNoRows, err)
require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error")
userID := utils.NewID(utils.IDTypeUser)