Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bugfix] Fix setting immediate expires_at value on filter endpoints #3513

Merged
merged 4 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 8 additions & 20 deletions internal/api/client/accounts/mute.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ package accounts

import (
"errors"
"fmt"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
Expand Down Expand Up @@ -140,25 +138,15 @@ func normalizeCreateUpdateMute(form *apimodel.UserMuteCreateUpdateRequest) error
// Apply defaults for missing fields.
form.Notifications = util.Ptr(util.PtrOrValue(form.Notifications, false))

// Normalize mute duration if necessary.
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
if ei := form.DurationI; ei != nil {
switch e := ei.(type) {
case float64:
form.Duration = util.Ptr(int(e))

case string:
duration, err := strconv.Atoi(e)
if err != nil {
return fmt.Errorf("could not parse duration value %s as integer: %w", e, err)
}

form.Duration = &duration

default:
return fmt.Errorf("could not parse expires_in type %T as integer", ei)
// Normalize duration if necessary.
if form.DurationI != nil {
// If we parsed this as JSON, duration
// may be either a float64 or a string.
duration, err := apiutil.ParseDuration(form.DurationI, "duration")
if err != nil {
return err
}
form.Duration = duration
}

// Interpret zero as indefinite duration.
Expand Down
39 changes: 18 additions & 21 deletions internal/api/client/filters/v1/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ package v1

import (
"errors"
"fmt"
"strconv"

"github.com/superseriousbusiness/gotosocial/internal/api/model"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)

func validateNormalizeCreateUpdateFilter(form *model.FilterCreateUpdateRequestV1) error {
func validateNormalizeCreateUpdateFilter(form *apimodel.FilterCreateUpdateRequestV1) error {
if err := validate.FilterKeyword(form.Phrase); err != nil {
return err
}
Expand All @@ -48,25 +47,23 @@ func validateNormalizeCreateUpdateFilter(form *model.FilterCreateUpdateRequestV1
}

// Normalize filter expiry if necessary.
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
if ei := form.ExpiresInI; ei != nil {
switch e := ei.(type) {
case float64:
form.ExpiresIn = util.Ptr(int(e))

case string:
expiresIn, err := strconv.Atoi(e)
if err != nil {
return fmt.Errorf("could not parse expires_in value %s as integer: %w", e, err)
}

form.ExpiresIn = &expiresIn

default:
return fmt.Errorf("could not parse expires_in type %T as integer", ei)
if form.ExpiresInI != nil {
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
var err error
form.ExpiresIn, err = apiutil.ParseDuration(
form.ExpiresInI,
"expires_in",
)
if err != nil {
return err
}
}

// Interpret zero as indefinite duration.
if form.ExpiresIn != nil && *form.ExpiresIn == 0 {
form.ExpiresIn = nil
}

return nil
}
34 changes: 15 additions & 19 deletions internal/api/client/filters/v2/filterpost.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
package v2

import (
"fmt"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
Expand Down Expand Up @@ -228,26 +226,24 @@ func validateNormalizeCreateFilter(form *apimodel.FilterCreateRequestV2) error {
form.FilterAction = util.Ptr(action)

// Normalize filter expiry if necessary.
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
if ei := form.ExpiresInI; ei != nil {
switch e := ei.(type) {
case float64:
form.ExpiresIn = util.Ptr(int(e))

case string:
expiresIn, err := strconv.Atoi(e)
if err != nil {
return fmt.Errorf("could not parse expires_in value %s as integer: %w", e, err)
}

form.ExpiresIn = &expiresIn

default:
return fmt.Errorf("could not parse expires_in type %T as integer", ei)
if form.ExpiresInI != nil {
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
var err error
form.ExpiresIn, err = apiutil.ParseDuration(
form.ExpiresInI,
"expires_in",
)
if err != nil {
return err
}
}

// Interpret zero as indefinite duration.
if form.ExpiresIn != nil && *form.ExpiresIn == 0 {
form.ExpiresIn = nil
}

// Normalize and validate new keywords and statuses.
for i, formKeyword := range form.Keywords {
if err := validate.FilterKeyword(formKeyword.Keyword); err != nil {
Expand Down
34 changes: 15 additions & 19 deletions internal/api/client/filters/v2/filterput.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ package v2

import (
"errors"
"fmt"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
Expand Down Expand Up @@ -272,26 +270,24 @@ func validateNormalizeUpdateFilter(form *apimodel.FilterUpdateRequestV2) error {
}

// Normalize filter expiry if necessary.
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
if ei := form.ExpiresInI; ei != nil {
switch e := ei.(type) {
case float64:
form.ExpiresIn = util.Ptr(int(e))

case string:
expiresIn, err := strconv.Atoi(e)
if err != nil {
return fmt.Errorf("could not parse expires_in value %s as integer: %w", e, err)
}

form.ExpiresIn = &expiresIn

default:
return fmt.Errorf("could not parse expires_in type %T as integer", ei)
if form.ExpiresInI != nil {
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
var err error
form.ExpiresIn, err = apiutil.ParseDuration(
form.ExpiresInI,
"expires_in",
)
if err != nil {
return err
}
}

// Interpret zero as indefinite duration.
if form.ExpiresIn != nil && *form.ExpiresIn == 0 {
form.ExpiresIn = nil
}

// Normalize and validate updates.
for i, formKeyword := range form.Keywords {
if formKeyword.Keyword != nil {
Expand Down
33 changes: 13 additions & 20 deletions internal/api/client/statuses/statuscreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
Expand Down Expand Up @@ -474,25 +473,19 @@ func validateStatusPoll(form *apimodel.StatusCreateRequest) gtserror.WithCode {
}

// Normalize poll expiry if necessary.
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
if ei := form.Poll.ExpiresInI; ei != nil {
switch e := ei.(type) {
case float64:
form.Poll.ExpiresIn = int(e)

case string:
expiresIn, err := strconv.Atoi(e)
if err != nil {
text := fmt.Sprintf("could not parse expires_in value %s as integer: %v", e, err)
return gtserror.NewErrorBadRequest(errors.New(text), text)
}

form.Poll.ExpiresIn = expiresIn

default:
text := fmt.Sprintf("could not parse expires_in type %T as integer", ei)
return gtserror.NewErrorBadRequest(errors.New(text), text)
if form.Poll.ExpiresInI != nil {
// If we parsed this as JSON, expires_in
// may be either a float64 or a string.
expiresIn, err := apiutil.ParseDuration(
form.Poll.ExpiresInI,
"expires_in",
)
if err != nil {
return gtserror.NewErrorBadRequest(err, err.Error())
}

if expiresIn != nil {
form.Poll.ExpiresIn = *expiresIn
}
}

Expand Down
70 changes: 70 additions & 0 deletions internal/api/util/parseform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// GoToSocial
// Copyright (C) GoToSocial Authors [email protected]
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package util

import (
"fmt"
"strconv"
)

// ParseDuration parses the given raw interface belonging to
// the given fieldName as an integer duration.
//
// Will return nil, nil if rawI is the zero value of its type.
func ParseDuration(rawI any, fieldName string) (*int, error) {
var (
asInteger int
err error
)

switch raw := rawI.(type) {
case float64:
// Submitted as JSON number
// (casts to float64 by default).
asInteger = int(raw)

case string:
// Submitted as JSON string or form field.
asInteger, err = strconv.Atoi(raw)
if err != nil {
err = fmt.Errorf(
"could not parse %s value %s as integer: %w",
fieldName, raw, err,
)
}

default:
// Submitted as god-knows-what.
err = fmt.Errorf(
"could not parse %s type %T as integer",
fieldName, rawI,
)
}

if err != nil {
return nil, err
}

// Someone submitted 0,
// don't point to this.
if asInteger == 0 {
return nil, nil
}

return &asInteger, nil
}