diff --git a/app/controllers/api/v0/articles_controller.rb b/app/controllers/api/v0/articles_controller.rb index ace76932f..aba347237 100644 --- a/app/controllers/api/v0/articles_controller.rb +++ b/app/controllers/api/v0/articles_controller.rb @@ -47,7 +47,19 @@ module Api def me per_page = (params[:per_page] || 30).to_i num = [per_page, 1000].min - @articles = @user.articles. + + @articles = case params[:status] + when "published" + @user.articles.published + when "unpublished" + @user.articles.unpublished + when "all" + @user.articles + else + @user.articles.published + end + + @articles = @articles. includes(:organization). order(published_at: :desc, created_at: :desc). page(params[:page]). diff --git a/app/models/article.rb b/app/models/article.rb index d339b4ec7..1a6f8d6f8 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -75,6 +75,7 @@ class Article < ApplicationRecord serialize :cached_organization scope :published, -> { where(published: true) } + scope :unpublished, -> { where(published: false) } scope :cached_tagged_with, ->(tag) { where("cached_tag_list ~* ?", "^#{tag},| #{tag},|, #{tag}$|^#{tag}$") } diff --git a/config/routes.rb b/config/routes.rb index 1225b1f81..be1fe7ff3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -79,7 +79,7 @@ Rails.application.routes.draw do constraints: ApiConstraints.new(version: 0, default: true) do resources :articles, only: %i[index show create update] do collection do - get :me + get "me(/:status)", to: "articles#me", as: :me, constraints: { status: /published|unpublished|all/ } end end resources :comments, only: %i[index show] diff --git a/docs/api_v0.yml b/docs/api_v0.yml index 6e07d6108..64f442392 100644 --- a/docs/api_v0.yml +++ b/docs/api_v0.yml @@ -2,7 +2,7 @@ openapi: "3.0.2" info: title: DEV API description: Access DEV articles, comments and other resources via API - version: "0.5.3" + version: "0.5.4" termsOfService: https://dev.to/terms contact: name: DEV Team @@ -983,6 +983,153 @@ paths: /articles/me: get: summary: Authenticated user's articles + description: | + This endpoint allows the client to retrieve a list of its published articles. + + "Articles" are all the posts that users create on DEV that typically + show up in the feed. They can be a blog post, a discussion question, + a help thread etc. but is referred to as article within the code. + + Published articles will be in reverse chronological publication order. + + It will return published articles with pagination. + By default a page will contain `30` articles. + tags: + - articles + parameters: + - name: page + in: query + description: Pagination page. + schema: + type: integer + format: int32 + example: 1 + - name: per_page + in: query + description: Page size (defaults to 30 with a maximum of 1000). + schema: + type: integer + format: int32 + example: 30 + responses: + 200: + description: A list of published articles + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ArticleMe" + security: + - api_key: [] + - oauth2: [] + x-code-samples: # https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md#x-code-samples + - lang: Shell + label: curl + source: | + curl https://dev.to/api/articles/me + + /articles/me/published: + get: + summary: Authenticated user's published articles + description: | + This endpoint allows the client to retrieve a list of its published articles. + + "Articles" are all the posts that users create on DEV that typically + show up in the feed. They can be a blog post, a discussion question, + a help thread etc. but is referred to as article within the code. + + Published articles will be in reverse chronological publication order. + + It will return published articles with pagination. + By default a page will contain `30` articles. + tags: + - articles + parameters: + - name: page + in: query + description: Pagination page. + schema: + type: integer + format: int32 + example: 1 + - name: per_page + in: query + description: Page size (defaults to 30 with a maximum of 1000). + schema: + type: integer + format: int32 + example: 30 + responses: + 200: + description: A list of published articles + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ArticleMe" + security: + - api_key: [] + - oauth2: [] + x-code-samples: # https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md#x-code-samples + - lang: Shell + label: curl + source: | + curl https://dev.to/api/articles/me/published + + /articles/me/unpublished: + get: + summary: Authenticated user's unpublished articles + description: | + This endpoint allows the client to retrieve a list of its unpublished articles. + + "Articles" are all the posts that users create on DEV that typically + show up in the feed. They can be a blog post, a discussion question, + a help thread etc. but is referred to as article within the code. + + Unpublished articles will be in reverse chronological creation order. + + It will return unpublished articles with pagination. + By default a page will contain `30` articles. + tags: + - articles + parameters: + - name: page + in: query + description: Pagination page. + schema: + type: integer + format: int32 + example: 1 + - name: per_page + in: query + description: Page size (defaults to 30 with a maximum of 1000). + schema: + type: integer + format: int32 + example: 30 + responses: + 200: + description: A list of articles + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ArticleMe" + security: + - api_key: [] + - oauth2: [] + x-code-samples: # https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md#x-code-samples + - lang: Shell + label: curl + source: | + curl https://dev.to/api/articles/me/unpublished + + /articles/me/all: + get: + summary: Authenticated user's all articles description: | This endpoint allows the client to retrieve a list of its articles. @@ -990,8 +1137,9 @@ paths: show up in the feed. They can be a blog post, a discussion question, a help thread etc. but is referred to as article within the code. - It will return both published and draft articles with pagination. - Draft articles will be at the top of the list in reverse chronological creation order. + It will return both published and unpublished articles with pagination. + + Unpublished articles will be at the top of the list in reverse chronological creation order. Published articles will follow in reverse chronological publication order. By default a page will contain `30` articles. @@ -1028,7 +1176,7 @@ paths: - lang: Shell label: curl source: | - curl https://dev.to/api/articles/me + curl https://dev.to/api/articles/me/all /webhooks: post: diff --git a/spec/requests/api/v0/articles_spec.rb b/spec/requests/api/v0/articles_spec.rb index 293acefff..de489e949 100644 --- a/spec/requests/api/v0/articles_spec.rb +++ b/spec/requests/api/v0/articles_spec.rb @@ -112,7 +112,7 @@ RSpec.describe "Api::V0::Articles", type: :request do end end - describe "GET /api/articles/me" do + describe "GET /api/articles/me(/:status)" do context "when request is unauthenticated" do it "return unauthorized" do get me_api_articles_path @@ -152,24 +152,42 @@ RSpec.describe "Api::V0::Articles", type: :request do expect(json_response.length).to eq(1) end - it "puts unpublished articles at the top" do - create(:article, user: user) + it "only includes published articles by default" do create(:article, published: false, published_at: nil, user: user) get me_api_articles_path, params: { access_token: access_token.token } - expected_order = json_response.map { |resp| resp["published"] } - expect(expected_order).to eq([false, true]) + expect(json_response.length).to eq(0) end - it "orders unpublished articles by reverse order" do + it "only includes published articles when asking for published articles" do + create(:article, published: false, published_at: nil, user: user) + get me_api_articles_path(status: :published), params: { access_token: access_token.token } + expect(json_response.length).to eq(0) + end + + it "only includes unpublished articles when asking for unpublished articles" do + create(:article, published: false, published_at: nil, user: user) + get me_api_articles_path(status: :unpublished), params: { access_token: access_token.token } + expect(json_response.length).to eq(1) + end + + it "orders unpublished articles by reverse order when asking for unpublished articles" do older = create(:article, published: false, published_at: nil, user: user) newer = nil Timecop.travel(1.day.from_now) do newer = create(:article, published: false, published_at: nil, user: user) end - get me_api_articles_path, params: { access_token: access_token.token } + get me_api_articles_path(status: :unpublished), params: { access_token: access_token.token } expected_order = json_response.map { |resp| resp["id"] } expect(expected_order).to eq([newer.id, older.id]) end + + it "puts unpublished articles at the top when asking for all articles" do + create(:article, user: user) + create(:article, published: false, published_at: nil, user: user) + get me_api_articles_path(status: :all), params: { access_token: access_token.token } + expected_order = json_response.map { |resp| resp["published"] } + expect(expected_order).to eq([false, true]) + end end end