mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 03:52:24 -05:00 
			
		
		
		
	emoji code passing muster
This commit is contained in:
		
					parent
					
						
							
								c4a533db72
							
						
					
				
			
			
				commit
				
					
						6bf39d0fc1
					
				
			
		
					 9 changed files with 104 additions and 39 deletions
				
			
		|  | @ -73,6 +73,8 @@ import ( | ||||||
| //      description: forbidden | //      description: forbidden | ||||||
| //   '400': | //   '400': | ||||||
| //      description: bad request | //      description: bad request | ||||||
|  | //   '409': | ||||||
|  | //      description: conflict -- domain/shortcode combo for emoji already exists | ||||||
| func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) { | func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) { | ||||||
| 	l := logrus.WithFields(logrus.Fields{ | 	l := logrus.WithFields(logrus.Fields{ | ||||||
| 		"func":        "emojiCreatePOSTHandler", | 		"func":        "emojiCreatePOSTHandler", | ||||||
|  | @ -116,10 +118,10 @@ func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	apiEmoji, err := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form) | 	apiEmoji, errWithCode := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form) | ||||||
| 	if err != nil { | 	if errWithCode != nil { | ||||||
| 		l.Debugf("error creating emoji: %s", err) | 		l.Debugf("error creating emoji: %s", errWithCode.Error()) | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | 		c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ func (suite *EmojiCreateTestSuite) TestEmojiCreate() { | ||||||
| 	requestBody, w, err := testrig.CreateMultipartFormData( | 	requestBody, w, err := testrig.CreateMultipartFormData( | ||||||
| 		"image", "../../../../testrig/media/rainbow-original.png", | 		"image", "../../../../testrig/media/rainbow-original.png", | ||||||
| 		map[string]string{ | 		map[string]string{ | ||||||
| 			"shortcode": "rainbow", | 			"shortcode": "new_emoji", | ||||||
| 		}) | 		}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		panic(err) | ||||||
|  | @ -55,24 +55,24 @@ func (suite *EmojiCreateTestSuite) TestEmojiCreate() { | ||||||
| 	suite.NoError(err) | 	suite.NoError(err) | ||||||
| 
 | 
 | ||||||
| 	// appropriate fields should be set | 	// appropriate fields should be set | ||||||
| 	suite.Equal("rainbow", apiEmoji.Shortcode) | 	suite.Equal("new_emoji", apiEmoji.Shortcode) | ||||||
| 	suite.NotEmpty(apiEmoji.URL) | 	suite.NotEmpty(apiEmoji.URL) | ||||||
| 	suite.NotEmpty(apiEmoji.StaticURL) | 	suite.NotEmpty(apiEmoji.StaticURL) | ||||||
| 	suite.True(apiEmoji.VisibleInPicker) | 	suite.True(apiEmoji.VisibleInPicker) | ||||||
| 
 | 
 | ||||||
| 	// emoji should be in the db | 	// emoji should be in the db | ||||||
| 	dbEmoji := >smodel.Emoji{} | 	dbEmoji := >smodel.Emoji{} | ||||||
| 	err = suite.db.GetWhere(context.Background(), []db.Where{{Key: "shortcode", Value: "rainbow"}}, dbEmoji) | 	err = suite.db.GetWhere(context.Background(), []db.Where{{Key: "shortcode", Value: "new_emoji"}}, dbEmoji) | ||||||
| 	suite.NoError(err) | 	suite.NoError(err) | ||||||
| 
 | 
 | ||||||
| 	// check fields on the emoji | 	// check fields on the emoji | ||||||
| 	suite.NotEmpty(dbEmoji.ID) | 	suite.NotEmpty(dbEmoji.ID) | ||||||
| 	suite.Equal("rainbow", dbEmoji.Shortcode) | 	suite.Equal("new_emoji", dbEmoji.Shortcode) | ||||||
| 	suite.Empty(dbEmoji.Domain) | 	suite.Empty(dbEmoji.Domain) | ||||||
| 	suite.Empty(dbEmoji.ImageRemoteURL) | 	suite.Empty(dbEmoji.ImageRemoteURL) | ||||||
| 	suite.Empty(dbEmoji.ImageStaticRemoteURL) | 	suite.Empty(dbEmoji.ImageStaticRemoteURL) | ||||||
| 	suite.Equal(apiEmoji.URL, dbEmoji.ImageURL) | 	suite.Equal(apiEmoji.URL, dbEmoji.ImageURL) | ||||||
| 	suite.Equal(apiEmoji.StaticURL, dbEmoji.ImageURL) | 	suite.Equal(apiEmoji.StaticURL, dbEmoji.ImageStaticURL) | ||||||
| 	suite.NotEmpty(dbEmoji.ImagePath) | 	suite.NotEmpty(dbEmoji.ImagePath) | ||||||
| 	suite.NotEmpty(dbEmoji.ImageStaticPath) | 	suite.NotEmpty(dbEmoji.ImageStaticPath) | ||||||
| 	suite.Equal("image/png", dbEmoji.ImageContentType) | 	suite.Equal("image/png", dbEmoji.ImageContentType) | ||||||
|  | @ -82,7 +82,45 @@ func (suite *EmojiCreateTestSuite) TestEmojiCreate() { | ||||||
| 	suite.False(dbEmoji.Disabled) | 	suite.False(dbEmoji.Disabled) | ||||||
| 	suite.NotEmpty(dbEmoji.URI) | 	suite.NotEmpty(dbEmoji.URI) | ||||||
| 	suite.True(dbEmoji.VisibleInPicker) | 	suite.True(dbEmoji.VisibleInPicker) | ||||||
| 	suite.Empty(dbEmoji.CategoryID)aaaaaaaaa | 	suite.Empty(dbEmoji.CategoryID) | ||||||
|  | 
 | ||||||
|  | 	// emoji should be in storage | ||||||
|  | 	emojiBytes, err := suite.storage.Get(dbEmoji.ImagePath) | ||||||
|  | 	suite.NoError(err) | ||||||
|  | 	suite.Len(emojiBytes, dbEmoji.ImageFileSize) | ||||||
|  | 	emojiStaticBytes, err := suite.storage.Get(dbEmoji.ImageStaticPath) | ||||||
|  | 	suite.NoError(err) | ||||||
|  | 	suite.Len(emojiStaticBytes, dbEmoji.ImageStaticFileSize) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (suite *EmojiCreateTestSuite) TestEmojiCreateAlreadyExists() { | ||||||
|  | 	// set up the request -- use a shortcode that already exists for an emoji in the database | ||||||
|  | 	requestBody, w, err := testrig.CreateMultipartFormData( | ||||||
|  | 		"image", "../../../../testrig/media/rainbow-original.png", | ||||||
|  | 		map[string]string{ | ||||||
|  | 			"shortcode": "rainbow", | ||||||
|  | 		}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	bodyBytes := requestBody.Bytes() | ||||||
|  | 	recorder := httptest.NewRecorder() | ||||||
|  | 	ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, admin.EmojiPath, w.FormDataContentType()) | ||||||
|  | 
 | ||||||
|  | 	// call the handler | ||||||
|  | 	suite.adminModule.EmojiCreatePOSTHandler(ctx) | ||||||
|  | 
 | ||||||
|  | 	suite.Equal(http.StatusConflict, recorder.Code) | ||||||
|  | 
 | ||||||
|  | 	result := recorder.Result() | ||||||
|  | 	defer result.Body.Close() | ||||||
|  | 
 | ||||||
|  | 	// check the response | ||||||
|  | 	b, err := ioutil.ReadAll(result.Body) | ||||||
|  | 	suite.NoError(err) | ||||||
|  | 	suite.NotEmpty(b) | ||||||
|  | 
 | ||||||
|  | 	suite.Equal(`{"error":"conflict: emoji with shortcode rainbow already exists"}`, string(b)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestEmojiCreateTestSuite(t *testing.T) { | func TestEmojiCreateTestSuite(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -122,3 +122,16 @@ func NewErrorInternalError(original error, helpText ...string) WithCode { | ||||||
| 		code:     http.StatusInternalServerError, | 		code:     http.StatusInternalServerError, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // NewErrorConflict returns an ErrorWithCode 409 with the given original error and optional help text. | ||||||
|  | func NewErrorConflict(original error, helpText ...string) WithCode { | ||||||
|  | 	safe := "conflict" | ||||||
|  | 	if helpText != nil { | ||||||
|  | 		safe = safe + ": " + strings.Join(helpText, ": ") | ||||||
|  | 	} | ||||||
|  | 	return withCode{ | ||||||
|  | 		original: original, | ||||||
|  | 		safe:     errors.New(safe), | ||||||
|  | 		code:     http.StatusConflict, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -72,6 +72,9 @@ type ProcessingEmoji struct { | ||||||
| 	storage  *kv.KVStore | 	storage  *kv.KVStore | ||||||
| 
 | 
 | ||||||
| 	err error // error created during processing, if any | 	err error // error created during processing, if any | ||||||
|  | 
 | ||||||
|  | 	// track whether this emoji has already been put in the databse | ||||||
|  | 	insertedInDB bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // EmojiID returns the ID of the underlying emoji without blocking processing. | // EmojiID returns the ID of the underlying emoji without blocking processing. | ||||||
|  | @ -94,6 +97,16 @@ func (p *ProcessingEmoji) LoadEmoji(ctx context.Context) (*gtsmodel.Emoji, error | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// store the result in the database before returning it | ||||||
|  | 	p.mu.Lock() | ||||||
|  | 	defer p.mu.Unlock() | ||||||
|  | 	if !p.insertedInDB { | ||||||
|  | 		if err := p.database.Put(ctx, p.emoji); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		p.insertedInDB = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return p.emoji, nil | 	return p.emoji, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -127,13 +140,6 @@ func (p *ProcessingEmoji) loadStatic(ctx context.Context) (*ImageMeta, error) { | ||||||
| 		// set appropriate fields on the emoji based on the static version we derived | 		// set appropriate fields on the emoji based on the static version we derived | ||||||
| 		p.emoji.ImageStaticFileSize = len(static.image) | 		p.emoji.ImageStaticFileSize = len(static.image) | ||||||
| 
 | 
 | ||||||
| 		// update the emoji in the db |  | ||||||
| 		if err := putOrUpdate(ctx, p.database, p.emoji); err != nil { |  | ||||||
| 			p.err = err |  | ||||||
| 			p.staticState = errored |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// set the static on the processing emoji | 		// set the static on the processing emoji | ||||||
| 		p.static = static | 		p.static = static | ||||||
| 
 | 
 | ||||||
|  | @ -197,7 +203,7 @@ func (p *ProcessingEmoji) loadFullSize(ctx context.Context) (*ImageMeta, error) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // fetchRawData calls the data function attached to p if it hasn't been called yet, | // fetchRawData calls the data function attached to p if it hasn't been called yet, | ||||||
| // and updates the underlying attachment fields as necessary. | // and updates the underlying emoji fields as necessary. | ||||||
| // It should only be called from within a function that already has a lock on p! | // It should only be called from within a function that already has a lock on p! | ||||||
| func (p *ProcessingEmoji) fetchRawData(ctx context.Context) error { | func (p *ProcessingEmoji) fetchRawData(ctx context.Context) error { | ||||||
| 	// check if we've already done this and bail early if we have | 	// check if we've already done this and bail early if we have | ||||||
|  |  | ||||||
|  | @ -70,6 +70,9 @@ type ProcessingMedia struct { | ||||||
| 	storage  *kv.KVStore | 	storage  *kv.KVStore | ||||||
| 
 | 
 | ||||||
| 	err error // error created during processing, if any | 	err error // error created during processing, if any | ||||||
|  | 
 | ||||||
|  | 	// track whether this media has already been put in the databse | ||||||
|  | 	insertedInDB bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AttachmentID returns the ID of the underlying media attachment without blocking processing. | // AttachmentID returns the ID of the underlying media attachment without blocking processing. | ||||||
|  | @ -92,6 +95,16 @@ func (p *ProcessingMedia) LoadAttachment(ctx context.Context) (*gtsmodel.MediaAt | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// store the result in the database before returning it | ||||||
|  | 	p.mu.Lock() | ||||||
|  | 	defer p.mu.Unlock() | ||||||
|  | 	if !p.insertedInDB { | ||||||
|  | 		if err := p.database.Put(ctx, p.attachment); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		p.insertedInDB = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return p.attachment, nil | 	return p.attachment, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -143,12 +156,6 @@ func (p *ProcessingMedia) loadThumb(ctx context.Context) (*ImageMeta, error) { | ||||||
| 		} | 		} | ||||||
| 		p.attachment.Thumbnail.FileSize = len(thumb.image) | 		p.attachment.Thumbnail.FileSize = len(thumb.image) | ||||||
| 
 | 
 | ||||||
| 		if err := putOrUpdate(ctx, p.database, p.attachment); err != nil { |  | ||||||
| 			p.err = err |  | ||||||
| 			p.thumbstate = errored |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// set the thumbnail of this media | 		// set the thumbnail of this media | ||||||
| 		p.thumb = thumb | 		p.thumb = thumb | ||||||
| 
 | 
 | ||||||
|  | @ -216,12 +223,6 @@ func (p *ProcessingMedia) loadFullSize(ctx context.Context) (*ImageMeta, error) | ||||||
| 		p.attachment.File.UpdatedAt = time.Now() | 		p.attachment.File.UpdatedAt = time.Now() | ||||||
| 		p.attachment.Processing = gtsmodel.ProcessingStatusProcessed | 		p.attachment.Processing = gtsmodel.ProcessingStatusProcessed | ||||||
| 
 | 
 | ||||||
| 		if err := putOrUpdate(ctx, p.database, p.attachment); err != nil { |  | ||||||
| 			p.err = err |  | ||||||
| 			p.fullSizeState = errored |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// set the fullsize of this media | 		// set the fullsize of this media | ||||||
| 		p.fullSize = decoded | 		p.fullSize = decoded | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (p *processor) AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) { | func (p *processor) AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) { | ||||||
| 	return p.adminProcessor.EmojiCreate(ctx, authed.Account, authed.User, form) | 	return p.adminProcessor.EmojiCreate(ctx, authed.Account, authed.User, form) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ type Processor interface { | ||||||
| 	DomainBlocksGet(ctx context.Context, account *gtsmodel.Account, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode) | 	DomainBlocksGet(ctx context.Context, account *gtsmodel.Account, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode) | ||||||
| 	DomainBlockGet(ctx context.Context, account *gtsmodel.Account, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode) | 	DomainBlockGet(ctx context.Context, account *gtsmodel.Account, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode) | ||||||
| 	DomainBlockDelete(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.DomainBlock, gtserror.WithCode) | 	DomainBlockDelete(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.DomainBlock, gtserror.WithCode) | ||||||
| 	EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) | 	EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type processor struct { | type processor struct { | ||||||
|  |  | ||||||
|  | @ -26,14 +26,16 @@ import ( | ||||||
| 	"io" | 	"io" | ||||||
| 
 | 
 | ||||||
| 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" | 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/id" | 	"github.com/superseriousbusiness/gotosocial/internal/id" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/uris" | 	"github.com/superseriousbusiness/gotosocial/internal/uris" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) { | func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) { | ||||||
| 	if !user.Admin { | 	if !user.Admin { | ||||||
| 		return nil, fmt.Errorf("user %s not an admin", user.ID) | 		return nil, gtserror.NewErrorNotAuthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	data := func(innerCtx context.Context) ([]byte, error) { | 	data := func(innerCtx context.Context) ([]byte, error) { | ||||||
|  | @ -56,24 +58,27 @@ func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, | ||||||
| 
 | 
 | ||||||
| 	emojiID, err := id.NewRandomULID() | 	emojiID, err := id.NewRandomULID() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("error creating id for new emoji: %s", err) | 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error creating id for new emoji: %s", err), "error creating emoji ID") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	emojiURI := uris.GenerateURIForEmoji(emojiID) | 	emojiURI := uris.GenerateURIForEmoji(emojiID) | ||||||
| 
 | 
 | ||||||
| 	processingEmoji, err := p.mediaManager.ProcessEmoji(ctx, data, form.Shortcode, emojiID, emojiURI, nil) | 	processingEmoji, err := p.mediaManager.ProcessEmoji(ctx, data, form.Shortcode, emojiID, emojiURI, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error processing emoji: %s", err), "error processing emoji") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	emoji, err := processingEmoji.LoadEmoji(ctx) | 	emoji, err := processingEmoji.LoadEmoji(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		if err == db.ErrAlreadyExists { | ||||||
|  | 			return nil, gtserror.NewErrorConflict(fmt.Errorf("emoji with shortcode %s already exists", form.Shortcode), fmt.Sprintf("emoji with shortcode %s already exists", form.Shortcode)) | ||||||
|  | 		} | ||||||
|  | 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error loading emoji: %s", err), "error loading emoji") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji) | 	apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("error converting emoji to apitype: %s", err) | 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting emoji: %s", err), "error converting emoji to api representation") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &apiEmoji, nil | 	return &apiEmoji, nil | ||||||
|  |  | ||||||
|  | @ -96,7 +96,7 @@ type Processor interface { | ||||||
| 	AccountBlockRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) | 	AccountBlockRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) | ||||||
| 
 | 
 | ||||||
| 	// AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form. | 	// AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form. | ||||||
| 	AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) | 	AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) | ||||||
| 	// AdminDomainBlockCreate handles the creation of a new domain block by an admin, using the given form. | 	// AdminDomainBlockCreate handles the creation of a new domain block by an admin, using the given form. | ||||||
| 	AdminDomainBlockCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) (*apimodel.DomainBlock, gtserror.WithCode) | 	AdminDomainBlockCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) (*apimodel.DomainBlock, gtserror.WithCode) | ||||||
| 	// AdminDomainBlocksImport handles the import of multiple domain blocks by an admin, using the given form. | 	// AdminDomainBlocksImport handles the import of multiple domain blocks by an admin, using the given form. | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue