diff --git a/app/controllers/api/v0/organizations_controller.rb b/app/controllers/api/v0/organizations_controller.rb index 5447a9e72..e1063594b 100644 --- a/app/controllers/api/v0/organizations_controller.rb +++ b/app/controllers/api/v0/organizations_controller.rb @@ -1,7 +1,7 @@ module Api module V0 class OrganizationsController < ApiController - before_action :find_organization, only: %i[users listings] + before_action :find_organization, only: %i[users listings articles] SHOW_ATTRIBUTES_FOR_SERIALIZATION = %i[ id username name summary twitter_username github_username url @@ -21,6 +21,8 @@ module Api ].freeze private_constant :LISTINGS_FOR_SERIALIZATION + ARTICLES_FOR_SERIALIZATION = Api::V0::ArticlesController::INDEX_ATTRIBUTES_FOR_SERIALIZATION + def show @organization = Organization.select(SHOW_ATTRIBUTES_FOR_SERIALIZATION) .find_by!(username: params[:username]) @@ -47,6 +49,22 @@ module Api @listings = @listings.in_category(params[:category]) if params[:category].present? end + def articles + per_page = (params[:per_page] || 30).to_i + num = [per_page, 1000].min + page = params[:page] || 1 + + @articles = @organization.articles.published + .select(ARTICLES_FOR_SERIALIZATION) + .includes(:user) + .order(published_at: :desc) + .page(page) + .per(num) + .decorate + + render "api/v0/articles/index.json.jbuilder" + end + private def find_organization diff --git a/app/views/api/v0/articles/index.json.jbuilder b/app/views/api/v0/articles/index.json.jbuilder index a720ce9c0..da98e6f20 100644 --- a/app/views/api/v0/articles/index.json.jbuilder +++ b/app/views/api/v0/articles/index.json.jbuilder @@ -1,5 +1,5 @@ json.array! @articles do |article| - json.partial! "article", article: article + json.partial! "api/v0/articles/article", article: article # /api/articles and /api/articles/:id have opposite representations # of `tag_list` and `tags and we can't align them without breaking the API, @@ -16,6 +16,6 @@ json.array! @articles do |article| flare_tag = FlareTag.new(article).tag if flare_tag - json.partial! "flare_tag", flare_tag: flare_tag + json.partial! "api/v0/articles/flare_tag", flare_tag: flare_tag end end diff --git a/config/routes.rb b/config/routes.rb index e4e4ef2a1..9784756dd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -204,6 +204,7 @@ Rails.application.routes.draw do resources :organizations, only: [:show], param: :username do resources :users, only: [:index], to: "organizations#users" resources :listings, only: [:index], to: "organizations#listings" + resources :articles, only: [:index], to: "organizations#articles" end namespace :admin do diff --git a/docs/api_v0.yml b/docs/api_v0.yml index f0a9aa6a9..4af6a91a6 100644 --- a/docs/api_v0.yml +++ b/docs/api_v0.yml @@ -17,7 +17,7 @@ info: Dates and date times, unless otherwise specified, must be in the [RFC 3339](https://tools.ietf.org/html/rfc3339) format. - version: "0.9.4" + version: "0.9.5" termsOfService: https://dev.to/terms contact: name: DEV Team @@ -3274,6 +3274,53 @@ paths: source: | curl https://dev.to/api/organizations/ecorp/listings + /organizations/{username}/articles: + get: + operationId: getOrgArticles + summary: Organization's Articles + description: | + This endpoint allows the client to retrieve a list of Articles belonging to the organization + + It supports pagination, each page will contain `30` listing by default. + tags: + - organizations + parameters: + - name: username + in: path + required: true + description: | + Username of the organization + schema: + type: string + example: "ecorp" + - $ref: '#/components/parameters/pageParam' + - $ref: '#/components/parameters/perPageParam30to1000' + responses: + "200": + description: A list of users belonging to the organization + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ArticleIndex" + examples: + article-success: + $ref: "#/components/examples/ArticlesIndex" + "404": + description: Resource not found + content: + application/json: + schema: + $ref: "#/components/schemas/APIError" + examples: + user-not-found: + $ref: "#/components/examples/ErrorNotFound" + x-code-samples: + - lang: Shell + label: curl + source: | + curl https://dev.to/api/organizations/ecorp/listings /podcast_episodes: get: diff --git a/spec/requests/api/v0/organizations_spec.rb b/spec/requests/api/v0/organizations_spec.rb index 48595e822..42ac31dd5 100644 --- a/spec/requests/api/v0/organizations_spec.rb +++ b/spec/requests/api/v0/organizations_spec.rb @@ -113,4 +113,49 @@ RSpec.describe "Api::V0::Organizations", type: :request do end end end + + describe "GET /api/organizations/:username/articles" do + let(:org_user) { create(:user, :org_member) } + let(:organization) { org_user.organizations.first } + let!(:article) { create(:article, user: org_user, organization: organization) } + + it "returns 404 if the organizations articles is not found" do + get "/api/organizations/invalid-username/articles" + expect(response).to have_http_status(:not_found) + end + + it "supports pagination" do + create(:article, organization: organization) + + get api_organization_articles_path(organization.username), params: { page: 1, per_page: 1 } + expect(response.parsed_body.length).to eq(1) + + get api_organization_articles_path(organization.username), params: { page: 2, per_page: 1 } + expect(response.parsed_body.length).to eq(1) + + get api_organization_articles_path(organization.username), params: { page: 3, per_page: 1 } + expect(response.parsed_body.length).to eq(0) + end + + it "returns the correct json representation of the organizations articles", :aggregate_failures do + get api_organization_articles_path(organization.username) + response_article = response.parsed_body.first + expect(response_article["type_of"]).to eq("article") + + %w[id title slug description path public_reactions_count + positive_reactions_count comments_count published_timestamp].each do |attr| + expect(response_article[attr]).to eq(article.public_send(attr)) + end + + expect(response_article["tag_list"]).to match_array(article.tag_list) + + %w[name username twitter_username github_username website_url].each do |attr| + expect(response_article["user"][attr]).to eq(org_user.public_send(attr)) + end + + %w[name username slug].each do |attr| + expect(response_article["organization"][attr]).to eq(organization.public_send(attr)) + end + end + end end