~netlandish/links

41c59fd8e430587b37938108f1734a58e3794353 — Peter Sanchez 5 months ago 7baf4f2
Fixing validation misses in Slack command
2 files changed, 72 insertions(+), 32 deletions(-)

M api/graph/schema.resolvers.go
M slack/commands.go
M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +11 -12
@@ 1819,7 1819,7 @@ func (r *mutationResolver) AddLinkShort(ctx context.Context, input *model.LinkSh
	}

	if input.ShortCode != nil {
		validator.Expect(len(*input.ShortCode) < 20,
		validator.Expect(len(*input.ShortCode) <= 20,
			lt.Translate("Short Code may not exceed 20 characters")).
			WithField("shortCode").
			WithCode(valid.ErrValidationCode)


@@ 1908,10 1908,12 @@ func (r *mutationResolver) AddLinkShort(ctx context.Context, input *model.LinkSh
	}

	// Validate custom short code
	if input.ShortCode != nil {
	var shortCode string
	if input.ShortCode != nil && *input.ShortCode != "" {
		shortCode = links.SlugifyCase(*input.ShortCode)
		opts := &database.FilterOptions{
			Filter: sq.And{
				sq.Eq{"l.short_code": *input.ShortCode},
				sq.Eq{"l.short_code": shortCode},
				sq.Eq{"l.domain_id": input.DomainID},
			},
			Limit: 1,


@@ 1931,16 1933,13 @@ func (r *mutationResolver) AddLinkShort(ctx context.Context, input *model.LinkSh
	}

	linkShort := &models.LinkShort{
		URL:      input.URL,
		OrgID:    org.ID,
		UserID:   int(user.ID),
		DomainID: input.DomainID,
		URL:       input.URL,
		OrgID:     org.ID,
		UserID:    int(user.ID),
		DomainID:  input.DomainID,
		ShortCode: shortCode, // if "", it will be generated before saving
	}

	if input.ShortCode != nil && *input.ShortCode != "" {
		shortCode := links.SlugifyCase(*input.ShortCode)
		linkShort.ShortCode = shortCode
	}
	if input.Title != nil {
		linkShort.Title = *input.Title
	}


@@ 2001,7 2000,7 @@ func (r *mutationResolver) UpdateLinkShort(ctx context.Context, input *model.Upd
	}

	if input.ShortCode != nil {
		validator.Expect(len(*input.ShortCode) < 20,
		validator.Expect(len(*input.ShortCode) <= 20,
			lt.Translate("Short Code may not exceed 20 characters")).
			WithField("shortCode").
			WithCode(valid.ErrValidationCode)

M slack/commands.go => slack/commands.go +61 -20
@@ 7,13 7,16 @@ import (
	"links/internal/localizer"
	"links/models"
	"net/http"
	"net/url"
	"strings"

	"git.sr.ht/~emersion/gqlclient"
	sq "github.com/Masterminds/squirrel"
	"github.com/labstack/echo/v4"
	"golang.org/x/exp/slices"
	"netlandish.com/x/gobwebs/auth"
	"netlandish.com/x/gobwebs/database"
	"netlandish.com/x/gobwebs/server"
)

type SlackBlock struct {


@@ 30,11 33,12 @@ type SlackCommandResponse struct {
	Blocks       []SlackBlock `json:"blocks"`
}

func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCommandResponse, error) {
func linkAddShort(c echo.Context, surl, teamID, slackUser, text string) (*SlackCommandResponse, error) {
	opts := &database.FilterOptions{
		Filter: sq.Eq{"sc.team_id": teamID},
		Limit:  1,
	}
	gctx := c.(*server.Context)
	ctx := c.Request().Context()
	lt := localizer.GetSessionLocalizer(c)
	slackConns, err := models.GetSlackConnections(ctx, opts)


@@ 64,7 68,7 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo
	}

	if len(slackUsers) == 0 {
		return sendInstructionLink(c, slackConn, url, slackUser)
		return sendInstructionLink(c, slackConn, surl, slackUser)
	}

	user, err := models.GetUser(ctx, slackUsers[0].UserID, true)


@@ 78,38 82,66 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo
	// index 0 is url
	// index 1 is shortcode
	// index 2 is domain
	input := strings.Split(text, " ")
	shortCodeArg := strings.Split(input[1], ":")
	if len(shortCodeArg) != 2 {
		block.Text.Text = lt.Translate("Wrong short code argument")
	var lurl, domstr, code string

	for _, blob := range strings.Split(text, " ") {
		_, err = url.Parse(blob)
		if err != nil {
			lurl = blob
			continue
		}

		parts := strings.SplitN(blob, ":", 2)
		if len(parts) != 2 {
			// Invalid input, skip
			continue
		}

		if !slices.Contains([]string{"code", "domain"}, parts[0]) {
			// Invalid keyword, skip
			continue
		}

		if parts[0] == "code" {
			code = parts[1]
		} else {
			domstr = parts[1]
		}
	}

	if lurl == "" {
		block.Text.Text = lt.Translate("No URL argument was given")
		commandResp := &SlackCommandResponse{
			Text:   "Error",
			Blocks: []SlackBlock{block},
		}
		return commandResp, nil

	}

	domainArg := strings.Split(input[2], ":")
	if len(domainArg) != 2 {
		block.Text.Text = lt.Translate("Wrong short code argument")
	if domstr == "" {
		block.Text.Text = lt.Translate("Wrong domain argument")
		commandResp := &SlackCommandResponse{
			Text:   "Error",
			Blocks: []SlackBlock{block},
		}
		return commandResp, nil

	}

	// Check that the domain exists, it is a short service domain
	// and belong to the slack connection organization
	opts = &database.FilterOptions{
		Filter: sq.And{
			sq.Eq{"d.lookup_name": domainArg[1]},
			sq.Eq{"d.lookup_name": strings.ToLower(domstr)},
			sq.Eq{"d.service": models.DomainServiceShort},
			sq.Eq{"d.status": models.DomainStatusApproved},
			sq.Eq{"d.is_active": true},
			sq.Eq{"d.org_id": slackConn.OrgID},
			sq.Or{
				sq.And{
					sq.Eq{"d.org_id": slackConn.OrgID},
					sq.Eq{"d.level": models.DomainLevelUser},
				},
				sq.Eq{"d.level": models.DomainLevelSystem},
			},
		},
		Limit: 1,
	}


@@ 119,7 151,7 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo
		return nil, err
	}
	if len(domains) == 0 {
		block.Text.Text = lt.Translate("%s: domain not found", domainArg[1])
		block.Text.Text = lt.Translate("%s: domain not found", domstr)
		commandResp := &SlackCommandResponse{
			Text:   "Error",
			Blocks: []SlackBlock{block},


@@ 143,21 175,30 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo
							domainId: $domain,
							shortCode: $short}) {
					id
					shortCode
				}
		}`)

	op.Var("title", input[0])
	op.Var("url", input[0])
	if len(lurl) > 150 {
		op.Var("title", fmt.Sprintf("%s...", lurl[:146]))
	} else {
		op.Var("title", lurl)
	}
	op.Var("url", lurl)
	op.Var("slug", slackConn.OrgSlug)
	op.Var("domain", domain.ID)
	op.Var("short", shortCodeArg[1])
	op.Var("short", code)
	err = links.Execute(ctx, op, &result)
	if err != nil {
		return nil, err
	}
	block.Type = "section"
	block.Text.Type = "mrkdwn"
	block.Text.Text = lt.Translate("Your short link was successfully created")

	shortURL := &url.URL{
		Scheme: gctx.Server.Config.Scheme,
		Host:   domain.LookupName,
		Path:   "/" + result.LinkShort.ShortCode,
	}
	block.Text.Text = lt.Translate("Your short link was successfully created: %s", shortURL.String())
	commandResp := &SlackCommandResponse{
		Text:   "Add Link",
		Blocks: []SlackBlock{block},