mirror of
				https://github.com/superseriousbusiness/gotosocial
				synced 2025-06-05 21:59:39 +02:00 
			
		
		
		
	[feature] Add /api/v1/admin/custom_emojis/{id} endpoint for single emoji GET (#910)
				
					
				
			* fix error in prev swagger docs * add GET for single admin emoji
This commit is contained in:
		@@ -60,6 +60,7 @@ type AdminStandardTestSuite struct {
 | 
			
		||||
	testAccounts     map[string]*gtsmodel.Account
 | 
			
		||||
	testAttachments  map[string]*gtsmodel.MediaAttachment
 | 
			
		||||
	testStatuses     map[string]*gtsmodel.Status
 | 
			
		||||
	testEmojis       map[string]*gtsmodel.Emoji
 | 
			
		||||
 | 
			
		||||
	// module being tested
 | 
			
		||||
	adminModule *admin.Module
 | 
			
		||||
@@ -73,6 +74,7 @@ func (suite *AdminStandardTestSuite) SetupSuite() {
 | 
			
		||||
	suite.testAccounts = testrig.NewTestAccounts()
 | 
			
		||||
	suite.testAttachments = testrig.NewTestAttachments()
 | 
			
		||||
	suite.testStatuses = testrig.NewTestStatuses()
 | 
			
		||||
	suite.testEmojis = testrig.NewTestEmojis()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *AdminStandardTestSuite) SetupTest() {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,27 +19,19 @@
 | 
			
		||||
package admin
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/api"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/config"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/db"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/oauth"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EmojisGETHandler swagger:operation GET /api/v1/admin/custom_emojis emojisGet
 | 
			
		||||
// EmojiGETHandler swagger:operation GET /api/v1/admin/custom_emojis/{id} emojiGet
 | 
			
		||||
//
 | 
			
		||||
// View local and remote emojis available to / known by this instance.
 | 
			
		||||
//
 | 
			
		||||
// The next and previous queries can be parsed from the returned Link header.
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
// `<http://localhost:8080/api/v1/admin/custom_emojis?limit=30&max_shortcode_domain=yell@fossbros-anonymous.io&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=30&min_shortcode_domain=rainbow@&filter=domain:all>; rel="prev"`
 | 
			
		||||
// Get the admin view of a single emoji.
 | 
			
		||||
//
 | 
			
		||||
//	---
 | 
			
		||||
//	tags:
 | 
			
		||||
@@ -50,66 +42,17 @@ import (
 | 
			
		||||
//
 | 
			
		||||
//	parameters:
 | 
			
		||||
//	-
 | 
			
		||||
//		name: filter
 | 
			
		||||
//		name: id
 | 
			
		||||
//		type: string
 | 
			
		||||
//		description: |-
 | 
			
		||||
//			Comma-separated list of filters to apply to results. Recognized filters are:
 | 
			
		||||
//
 | 
			
		||||
//			`domain:[domain]` -- show emojis from the given domain, eg `?filter=domain:example.org` will show emojis from `example.org` only.
 | 
			
		||||
//			Instead of giving a specific domain, you can also give either one of the key words `local` or `all` to show either local emojis only (`domain:local`) or show all emojis from all domains (`domain:all`).
 | 
			
		||||
//			Note: `domain:*` is equivalent to `domain:all` (including local).
 | 
			
		||||
//			If no domain filter is provided, `domain:all` will be assumed.
 | 
			
		||||
//
 | 
			
		||||
//			`disabled` -- include emojis that have been disabled.
 | 
			
		||||
//
 | 
			
		||||
//			`enabled` -- include emojis that are enabled.
 | 
			
		||||
//
 | 
			
		||||
//			`shortcode:[shortcode]` -- show only emojis with the given shortcode, eg `?filter=shortcode:blob_cat_uwu` will show only emojis with the shortcode `blob_cat_uwu` (case sensitive).
 | 
			
		||||
//
 | 
			
		||||
//			If neither `disabled` or `enabled` are provided, both disabled and enabled emojis will be shown.
 | 
			
		||||
//
 | 
			
		||||
//			If no filter query string is provided, the default `domain:all` will be used, which will show all emojis from all domains.
 | 
			
		||||
//		in: query
 | 
			
		||||
//		required: false
 | 
			
		||||
//		default: "domain:all"
 | 
			
		||||
//	-
 | 
			
		||||
//		name: limit
 | 
			
		||||
//		type: integer
 | 
			
		||||
//		description: Number of emojis to return. Less than 1, or not set, means unlimited (all emojis).
 | 
			
		||||
//		default: 50
 | 
			
		||||
//		in: query
 | 
			
		||||
//	-
 | 
			
		||||
//		name: max_shortcode_domain
 | 
			
		||||
//		type: string
 | 
			
		||||
//		description: >-
 | 
			
		||||
//			Return only emojis with `[shortcode]@[domain]` *LOWER* (alphabetically) than given `[shortcode]@[domain]`.
 | 
			
		||||
//			For example, if `max_shortcode_domain=beep@example.org`, then returned values might include emojis with
 | 
			
		||||
//			`[shortcode]@[domain]`s like `car@example.org`, `debian@aaa.com`, `test@` (local emoji), etc.
 | 
			
		||||
//
 | 
			
		||||
//			Emoji with the given `[shortcode]@[domain]` will not be included in the result set.
 | 
			
		||||
//		in: query
 | 
			
		||||
//	-
 | 
			
		||||
//		name: min_shortcode_domain
 | 
			
		||||
//		type: string
 | 
			
		||||
//		description: >-
 | 
			
		||||
//			Return only emojis with `[shortcode]@[domain]` *HIGHER* (alphabetically) than given `[shortcode]@[domain]`.
 | 
			
		||||
//			For example, if `max_shortcode_domain=beep@example.org`, then returned values might include emojis with
 | 
			
		||||
//			`[shortcode]@[domain]`s like `arse@test.com`, `0101_binary@hackers.net`, `bee@` (local emoji), etc.
 | 
			
		||||
//
 | 
			
		||||
//			Emoji with the given `[shortcode]@[domain]` will not be included in the result set.
 | 
			
		||||
//		in: query
 | 
			
		||||
//		description: The id of the emoji.
 | 
			
		||||
//		in: path
 | 
			
		||||
//		required: true
 | 
			
		||||
//
 | 
			
		||||
//	responses:
 | 
			
		||||
//		'200':
 | 
			
		||||
//			headers:
 | 
			
		||||
//				Link:
 | 
			
		||||
//					type: string
 | 
			
		||||
//					description: Links to the next and previous queries.
 | 
			
		||||
//			description: An array of emojis, arranged alphabetically by shortcode and domain.
 | 
			
		||||
//			description: A single emoji.
 | 
			
		||||
//			schema:
 | 
			
		||||
//				type: array
 | 
			
		||||
//				items:
 | 
			
		||||
//					"$ref": "#/definitions/adminEmoji"
 | 
			
		||||
//				"$ref": "#/definitions/adminEmoji"
 | 
			
		||||
//		'400':
 | 
			
		||||
//			description: bad request
 | 
			
		||||
//		'401':
 | 
			
		||||
@@ -122,7 +65,7 @@ import (
 | 
			
		||||
//			description: not acceptable
 | 
			
		||||
//		'500':
 | 
			
		||||
//			description: internal server error
 | 
			
		||||
func (m *Module) EmojisGETHandler(c *gin.Context) {
 | 
			
		||||
func (m *Module) EmojiGETHandler(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)
 | 
			
		||||
@@ -140,72 +83,18 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	maxShortcodeDomain := c.Query(MaxShortcodeDomainKey)
 | 
			
		||||
	minShortcodeDomain := c.Query(MinShortcodeDomainKey)
 | 
			
		||||
 | 
			
		||||
	limit := 50
 | 
			
		||||
	limitString := c.Query(LimitKey)
 | 
			
		||||
	if limitString != "" {
 | 
			
		||||
		i, err := strconv.ParseInt(limitString, 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
 | 
			
		||||
			api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		limit = int(i)
 | 
			
		||||
	}
 | 
			
		||||
	if limit < 0 {
 | 
			
		||||
		limit = 0
 | 
			
		||||
	emojiID := c.Param(IDKey)
 | 
			
		||||
	if emojiID == "" {
 | 
			
		||||
		err := errors.New("no emoji id specified")
 | 
			
		||||
		api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var domain string
 | 
			
		||||
	var includeDisabled bool
 | 
			
		||||
	var includeEnabled bool
 | 
			
		||||
	var shortcode string
 | 
			
		||||
	if filterParam := c.Query(FilterQueryKey); filterParam != "" {
 | 
			
		||||
		filters := strings.Split(filterParam, ",")
 | 
			
		||||
		for _, filter := range filters {
 | 
			
		||||
			lower := strings.ToLower(filter)
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.HasPrefix(lower, "domain:"):
 | 
			
		||||
				domain = strings.TrimPrefix(lower, "domain:")
 | 
			
		||||
			case lower == "disabled":
 | 
			
		||||
				includeDisabled = true
 | 
			
		||||
			case lower == "enabled":
 | 
			
		||||
				includeEnabled = true
 | 
			
		||||
			case strings.HasPrefix(lower, "shortcode:"):
 | 
			
		||||
				shortcode = strings.Trim(filter[10:], ":") // remove any errant ":"
 | 
			
		||||
			default:
 | 
			
		||||
				err := fmt.Errorf("filter %s not recognized; accepted values are 'domain:[domain]', 'disabled', 'enabled', 'shortcode:[shortcode]'", filter)
 | 
			
		||||
				api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if domain == "" {
 | 
			
		||||
		// default is to show all domains
 | 
			
		||||
		domain = db.EmojiAllDomains
 | 
			
		||||
	} else if domain == "local" || domain == config.GetHost() || domain == config.GetAccountDomain() {
 | 
			
		||||
		// pass empty string for local domain
 | 
			
		||||
		domain = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// normalize filters
 | 
			
		||||
	if !includeDisabled && !includeEnabled {
 | 
			
		||||
		// include both if neither specified
 | 
			
		||||
		includeDisabled = true
 | 
			
		||||
		includeEnabled = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, errWithCode := m.processor.AdminEmojisGet(c.Request.Context(), authed, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
 | 
			
		||||
	emoji, errWithCode := m.processor.AdminEmojiGet(c.Request.Context(), authed, emojiID)
 | 
			
		||||
	if errWithCode != nil {
 | 
			
		||||
		api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.LinkHeader != "" {
 | 
			
		||||
		c.Header("Link", resp.LinkHeader)
 | 
			
		||||
	}
 | 
			
		||||
	c.JSON(http.StatusOK, resp.Items)
 | 
			
		||||
	c.JSON(http.StatusOK, emoji)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@
 | 
			
		||||
package admin_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
@@ -27,86 +26,62 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
 | 
			
		||||
	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EmojiGetTestSuite struct {
 | 
			
		||||
	AdminStandardTestSuite
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *EmojiGetTestSuite) TestEmojiGet() {
 | 
			
		||||
func (suite *EmojiGetTestSuite) TestEmojiGet1() {
 | 
			
		||||
	recorder := httptest.NewRecorder()
 | 
			
		||||
	testEmoji := suite.testEmojis["rainbow"]
 | 
			
		||||
 | 
			
		||||
	path := admin.EmojiPath + "?filter=domain:all&limit=1"
 | 
			
		||||
	path := admin.EmojiPathWithID
 | 
			
		||||
	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")
 | 
			
		||||
	ctx.AddParam(admin.IDKey, testEmoji.ID)
 | 
			
		||||
 | 
			
		||||
	suite.adminModule.EmojisGETHandler(ctx)
 | 
			
		||||
	suite.adminModule.EmojiGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusOK, recorder.Code)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(recorder.Body)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.NotNil(b)
 | 
			
		||||
 | 
			
		||||
	apiEmojis := []*apimodel.AdminEmoji{}
 | 
			
		||||
	if err := json.Unmarshal(b, &apiEmojis); err != nil {
 | 
			
		||||
		suite.FailNow(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.Len(apiEmojis, 1)
 | 
			
		||||
	suite.Equal("rainbow", apiEmojis[0].Shortcode)
 | 
			
		||||
	suite.Equal("", apiEmojis[0].Domain)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(`<http://localhost:8080/api/v1/admin/custom_emojis?limit=1&max_shortcode_domain=rainbow@&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=1&min_shortcode_domain=rainbow@&filter=domain:all>; rel="prev"`, recorder.Header().Get("link"))
 | 
			
		||||
	suite.Equal(`{"shortcode":"rainbow","url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png","static_url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/emoji/static/01F8MH9H8E4VG3KDYJR9EGPXCQ.png","visible_in_picker":true,"id":"01F8MH9H8E4VG3KDYJR9EGPXCQ","disabled":false,"updated_at":"2021-09-20T10:40:37.000Z","total_file_size":47115,"content_type":"image/png","uri":"http://localhost:8080/emoji/01F8MH9H8E4VG3KDYJR9EGPXCQ"}`, string(b))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *EmojiGetTestSuite) TestEmojiGet2() {
 | 
			
		||||
	recorder := httptest.NewRecorder()
 | 
			
		||||
	testEmoji := suite.testEmojis["yell"]
 | 
			
		||||
 | 
			
		||||
	path := admin.EmojiPath + "?filter=domain:all&limit=1&max_shortcode_domain=rainbow@"
 | 
			
		||||
	path := admin.EmojiPathWithID
 | 
			
		||||
	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")
 | 
			
		||||
	ctx.AddParam(admin.IDKey, testEmoji.ID)
 | 
			
		||||
 | 
			
		||||
	suite.adminModule.EmojisGETHandler(ctx)
 | 
			
		||||
	suite.adminModule.EmojiGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusOK, recorder.Code)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(recorder.Body)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.NotNil(b)
 | 
			
		||||
 | 
			
		||||
	apiEmojis := []*apimodel.AdminEmoji{}
 | 
			
		||||
	if err := json.Unmarshal(b, &apiEmojis); err != nil {
 | 
			
		||||
		suite.FailNow(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.Len(apiEmojis, 1)
 | 
			
		||||
	suite.Equal("yell", apiEmojis[0].Shortcode)
 | 
			
		||||
	suite.Equal("fossbros-anonymous.io", apiEmojis[0].Domain)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(`<http://localhost:8080/api/v1/admin/custom_emojis?limit=1&max_shortcode_domain=yell@fossbros-anonymous.io&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=1&min_shortcode_domain=yell@fossbros-anonymous.io&filter=domain:all>; rel="prev"`, recorder.Header().Get("link"))
 | 
			
		||||
	suite.Equal(`{"shortcode":"yell","url":"http://localhost:8080/fileserver/01GD5KR15NHTY8FZ01CD4D08XP/emoji/original/01GD5KP5CQEE1R3X43Y1EHS2CW.png","static_url":"http://localhost:8080/fileserver/01GD5KR15NHTY8FZ01CD4D08XP/emoji/static/01GD5KP5CQEE1R3X43Y1EHS2CW.png","visible_in_picker":false,"id":"01GD5KP5CQEE1R3X43Y1EHS2CW","disabled":false,"domain":"fossbros-anonymous.io","updated_at":"2020-03-18T12:12:00.000Z","total_file_size":21697,"content_type":"image/png","uri":"http://fossbros-anonymous.io/emoji/01GD5KP5CQEE1R3X43Y1EHS2CW"}`, string(b))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *EmojiGetTestSuite) TestEmojiGet3() {
 | 
			
		||||
func (suite *EmojiGetTestSuite) TestEmojiGetNotFound() {
 | 
			
		||||
	recorder := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	path := admin.EmojiPath + "?filter=domain:all&limit=1&min_shortcode_domain=yell@fossbros-anonymous.io"
 | 
			
		||||
	path := admin.EmojiPathWithID
 | 
			
		||||
	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")
 | 
			
		||||
	ctx.AddParam(admin.IDKey, "01GF8VRXX1R00X7XH8973Z29R1")
 | 
			
		||||
 | 
			
		||||
	suite.adminModule.EmojisGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusOK, recorder.Code)
 | 
			
		||||
	suite.adminModule.EmojiGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusNotFound, recorder.Code)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(recorder.Body)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.NotNil(b)
 | 
			
		||||
 | 
			
		||||
	apiEmojis := []*apimodel.AdminEmoji{}
 | 
			
		||||
	if err := json.Unmarshal(b, &apiEmojis); err != nil {
 | 
			
		||||
		suite.FailNow(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.Len(apiEmojis, 1)
 | 
			
		||||
	suite.Equal("rainbow", apiEmojis[0].Shortcode)
 | 
			
		||||
	suite.Equal("", apiEmojis[0].Domain)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(`<http://localhost:8080/api/v1/admin/custom_emojis?limit=1&max_shortcode_domain=rainbow@&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=1&min_shortcode_domain=rainbow@&filter=domain:all>; rel="prev"`, recorder.Header().Get("link"))
 | 
			
		||||
	suite.Equal(`{"error":"Not Found"}`, string(b))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEmojiGetTestSuite(t *testing.T) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										211
									
								
								internal/api/client/admin/emojisget.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								internal/api/client/admin/emojisget.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
/*
 | 
			
		||||
   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"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/api"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/config"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/db"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/oauth"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EmojisGETHandler swagger:operation GET /api/v1/admin/custom_emojis emojisGet
 | 
			
		||||
//
 | 
			
		||||
// View local and remote emojis available to / known by this instance.
 | 
			
		||||
//
 | 
			
		||||
// The next and previous queries can be parsed from the returned Link header.
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
// `<http://localhost:8080/api/v1/admin/custom_emojis?limit=30&max_shortcode_domain=yell@fossbros-anonymous.io&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=30&min_shortcode_domain=rainbow@&filter=domain:all>; rel="prev"`
 | 
			
		||||
//
 | 
			
		||||
//	---
 | 
			
		||||
//	tags:
 | 
			
		||||
//	- admin
 | 
			
		||||
//
 | 
			
		||||
//	produces:
 | 
			
		||||
//	- application/json
 | 
			
		||||
//
 | 
			
		||||
//	parameters:
 | 
			
		||||
//	-
 | 
			
		||||
//		name: filter
 | 
			
		||||
//		type: string
 | 
			
		||||
//		description: |-
 | 
			
		||||
//			Comma-separated list of filters to apply to results. Recognized filters are:
 | 
			
		||||
//
 | 
			
		||||
//			`domain:[domain]` -- show emojis from the given domain, eg `?filter=domain:example.org` will show emojis from `example.org` only.
 | 
			
		||||
//			Instead of giving a specific domain, you can also give either one of the key words `local` or `all` to show either local emojis only (`domain:local`) or show all emojis from all domains (`domain:all`).
 | 
			
		||||
//			Note: `domain:*` is equivalent to `domain:all` (including local).
 | 
			
		||||
//			If no domain filter is provided, `domain:all` will be assumed.
 | 
			
		||||
//
 | 
			
		||||
//			`disabled` -- include emojis that have been disabled.
 | 
			
		||||
//
 | 
			
		||||
//			`enabled` -- include emojis that are enabled.
 | 
			
		||||
//
 | 
			
		||||
//			`shortcode:[shortcode]` -- show only emojis with the given shortcode, eg `?filter=shortcode:blob_cat_uwu` will show only emojis with the shortcode `blob_cat_uwu` (case sensitive).
 | 
			
		||||
//
 | 
			
		||||
//			If neither `disabled` or `enabled` are provided, both disabled and enabled emojis will be shown.
 | 
			
		||||
//
 | 
			
		||||
//			If no filter query string is provided, the default `domain:all` will be used, which will show all emojis from all domains.
 | 
			
		||||
//		in: query
 | 
			
		||||
//		required: false
 | 
			
		||||
//		default: "domain:all"
 | 
			
		||||
//	-
 | 
			
		||||
//		name: limit
 | 
			
		||||
//		type: integer
 | 
			
		||||
//		description: Number of emojis to return. Less than 1, or not set, means unlimited (all emojis).
 | 
			
		||||
//		default: 50
 | 
			
		||||
//		in: query
 | 
			
		||||
//	-
 | 
			
		||||
//		name: max_shortcode_domain
 | 
			
		||||
//		type: string
 | 
			
		||||
//		description: >-
 | 
			
		||||
//			Return only emojis with `[shortcode]@[domain]` *LOWER* (alphabetically) than given `[shortcode]@[domain]`.
 | 
			
		||||
//			For example, if `max_shortcode_domain=beep@example.org`, then returned values might include emojis with
 | 
			
		||||
//			`[shortcode]@[domain]`s like `car@example.org`, `debian@aaa.com`, `test@` (local emoji), etc.
 | 
			
		||||
//
 | 
			
		||||
//			Emoji with the given `[shortcode]@[domain]` will not be included in the result set.
 | 
			
		||||
//		in: query
 | 
			
		||||
//	-
 | 
			
		||||
//		name: min_shortcode_domain
 | 
			
		||||
//		type: string
 | 
			
		||||
//		description: >-
 | 
			
		||||
//			Return only emojis with `[shortcode]@[domain]` *HIGHER* (alphabetically) than given `[shortcode]@[domain]`.
 | 
			
		||||
//			For example, if `max_shortcode_domain=beep@example.org`, then returned values might include emojis with
 | 
			
		||||
//			`[shortcode]@[domain]`s like `arse@test.com`, `0101_binary@hackers.net`, `bee@` (local emoji), etc.
 | 
			
		||||
//
 | 
			
		||||
//			Emoji with the given `[shortcode]@[domain]` will not be included in the result set.
 | 
			
		||||
//		in: query
 | 
			
		||||
//
 | 
			
		||||
//	responses:
 | 
			
		||||
//		'200':
 | 
			
		||||
//			headers:
 | 
			
		||||
//				Link:
 | 
			
		||||
//					type: string
 | 
			
		||||
//					description: Links to the next and previous queries.
 | 
			
		||||
//			description: An array of emojis, arranged alphabetically by shortcode and domain.
 | 
			
		||||
//			schema:
 | 
			
		||||
//				type: array
 | 
			
		||||
//				items:
 | 
			
		||||
//					"$ref": "#/definitions/adminEmoji"
 | 
			
		||||
//		'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) EmojisGETHandler(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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	maxShortcodeDomain := c.Query(MaxShortcodeDomainKey)
 | 
			
		||||
	minShortcodeDomain := c.Query(MinShortcodeDomainKey)
 | 
			
		||||
 | 
			
		||||
	limit := 50
 | 
			
		||||
	limitString := c.Query(LimitKey)
 | 
			
		||||
	if limitString != "" {
 | 
			
		||||
		i, err := strconv.ParseInt(limitString, 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
 | 
			
		||||
			api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		limit = int(i)
 | 
			
		||||
	}
 | 
			
		||||
	if limit < 0 {
 | 
			
		||||
		limit = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var domain string
 | 
			
		||||
	var includeDisabled bool
 | 
			
		||||
	var includeEnabled bool
 | 
			
		||||
	var shortcode string
 | 
			
		||||
	if filterParam := c.Query(FilterQueryKey); filterParam != "" {
 | 
			
		||||
		filters := strings.Split(filterParam, ",")
 | 
			
		||||
		for _, filter := range filters {
 | 
			
		||||
			lower := strings.ToLower(filter)
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.HasPrefix(lower, "domain:"):
 | 
			
		||||
				domain = strings.TrimPrefix(lower, "domain:")
 | 
			
		||||
			case lower == "disabled":
 | 
			
		||||
				includeDisabled = true
 | 
			
		||||
			case lower == "enabled":
 | 
			
		||||
				includeEnabled = true
 | 
			
		||||
			case strings.HasPrefix(lower, "shortcode:"):
 | 
			
		||||
				shortcode = strings.Trim(filter[10:], ":") // remove any errant ":"
 | 
			
		||||
			default:
 | 
			
		||||
				err := fmt.Errorf("filter %s not recognized; accepted values are 'domain:[domain]', 'disabled', 'enabled', 'shortcode:[shortcode]'", filter)
 | 
			
		||||
				api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if domain == "" {
 | 
			
		||||
		// default is to show all domains
 | 
			
		||||
		domain = db.EmojiAllDomains
 | 
			
		||||
	} else if domain == "local" || domain == config.GetHost() || domain == config.GetAccountDomain() {
 | 
			
		||||
		// pass empty string for local domain
 | 
			
		||||
		domain = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// normalize filters
 | 
			
		||||
	if !includeDisabled && !includeEnabled {
 | 
			
		||||
		// include both if neither specified
 | 
			
		||||
		includeDisabled = true
 | 
			
		||||
		includeEnabled = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, errWithCode := m.processor.AdminEmojisGet(c.Request.Context(), authed, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
 | 
			
		||||
	if errWithCode != nil {
 | 
			
		||||
		api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.LinkHeader != "" {
 | 
			
		||||
		c.Header("Link", resp.LinkHeader)
 | 
			
		||||
	}
 | 
			
		||||
	c.JSON(http.StatusOK, resp.Items)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								internal/api/client/admin/emojisget_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								internal/api/client/admin/emojisget_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
/*
 | 
			
		||||
   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 (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
	"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
 | 
			
		||||
	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EmojisGetTestSuite struct {
 | 
			
		||||
	AdminStandardTestSuite
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *EmojisGetTestSuite) TestEmojiGet() {
 | 
			
		||||
	recorder := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	path := admin.EmojiPath + "?filter=domain:all&limit=1"
 | 
			
		||||
	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")
 | 
			
		||||
 | 
			
		||||
	suite.adminModule.EmojisGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusOK, recorder.Code)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(recorder.Body)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.NotNil(b)
 | 
			
		||||
 | 
			
		||||
	apiEmojis := []*apimodel.AdminEmoji{}
 | 
			
		||||
	if err := json.Unmarshal(b, &apiEmojis); err != nil {
 | 
			
		||||
		suite.FailNow(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.Len(apiEmojis, 1)
 | 
			
		||||
	suite.Equal("rainbow", apiEmojis[0].Shortcode)
 | 
			
		||||
	suite.Equal("", apiEmojis[0].Domain)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(`<http://localhost:8080/api/v1/admin/custom_emojis?limit=1&max_shortcode_domain=rainbow@&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=1&min_shortcode_domain=rainbow@&filter=domain:all>; rel="prev"`, recorder.Header().Get("link"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *EmojisGetTestSuite) TestEmojiGet2() {
 | 
			
		||||
	recorder := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	path := admin.EmojiPath + "?filter=domain:all&limit=1&max_shortcode_domain=rainbow@"
 | 
			
		||||
	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")
 | 
			
		||||
 | 
			
		||||
	suite.adminModule.EmojisGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusOK, recorder.Code)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(recorder.Body)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.NotNil(b)
 | 
			
		||||
 | 
			
		||||
	apiEmojis := []*apimodel.AdminEmoji{}
 | 
			
		||||
	if err := json.Unmarshal(b, &apiEmojis); err != nil {
 | 
			
		||||
		suite.FailNow(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.Len(apiEmojis, 1)
 | 
			
		||||
	suite.Equal("yell", apiEmojis[0].Shortcode)
 | 
			
		||||
	suite.Equal("fossbros-anonymous.io", apiEmojis[0].Domain)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(`<http://localhost:8080/api/v1/admin/custom_emojis?limit=1&max_shortcode_domain=yell@fossbros-anonymous.io&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=1&min_shortcode_domain=yell@fossbros-anonymous.io&filter=domain:all>; rel="prev"`, recorder.Header().Get("link"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *EmojisGetTestSuite) TestEmojiGet3() {
 | 
			
		||||
	recorder := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	path := admin.EmojiPath + "?filter=domain:all&limit=1&min_shortcode_domain=yell@fossbros-anonymous.io"
 | 
			
		||||
	ctx := suite.newContext(recorder, http.MethodGet, nil, path, "application/json")
 | 
			
		||||
 | 
			
		||||
	suite.adminModule.EmojisGETHandler(ctx)
 | 
			
		||||
	suite.Equal(http.StatusOK, recorder.Code)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(recorder.Body)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.NotNil(b)
 | 
			
		||||
 | 
			
		||||
	apiEmojis := []*apimodel.AdminEmoji{}
 | 
			
		||||
	if err := json.Unmarshal(b, &apiEmojis); err != nil {
 | 
			
		||||
		suite.FailNow(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.Len(apiEmojis, 1)
 | 
			
		||||
	suite.Equal("rainbow", apiEmojis[0].Shortcode)
 | 
			
		||||
	suite.Equal("", apiEmojis[0].Domain)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(`<http://localhost:8080/api/v1/admin/custom_emojis?limit=1&max_shortcode_domain=rainbow@&filter=domain:all>; rel="next", <http://localhost:8080/api/v1/admin/custom_emojis?limit=1&min_shortcode_domain=rainbow@&filter=domain:all>; rel="prev"`, recorder.Header().Get("link"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEmojisGetTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, &EmojisGetTestSuite{})
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user