Skip to content

Commit

Permalink
[feature] Allow newly uploaded emojis to be placed in categories (#939)
Browse files Browse the repository at this point in the history
* [feature] Add emoji categories GET
Serialize emojis in appropriate categories; make it possible to get categories via the admin API

* [feature] Create (or use existing) category for new emoji uploads

* fix lint issue

* update misleading line in swagger docs
  • Loading branch information
tsmethurst authored Nov 14, 2022
1 parent 8c20ccd commit 4cd00d5
Show file tree
Hide file tree
Showing 31 changed files with 916 additions and 52 deletions.
56 changes: 56 additions & 0 deletions docs/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -859,8 +859,27 @@ definitions:
type: object
x-go-name: Emoji
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
emojiCategory:
properties:
id:
description: The ID of the custom emoji category.
type: string
x-go-name: ID
name:
description: The name of the custom emoji category.
type: string
x-go-name: Name
title: EmojiCategory represents a custom emoji category.
type: object
x-go-name: EmojiCategory
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
emojiCreateRequest:
properties:
CategoryName:
description: |-
Category in which to place the new emoji. Will be uncategorized by default.
CategoryName length should not exceed 64 characters.
type: string
Image:
description: Image file to use for the emoji. Must be png or gif and no larger than 50kb.
Shortcode:
Expand Down Expand Up @@ -2755,6 +2774,10 @@ paths:
name: image
required: true
type: file
- description: Category in which to place the new emoji. 64 characters or less. If left blank, emoji will be uncategorized. If a category with the given name doesn't exist yet, it will be created.
in: formData
name: category
type: string
produces:
- application/json
responses:
Expand Down Expand Up @@ -2852,6 +2875,39 @@ paths:
summary: Get the admin view of a single emoji.
tags:
- admin
/api/v1/admin/custom_emojis/categories:
get:
operationId: emojiCategoriesGet
parameters:
- description: The id of the emoji.
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: Array of existing emoji categories.
schema:
items:
$ref: '#/definitions/adminEmojiCategory'
type: array
"400":
description: bad request
"401":
description: unauthorized
"403":
description: forbidden
"404":
description: not found
"406":
description: not acceptable
"500":
description: internal server error
summary: Get a list of existing emoji categories.
tags:
- admin
/api/v1/admin/domain_blocks:
get:
operationId: domainBlocksGet
Expand Down
3 changes: 3 additions & 0 deletions internal/api/client/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const (
EmojiPath = BasePath + "/custom_emojis"
// EmojiPathWithID is used for interacting with a single emoji.
EmojiPathWithID = EmojiPath + "/:" + IDKey
// EmojiCategoriesPath is used for interacting with emoji categories.
EmojiCategoriesPath = EmojiPath + "/categories"
// DomainBlocksPath is used for posting domain blocks.
DomainBlocksPath = BasePath + "/domain_blocks"
// DomainBlocksPathWithID is used for interacting with a single domain block.
Expand Down Expand Up @@ -87,5 +89,6 @@ func (m *Module) Route(r router.Router) error {
r.AttachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
r.AttachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
r.AttachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
r.AttachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
return nil
}
18 changes: 10 additions & 8 deletions internal/api/client/admin/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,15 @@ type AdminStandardTestSuite struct {
sentEmails map[string]string

// standard suite models
testTokens map[string]*gtsmodel.Token
testClients map[string]*gtsmodel.Client
testApplications map[string]*gtsmodel.Application
testUsers map[string]*gtsmodel.User
testAccounts map[string]*gtsmodel.Account
testAttachments map[string]*gtsmodel.MediaAttachment
testStatuses map[string]*gtsmodel.Status
testEmojis map[string]*gtsmodel.Emoji
testTokens map[string]*gtsmodel.Token
testClients map[string]*gtsmodel.Client
testApplications map[string]*gtsmodel.Application
testUsers map[string]*gtsmodel.User
testAccounts map[string]*gtsmodel.Account
testAttachments map[string]*gtsmodel.MediaAttachment
testStatuses map[string]*gtsmodel.Status
testEmojis map[string]*gtsmodel.Emoji
testEmojiCategories map[string]*gtsmodel.EmojiCategory

// module being tested
adminModule *admin.Module
Expand All @@ -75,6 +76,7 @@ func (suite *AdminStandardTestSuite) SetupSuite() {
suite.testAttachments = testrig.NewTestAttachments()
suite.testStatuses = testrig.NewTestStatuses()
suite.testEmojis = testrig.NewTestEmojis()
suite.testEmojiCategories = testrig.NewTestEmojiCategories()
}

func (suite *AdminStandardTestSuite) SetupTest() {
Expand Down
94 changes: 94 additions & 0 deletions internal/api/client/admin/emojicategoriesget.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
GoToSocial
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
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 admin

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)

// EmojiCategoriesGETHandler swagger:operation GET /api/v1/admin/custom_emojis/categories emojiCategoriesGet
//
// Get a list of existing emoji categories.
//
// ---
// tags:
// - admin
//
// produces:
// - application/json
//
// parameters:
// -
// name: id
// type: string
// description: The id of the emoji.
// in: path
// required: true
//
// responses:
// '200':
// description: Array of existing emoji categories.
// schema:
// type: array
// items:
// "$ref": "#/definitions/adminEmojiCategory"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
// '406':
// description: not acceptable
// '500':
// description: internal server error
func (m *Module) EmojiCategoriesGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}

if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}

if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}

categories, errWithCode := m.processor.AdminEmojiCategoriesGet(c.Request.Context())
if errWithCode != nil {
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}

c.JSON(http.StatusOK, categories)
}
53 changes: 53 additions & 0 deletions internal/api/client/admin/emojicategoriesget_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
GoToSocial
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
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 admin_test

import (
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
)

type EmojiCategoriesGetTestSuite struct {
AdminStandardTestSuite
}

func (suite *EmojiCategoriesGetTestSuite) TestEmojiCategoriesGet() {
recorder := httptest.NewRecorder()

path := admin.EmojiCategoriesPath
ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")

suite.adminModule.EmojiCategoriesGETHandler(ctx)
suite.Equal(http.StatusOK, recorder.Code)

b, err := io.ReadAll(recorder.Body)
suite.NoError(err)
suite.NotNil(b)

suite.Equal(`[{"id":"01GGQ989PTT9PMRN4FZ1WWK2B9","name":"cute stuff"},{"id":"01GGQ8V4993XK67B2JB396YFB7","name":"reactions"}]`, string(b))
}

func TestEmojiCategoriesGetTestSuite(t *testing.T) {
suite.Run(t, &EmojiCategoriesGetTestSuite{})
}
15 changes: 14 additions & 1 deletion internal/api/client/admin/emojicreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ import (
// To ensure compatibility with other fedi implementations, emoji size limit is 50kb by default.
// type: file
// required: true
// -
// name: category
// in: formData
// description: >-
// Category in which to place the new emoji. 64 characters or less.
// If left blank, emoji will be uncategorized. If a category with the
// given name doesn't exist yet, it will be created.
// type: string
// required: false
//
// security:
// - OAuth2 Bearer:
Expand Down Expand Up @@ -136,5 +145,9 @@ func validateCreateEmoji(form *model.EmojiCreateRequest) error {
return fmt.Errorf("emoji image too large: image is %dKB but size limit for custom emojis is %dKB", form.Image.Size/1024, maxSize/1024)
}

return validate.EmojiShortcode(form.Shortcode)
if err := validate.EmojiShortcode(form.Shortcode); err != nil {
return err
}

return validate.EmojiCategory(form.CategoryName)
}
Loading

0 comments on commit 4cd00d5

Please sign in to comment.