diff --git a/backend/pkg/spot/api/handlers.go b/backend/pkg/spot/api/handlers.go index e1afa2c08..42765e213 100644 --- a/backend/pkg/spot/api/handlers.go +++ b/backend/pkg/spot/api/handlers.go @@ -278,7 +278,7 @@ func (e *Router) getSpots(w http.ResponseWriter, r *http.Request) { default: opts.TenantID = user.TenantID } - spots, total, err := e.services.Spots.Get(user, opts) + spots, total, tenantHasSpots, err := e.services.Spots.Get(user, opts) if err != nil { e.ResponseWithError(r.Context(), w, http.StatusInternalServerError, err, startTime, r.URL.Path, bodySize) return @@ -298,7 +298,7 @@ func (e *Router) getSpots(w http.ResponseWriter, r *http.Request) { PreviewURL: previewUrl, }) } - e.ResponseWithJSON(r.Context(), w, &GetSpotsResponse{Spots: res, Total: total}, startTime, r.URL.Path, bodySize) + e.ResponseWithJSON(r.Context(), w, &GetSpotsResponse{Spots: res, Total: total, TenantHasSpots: tenantHasSpots}, startTime, r.URL.Path, bodySize) } func (e *Router) deleteSpots(w http.ResponseWriter, r *http.Request) { diff --git a/backend/pkg/spot/api/model.go b/backend/pkg/spot/api/model.go index 70da02bf7..86a487bd9 100644 --- a/backend/pkg/spot/api/model.go +++ b/backend/pkg/spot/api/model.go @@ -53,8 +53,9 @@ type ShortInfo struct { } type GetSpotsResponse struct { - Spots []ShortInfo `json:"spots"` - Total uint64 `json:"total"` + Spots []ShortInfo `json:"spots"` + Total uint64 `json:"total"` + TenantHasSpots bool `json:"tenantHasSpots"` } type UpdateSpotRequest struct { diff --git a/backend/pkg/spot/service/spot.go b/backend/pkg/spot/service/spot.go index da860df70..a2ef2bca9 100644 --- a/backend/pkg/spot/service/spot.go +++ b/backend/pkg/spot/service/spot.go @@ -60,7 +60,7 @@ type Update struct { type Spots interface { Add(user *auth.User, name, comment string, duration int, crop []int) (*Spot, error) GetByID(user *auth.User, spotID uint64) (*Spot, error) - Get(user *auth.User, opts *GetOpts) ([]*Spot, uint64, error) + Get(user *auth.User, opts *GetOpts) ([]*Spot, uint64, bool, error) UpdateName(user *auth.User, spotID uint64, newName string) (*Spot, error) AddComment(user *auth.User, spotID uint64, comment *Comment) (*Spot, error) Delete(user *auth.User, spotIds []uint64) error @@ -176,14 +176,14 @@ func (s *spotsImpl) getByID(spotID uint64, user *auth.User) (*Spot, error) { return spot, nil } -func (s *spotsImpl) Get(user *auth.User, opts *GetOpts) ([]*Spot, uint64, error) { +func (s *spotsImpl) Get(user *auth.User, opts *GetOpts) ([]*Spot, uint64, bool, error) { switch { case user == nil: - return nil, 0, fmt.Errorf("user is required") + return nil, 0, false, fmt.Errorf("user is required") case opts == nil: - return nil, 0, fmt.Errorf("get options are required") + return nil, 0, false, fmt.Errorf("get options are required") case user.TenantID == 0: // Tenant ID is required even for public get functions - return nil, 0, fmt.Errorf("tenant id is required") + return nil, 0, false, fmt.Errorf("tenant id is required") } // Show the latest spots first by default @@ -200,22 +200,22 @@ func (s *spotsImpl) Get(user *auth.User, opts *GetOpts) ([]*Spot, uint64, error) return s.getAll(user, opts) } -func (s *spotsImpl) getAll(user *auth.User, opts *GetOpts) ([]*Spot, uint64, error) { +func (s *spotsImpl) getAll(user *auth.User, opts *GetOpts) ([]*Spot, uint64, bool, error) { sql := `SELECT COUNT(1) OVER () AS total, s.spot_id, s.name, u.email, s.duration, s.created_at FROM spots.spots s JOIN public.users u ON s.user_id = u.user_id WHERE s.tenant_id = $1 AND s.deleted_at IS NULL` args := []interface{}{user.TenantID} if opts.UserID != 0 { - sql += ` AND user_id = ` + fmt.Sprintf("$%d", len(args)+1) + sql += ` AND s.user_id = ` + fmt.Sprintf("$%d", len(args)+1) args = append(args, opts.UserID) } if opts.NameFilter != "" { - sql += ` AND name ILIKE ` + fmt.Sprintf("$%d", len(args)+1) + sql += ` AND s.name ILIKE ` + fmt.Sprintf("$%d", len(args)+1) args = append(args, "%"+opts.NameFilter+"%") } if opts.Order != "" { - sql += ` ORDER BY created_at ` + opts.Order + sql += ` ORDER BY s.created_at ` + opts.Order } if opts.Limit != 0 { sql += ` LIMIT ` + fmt.Sprintf("$%d", len(args)+1) @@ -227,20 +227,38 @@ func (s *spotsImpl) getAll(user *auth.User, opts *GetOpts) ([]*Spot, uint64, err } rows, err := s.pgconn.Query(sql, args...) if err != nil { - return nil, 0, err + return nil, 0, false, err } defer rows.Close() - var total uint64 - var spots []*Spot + var ( + total uint64 + spots []*Spot + hasSpots bool + ) for rows.Next() { spot := &Spot{} if err = rows.Scan(&total, &spot.ID, &spot.Name, &spot.UserEmail, &spot.Duration, &spot.CreatedAt); err != nil { - return nil, 0, err + return nil, 0, false, err } spots = append(spots, spot) } - return spots, total, nil + if len(spots) == 0 { + hasSpots = s.doesTenantHasSpots(user.TenantID) + } else { + hasSpots = true + } + return spots, total, hasSpots, nil +} + +func (s *spotsImpl) doesTenantHasSpots(tenantID uint64) bool { + sql := `SELECT 1 FROM spots.spots s WHERE s.tenant_id = $1 AND s.deleted_at IS NULL LIMIT 1;` + var count uint64 + if err := s.pgconn.QueryRow(sql, tenantID).Scan(&count); err != nil { + s.log.Info(context.Background(), "failed to check if tenant has spots: %s", err) + return false + } + return count > 0 } func (s *spotsImpl) UpdateName(user *auth.User, spotID uint64, newName string) (*Spot, error) {