diff options
Diffstat (limited to 'internal/api/client/media')
-rw-r--r-- | internal/api/client/media/media.go | 27 | ||||
-rw-r--r-- | internal/api/client/media/mediacreate.go | 34 | ||||
-rw-r--r-- | internal/api/client/media/mediacreate_test.go | 53 | ||||
-rw-r--r-- | internal/api/client/media/mediaget.go | 18 | ||||
-rw-r--r-- | internal/api/client/media/mediaupdate.go | 28 | ||||
-rw-r--r-- | internal/api/client/media/mediaupdate_test.go | 31 |
6 files changed, 85 insertions, 106 deletions
diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go index 87cc2f091..889a4f3df 100644 --- a/internal/api/client/media/media.go +++ b/internal/api/client/media/media.go @@ -21,34 +21,31 @@ package media import ( "net/http" - "github.com/superseriousbusiness/gotosocial/internal/api" + "github.com/gin-gonic/gin" "github.com/superseriousbusiness/gotosocial/internal/processing" - "github.com/superseriousbusiness/gotosocial/internal/router" ) const ( - IDKey = "id" // IDKey is the key for media attachment IDs - APIVersionKey = "api_version" // APIVersionKey is the key for which version of the API to use (v1 or v2) - BasePathWithAPIVersion = "/api/:" + APIVersionKey + "/media" // BasePathWithAPIVersion is the base API path for making media requests through v1 or v2 of the api (for mastodon API compatibility) - BasePathWithIDV1 = "/api/v1/media/:" + IDKey // BasePathWithID corresponds to a media attachment with the given ID + IDKey = "id" // IDKey is the key for media attachment IDs + APIVersionKey = "api_version" // APIVersionKey is the key for which version of the API to use (v1 or v2) + APIv1 = "v1" // APIV1 corresponds to version 1 of the api + APIv2 = "v2" // APIV2 corresponds to version 2 of the api + BasePath = "/:" + APIVersionKey + "/media" // BasePath is the base API path for making media requests through v1 or v2 of the api (for mastodon API compatibility) + AttachmentWithID = BasePath + "/:" + IDKey // BasePathWithID corresponds to a media attachment with the given ID ) -// Module implements the ClientAPIModule interface for media type Module struct { processor processing.Processor } -// New returns a new auth module -func New(processor processing.Processor) api.ClientModule { +func New(processor processing.Processor) *Module { return &Module{ processor: processor, } } -// Route satisfies the RESTAPIModule interface -func (m *Module) Route(s router.Router) error { - s.AttachHandler(http.MethodPost, BasePathWithAPIVersion, m.MediaCreatePOSTHandler) - s.AttachHandler(http.MethodGet, BasePathWithIDV1, m.MediaGETHandler) - s.AttachHandler(http.MethodPut, BasePathWithIDV1, m.MediaPUTHandler) - return nil +func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) { + attachHandler(http.MethodPost, BasePath, m.MediaCreatePOSTHandler) + attachHandler(http.MethodGet, AttachmentWithID, m.MediaGETHandler) + attachHandler(http.MethodPut, AttachmentWithID, m.MediaPUTHandler) } diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go index db8b2ea56..7e29b2bb3 100644 --- a/internal/api/client/media/mediacreate.go +++ b/internal/api/client/media/mediacreate.go @@ -24,8 +24,8 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/api" - "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/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -94,42 +94,42 @@ import ( // '500': // description: internal server error func (m *Module) MediaCreatePOSTHandler(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) + apiVersion := c.Param(APIVersionKey) + if apiVersion != APIv1 && apiVersion != APIv2 { + err := errors.New("api version must be one of v1 or v2 for this path") + apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(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) + authed, err := oauth.Authed(c, true, true, true, true) + if err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) return } - apiVersion := c.Param(APIVersionKey) - if apiVersion != "v1" && apiVersion != "v2" { - err := errors.New("api version must be one of v1 or v2") - api.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet) + if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet) return } - form := &model.AttachmentRequest{} + form := &apimodel.AttachmentRequest{} if err := c.ShouldBind(&form); err != nil { - api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) return } if err := validateCreateMedia(form); err != nil { - api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) return } apiAttachment, errWithCode := m.processor.MediaCreate(c.Request.Context(), authed, form) if errWithCode != nil { - api.ErrorHandler(c, errWithCode, m.processor.InstanceGet) + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet) return } - if apiVersion == "v2" { + if apiVersion == APIv2 { // the mastodon v2 media API specifies that the URL should be null // and that the client should call /api/v1/media/:id to get the URL // @@ -141,7 +141,7 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) { c.JSON(http.StatusOK, apiAttachment) } -func validateCreateMedia(form *model.AttachmentRequest) error { +func validateCreateMedia(form *apimodel.AttachmentRequest) error { // check there actually is a file attached and it's not size 0 if form.File == nil { return errors.New("no attachment given") diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go index 2f6fb12a4..9e787b4b9 100644 --- a/internal/api/client/media/mediacreate_test.go +++ b/internal/api/client/media/mediacreate_test.go @@ -30,10 +30,9 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/suite" mediamodule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" - "github.com/superseriousbusiness/gotosocial/internal/api/model" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/concurrency" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -96,7 +95,7 @@ func (suite *MediaCreateTestSuite) SetupSuite() { suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker) // setup module being tested - suite.mediaModule = mediamodule.New(suite.processor).(*mediamodule.Module) + suite.mediaModule = mediamodule.New(suite.processor) } func (suite *MediaCreateTestSuite) TearDownSuite() { @@ -158,12 +157,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() { ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v1/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) ctx.Request.Header.Set("accept", "application/json") - ctx.Params = gin.Params{ - gin.Param{ - Key: mediamodule.APIVersionKey, - Value: "v1", - }, - } + ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1) // do the actual request suite.mediaModule.MediaCreatePOSTHandler(ctx) @@ -188,26 +182,26 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() { suite.NoError(err) fmt.Println(string(b)) - attachmentReply := &model.Attachment{} + attachmentReply := &apimodel.Attachment{} err = json.Unmarshal(b, attachmentReply) suite.NoError(err) suite.Equal("this is a test image -- a cool background from somewhere", *attachmentReply.Description) suite.Equal("image", attachmentReply.Type) - suite.EqualValues(model.MediaMeta{ - Original: model.MediaDimensions{ + suite.EqualValues(apimodel.MediaMeta{ + Original: apimodel.MediaDimensions{ Width: 1920, Height: 1080, Size: "1920x1080", Aspect: 1.7777778, }, - Small: model.MediaDimensions{ + Small: apimodel.MediaDimensions{ Width: 512, Height: 288, Size: "512x288", Aspect: 1.7777778, }, - Focus: model.MediaFocus{ + Focus: apimodel.MediaFocus{ X: -0.5, Y: 0.5, }, @@ -252,12 +246,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() { ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v2/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) ctx.Request.Header.Set("accept", "application/json") - ctx.Params = gin.Params{ - gin.Param{ - Key: mediamodule.APIVersionKey, - Value: "v2", - }, - } + ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv2) // do the actual request suite.mediaModule.MediaCreatePOSTHandler(ctx) @@ -282,26 +271,26 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() { suite.NoError(err) fmt.Println(string(b)) - attachmentReply := &model.Attachment{} + attachmentReply := &apimodel.Attachment{} err = json.Unmarshal(b, attachmentReply) suite.NoError(err) suite.Equal("this is a test image -- a cool background from somewhere", *attachmentReply.Description) suite.Equal("image", attachmentReply.Type) - suite.EqualValues(model.MediaMeta{ - Original: model.MediaDimensions{ + suite.EqualValues(apimodel.MediaMeta{ + Original: apimodel.MediaDimensions{ Width: 1920, Height: 1080, Size: "1920x1080", Aspect: 1.7777778, }, - Small: model.MediaDimensions{ + Small: apimodel.MediaDimensions{ Width: 512, Height: 288, Size: "512x288", Aspect: 1.7777778, }, - Focus: model.MediaFocus{ + Focus: apimodel.MediaFocus{ X: -0.5, Y: 0.5, }, @@ -342,12 +331,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateLongDescription() { ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v1/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) ctx.Request.Header.Set("accept", "application/json") - ctx.Params = gin.Params{ - gin.Param{ - Key: mediamodule.APIVersionKey, - Value: "v1", - }, - } + ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1) // do the actual request suite.mediaModule.MediaCreatePOSTHandler(ctx) @@ -388,12 +372,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateTooShortDescription() { ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v1/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) ctx.Request.Header.Set("accept", "application/json") - ctx.Params = gin.Params{ - gin.Param{ - Key: mediamodule.APIVersionKey, - Value: "v1", - }, - } + ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1) // do the actual request suite.mediaModule.MediaCreatePOSTHandler(ctx) diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go index fd232c4c7..b22c8e79c 100644 --- a/internal/api/client/media/mediaget.go +++ b/internal/api/client/media/mediaget.go @@ -23,7 +23,7 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/api" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -67,27 +67,33 @@ import ( // '500': // description: internal server error func (m *Module) MediaGETHandler(c *gin.Context) { + if apiVersion := c.Param(APIVersionKey); apiVersion != APIv1 { + err := errors.New("api version must be one v1 for this path") + apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet) + return + } + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { - api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(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) + if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet) return } attachmentID := c.Param(IDKey) if attachmentID == "" { err := errors.New("no attachment id specified") - api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) return } attachment, errWithCode := m.processor.MediaGet(c.Request.Context(), authed, attachmentID) if errWithCode != nil { - api.ErrorHandler(c, errWithCode, m.processor.InstanceGet) + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet) return } diff --git a/internal/api/client/media/mediaupdate.go b/internal/api/client/media/mediaupdate.go index 438eaca23..9cfd8a5f1 100644 --- a/internal/api/client/media/mediaupdate.go +++ b/internal/api/client/media/mediaupdate.go @@ -24,8 +24,8 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/api" - "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/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -99,45 +99,51 @@ import ( // '500': // description: internal server error func (m *Module) MediaPUTHandler(c *gin.Context) { + if apiVersion := c.Param(APIVersionKey); apiVersion != APIv1 { + err := errors.New("api version must be one v1 for this path") + apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet) + return + } + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { - api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(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) + if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet) return } attachmentID := c.Param(IDKey) if attachmentID == "" { err := errors.New("no attachment id specified") - api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) return } - form := &model.AttachmentUpdateRequest{} + form := &apimodel.AttachmentUpdateRequest{} if err := c.ShouldBind(form); err != nil { - api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) return } if err := validateUpdateMedia(form); err != nil { - api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) + apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet) return } attachment, errWithCode := m.processor.MediaUpdate(c.Request.Context(), authed, attachmentID, form) if errWithCode != nil { - api.ErrorHandler(c, errWithCode, m.processor.InstanceGet) + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet) return } c.JSON(http.StatusOK, attachment) } -func validateUpdateMedia(form *model.AttachmentUpdateRequest) error { +func validateUpdateMedia(form *apimodel.AttachmentUpdateRequest) error { minDescriptionChars := config.GetMediaDescriptionMinChars() maxDescriptionChars := config.GetMediaDescriptionMaxChars() diff --git a/internal/api/client/media/mediaupdate_test.go b/internal/api/client/media/mediaupdate_test.go index e5abb0a91..bcf9a4dfe 100644 --- a/internal/api/client/media/mediaupdate_test.go +++ b/internal/api/client/media/mediaupdate_test.go @@ -28,10 +28,9 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/suite" mediamodule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" - "github.com/superseriousbusiness/gotosocial/internal/api/model" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/concurrency" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -94,7 +93,7 @@ func (suite *MediaUpdateTestSuite) SetupSuite() { suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker) // setup module being tested - suite.mediaModule = mediamodule.New(suite.processor).(*mediamodule.Module) + suite.mediaModule = mediamodule.New(suite.processor) } func (suite *MediaUpdateTestSuite) TearDownSuite() { @@ -148,12 +147,8 @@ func (suite *MediaUpdateTestSuite) TestUpdateImage() { ctx.Request = httptest.NewRequest(http.MethodPut, fmt.Sprintf("http://localhost:8080/api/v1/media/%s", toUpdate.ID), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) ctx.Request.Header.Set("accept", "application/json") - ctx.Params = gin.Params{ - gin.Param{ - Key: mediamodule.IDKey, - Value: toUpdate.ID, - }, - } + ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1) + ctx.AddParam(mediamodule.IDKey, toUpdate.ID) // do the actual request suite.mediaModule.MediaPUTHandler(ctx) @@ -167,17 +162,17 @@ func (suite *MediaUpdateTestSuite) TestUpdateImage() { suite.NoError(err) // reply should be an attachment - attachmentReply := &model.Attachment{} + attachmentReply := &apimodel.Attachment{} err = json.Unmarshal(b, attachmentReply) suite.NoError(err) // the reply should contain the updated fields suite.Equal("new description!", *attachmentReply.Description) suite.EqualValues("image", attachmentReply.Type) - suite.EqualValues(model.MediaMeta{ - Original: model.MediaDimensions{Width: 800, Height: 450, FrameRate: "", Duration: 0, Bitrate: 0, Size: "800x450", Aspect: 1.7777778}, - Small: model.MediaDimensions{Width: 256, Height: 144, FrameRate: "", Duration: 0, Bitrate: 0, Size: "256x144", Aspect: 1.7777778}, - Focus: model.MediaFocus{X: -0.1, Y: 0.3}, + suite.EqualValues(apimodel.MediaMeta{ + Original: apimodel.MediaDimensions{Width: 800, Height: 450, FrameRate: "", Duration: 0, Bitrate: 0, Size: "800x450", Aspect: 1.7777778}, + Small: apimodel.MediaDimensions{Width: 256, Height: 144, FrameRate: "", Duration: 0, Bitrate: 0, Size: "256x144", Aspect: 1.7777778}, + Focus: apimodel.MediaFocus{X: -0.1, Y: 0.3}, }, attachmentReply.Meta) suite.Equal(toUpdate.Blurhash, attachmentReply.Blurhash) suite.Equal(toUpdate.ID, attachmentReply.ID) @@ -213,12 +208,8 @@ func (suite *MediaUpdateTestSuite) TestUpdateImageShortDescription() { ctx.Request = httptest.NewRequest(http.MethodPut, fmt.Sprintf("http://localhost:8080/api/v1/media/%s", toUpdate.ID), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) ctx.Request.Header.Set("accept", "application/json") - ctx.Params = gin.Params{ - gin.Param{ - Key: mediamodule.IDKey, - Value: toUpdate.ID, - }, - } + ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1) + ctx.AddParam(mediamodule.IDKey, toUpdate.ID) // do the actual request suite.mediaModule.MediaPUTHandler(ctx) |