diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 5da1ffb4b..42c54ca5e 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -159,6 +159,28 @@ const docTemplate = `{ } } }, + "/api/v1/bookmarks/id/readable": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Get readable version of bookmark.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api_v1.contentResponseMessage" + } + }, + "403": { + "description": "Token not provided/invalid" + } + } + } + }, "/api/v1/tags": { "get": { "produces": [ @@ -206,6 +228,17 @@ const docTemplate = `{ } }, "definitions": { + "api_v1.contentResponseMessage": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "html": { + "type": "string" + } + } + }, "api_v1.loginRequestPayload": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 58a6d7be7..251e02b42 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -148,6 +148,28 @@ } } }, + "/api/v1/bookmarks/id/readable": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Get readable version of bookmark.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api_v1.contentResponseMessage" + } + }, + "403": { + "description": "Token not provided/invalid" + } + } + } + }, "/api/v1/tags": { "get": { "produces": [ @@ -195,6 +217,17 @@ } }, "definitions": { + "api_v1.contentResponseMessage": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "html": { + "type": "string" + } + } + }, "api_v1.loginRequestPayload": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 6935a30b3..0ce37a25c 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1,4 +1,11 @@ definitions: + api_v1.contentResponseMessage: + properties: + content: + type: string + html: + type: string + type: object api_v1.loginRequestPayload: properties: password: @@ -218,6 +225,20 @@ paths: summary: Update Cache and Ebook on server. tags: - Auth + /api/v1/bookmarks/id/readable: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api_v1.contentResponseMessage' + "403": + description: Token not provided/invalid + summary: Get readable version of bookmark. + tags: + - Auth /api/v1/tags: get: produces: diff --git a/internal/http/routes/api/v1/bookmarks.go b/internal/http/routes/api/v1/bookmarks.go index fc65c4cfd..fab06d06b 100644 --- a/internal/http/routes/api/v1/bookmarks.go +++ b/internal/http/routes/api/v1/bookmarks.go @@ -27,6 +27,7 @@ type BookmarksAPIRoutes struct { func (r *BookmarksAPIRoutes) Setup(g *gin.RouterGroup) model.Routes { g.Use(middleware.AuthenticationRequired()) g.PUT("/cache", r.updateCache) + g.GET("/:id/readable", r.bookmarkReadable) return r } @@ -57,6 +58,63 @@ func (p *updateCachePayload) IsValid() error { return nil } +func (r *BookmarksAPIRoutes) getBookmark(c *context.Context) (*model.BookmarkDTO, error) { + bookmarkIDParam, present := c.Params.Get("id") + if !present { + response.SendError(c.Context, http.StatusBadRequest, "Invalid bookmark ID") + return nil, model.ErrBookmarkInvalidID + } + + bookmarkID, err := strconv.Atoi(bookmarkIDParam) + if err != nil { + r.logger.WithError(err).Error("error parsing bookmark ID parameter") + response.SendInternalServerError(c.Context) + return nil, err + } + + if bookmarkID == 0 { + response.SendError(c.Context, http.StatusNotFound, nil) + return nil, model.ErrBookmarkNotFound + } + + bookmark, err := r.deps.Domains.Bookmarks.GetBookmark(c.Context, model.DBID(bookmarkID)) + if err != nil { + response.SendError(c.Context, http.StatusNotFound, nil) + return nil, model.ErrBookmarkNotFound + } + + return bookmark, nil +} + +type readableResponseMessage struct { + Content string `json:"content"` + Html string `json:"html"` +} + +// Bookmark Readable godoc +// +// @Summary Get readable version of bookmark. +// @Tags Auth +// @securityDefinitions.apikey ApiKeyAuth +// @Produce json +// @Success 200 {object} contentResponseMessage +// @Failure 403 {object} nil "Token not provided/invalid" +// @Router /api/v1/bookmarks/id/readable [get] +func (r *BookmarksAPIRoutes) bookmarkReadable(c *gin.Context) { + ctx := context.NewContextFromGin(c) + + bookmark, err := r.getBookmark(ctx) + if err != nil { + return + } + responseMessage := readableResponseMessage{ + Content: bookmark.Content, + Html: bookmark.HTML, + } + + response.Send(c, 200, responseMessage) +} + // updateCache godoc // // @Summary Update Cache and Ebook on server. diff --git a/internal/http/routes/api/v1/bookmarks_test.go b/internal/http/routes/api/v1/bookmarks_test.go index c56c61349..7af902565 100644 --- a/internal/http/routes/api/v1/bookmarks_test.go +++ b/internal/http/routes/api/v1/bookmarks_test.go @@ -45,3 +45,53 @@ func TestUpdateBookmarkCache(t *testing.T) { require.Equal(t, http.StatusForbidden, w.Code) }) } + +func TestReadableeBookmarkContent(t *testing.T) { + logger := logrus.New() + ctx := context.TODO() + + g := gin.New() + + _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) + g.Use(middleware.AuthMiddleware(deps)) + + router := NewBookmarksAPIRoutes(logger, deps) + router.Setup(g.Group("/")) + + account := model.Account{ + Username: "test", + Password: "test", + Owner: false, + } + require.NoError(t, deps.Database.SaveAccount(ctx, account)) + token, err := deps.Domains.Auth.CreateTokenForAccount(&account, time.Now().Add(time.Minute)) + require.NoError(t, err) + + bookmark := testutil.GetValidBookmark() + _, err = deps.Database.SaveBookmarks(ctx, true, *bookmark) + require.NoError(t, err) + response := `{"ok":true,"message":{"content":"","html":""}}` + + t.Run("require authentication", func(t *testing.T) { + w := testutil.PerformRequest(g, "GET", "/1/readable") + require.Equal(t, http.StatusUnauthorized, w.Code) + }) + t.Run("get content but invalid id", func(t *testing.T) { + w := testutil.PerformRequest(g, "GET", "/invalidId/readable", testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) + require.Equal(t, http.StatusInternalServerError, w.Code) + }) + t.Run("get content but 0 id", func(t *testing.T) { + w := testutil.PerformRequest(g, "GET", "/0/readable", testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) + require.Equal(t, http.StatusNotFound, w.Code) + }) + t.Run("get content but not exist", func(t *testing.T) { + w := testutil.PerformRequest(g, "GET", "/2/readable", testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) + require.Equal(t, http.StatusNotFound, w.Code) + }) + t.Run("get content", func(t *testing.T) { + w := testutil.PerformRequest(g, "GET", "/1/readable", testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) + require.Equal(t, response, w.Body.String()) + require.Equal(t, http.StatusOK, w.Code) + }) + +}