diff --git a/app/controllers/concerns/api/organizations_controller.rb b/app/controllers/concerns/api/organizations_controller.rb index 6ae80673e..6883947b2 100644 --- a/app/controllers/concerns/api/organizations_controller.rb +++ b/app/controllers/concerns/api/organizations_controller.rb @@ -73,7 +73,9 @@ module Api def find_organization # Looking up via id_or_slug parameter since both types of lookup are handled in # the v1 show route - @organization = Organization.find_by!(username: params[:organization_id_or_slug]) + @organization = Organization.find_by(id: params[:organization_id_or_slug]) || + Organization.find_by(username: params[:organization_id_or_slug]) + raise ActiveRecord::RecordNotFound unless @organization end end end diff --git a/spec/requests/api/v1/docs/organizations_spec.rb b/spec/requests/api/v1/docs/organizations_spec.rb index e547401d7..30760e05b 100644 --- a/spec/requests/api/v1/docs/organizations_spec.rb +++ b/spec/requests/api/v1/docs/organizations_spec.rb @@ -41,7 +41,7 @@ RSpec.describe "Api::V1::Docs::Organizations" do end end - path "/api/organizations/{username}/users" do + path "/api/organizations/{organization_id_or_username}/users" do get "Organization's users" do tags "organizations", "users" security [] @@ -50,12 +50,20 @@ RSpec.describe "Api::V1::Docs::Organizations" do It supports pagination, each page will contain `30` users by default." operationId "getOrgUsers" produces "application/json" - parameter name: :username, in: :path, type: :string, required: true + parameter name: :organization_id_or_username, in: :path, type: :string, required: true parameter "$ref": "#/components/parameters/pageParam" parameter "$ref": "#/components/parameters/perPageParam30to1000" - response "200", "An Organization's users" do - let(:username) { organization.username } + response "200", "An Organization's users (with username)" do + let(:organization_id_or_username) { organization.username } + schema type: :array, + items: { "$ref": "#/components/schemas/User" } + add_examples + run_test! + end + + response "200", "An Organization's users (with ID)" do + let(:organization_id_or_username) { organization.id } schema type: :array, items: { "$ref": "#/components/schemas/User" } add_examples @@ -63,14 +71,14 @@ It supports pagination, each page will contain `30` users by default." end response "404", "Not Found" do - let(:username) { "non-existent-username" } + let(:organization_id_or_username) { "non-existent-id-or-username" } add_examples run_test! end end end - path "/api/organizations/{username}/articles" do + path "/api/organizations/{organization_id_or_username}/articles" do get "Organization's Articles" do tags "organizations", "articles" security [] @@ -79,12 +87,20 @@ It supports pagination, each page will contain `30` users by default." It supports pagination, each page will contain `30` users by default." operationId "getOrgArticles" produces "application/json" - parameter name: :username, in: :path, type: :string, required: true + parameter name: :organization_id_or_username, in: :path, type: :string, required: true parameter "$ref": "#/components/parameters/pageParam" parameter "$ref": "#/components/parameters/perPageParam30to1000" - response "200", "An Organization's Articles" do - let(:username) { organization.username } + response "200", "An Organization's Articles (with username)" do + let(:organization_id_or_username) { organization.username } + schema type: :array, + items: { "$ref": "#/components/schemas/ArticleIndex" } + add_examples + run_test! + end + + response "200", "An Organization's Articles (with ID)" do + let(:organization_id_or_username) { organization.id } schema type: :array, items: { "$ref": "#/components/schemas/ArticleIndex" } add_examples @@ -92,7 +108,7 @@ It supports pagination, each page will contain `30` users by default." end response "404", "Not Found" do - let(:username) { "non-existent-username" } + let(:organization_id_or_username) { "non-existent-username" } add_examples run_test! end diff --git a/spec/requests/api/v1/organizations_spec.rb b/spec/requests/api/v1/organizations_spec.rb index d5dcf8510..3f18a7629 100644 --- a/spec/requests/api/v1/organizations_spec.rb +++ b/spec/requests/api/v1/organizations_spec.rb @@ -243,61 +243,74 @@ RSpec.describe "Api::V1::Organizations" do end end - describe "GET /api/organizations/:username/users" do + describe "GET /api/organizations/:organization_id_or_slug/users" do let!(:org_user) { create(:user, :org_member) } let(:organization) { org_user.organizations.first } - it "returns 404 if the organizations username is not found" do - get "/api/organizations/invalid-username/users", headers: headers - expect(response).to have_http_status(:not_found) - end - - it "supports pagination" do - create(:organization_membership, user: create(:user), organization: organization) - - get api_organization_users_path(organization.username), params: { page: 1, per_page: 1 }, headers: headers - expect(response.parsed_body.length).to eq(1) - - get api_organization_users_path(organization.username), params: { page: 2, per_page: 1 }, headers: headers - expect(response.parsed_body.length).to eq(1) - - get api_organization_users_path(organization.username), params: { page: 3, per_page: 1 }, headers: headers - expect(response.parsed_body.length).to eq(0) - end - - it "respects API_PER_PAGE_MAX limit set in ENV variable" do - allow(ApplicationConfig).to receive(:[]).and_return(nil) - allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://") - allow(ApplicationConfig).to receive(:[]).with("API_PER_PAGE_MAX").and_return(2) - - create(:organization_membership, user: create(:user), organization: organization) - create(:organization_membership, user: create(:user), organization: organization) - create(:organization_membership, user: create(:user), organization: organization) - - get api_organization_users_path(organization.username), params: { per_page: 10 } - expect(response.parsed_body.count).to eq(2) - end - - it "returns the correct json representation of the organizations users", :aggregate_failures do - get api_organization_users_path(organization.username), headers: headers - - response_org_users = response.parsed_body.first - - expect(response_org_users["type_of"]).to eq("user") - - %w[id username name twitter_username github_username].each do |attr| - expect(response_org_users[attr]).to eq(org_user.public_send(attr)) + shared_examples "a lookup by ID or slug" do + it "returns 404 if the organization is not found" do + get "/api/organizations/invalid-id-or-username/users", headers: headers + expect(response).to have_http_status(:not_found) end - org_user_profile = org_user.profile - %w[summary location website_url].each do |attr| - expect(response_org_users[attr]).to eq(org_user_profile.public_send(attr)) + it "supports pagination" do + create(:organization_membership, user: create(:user), organization: organization) + + get api_organization_users_path(id_or_slug), params: { page: 1, per_page: 1 }, headers: headers + expect(response.parsed_body.length).to eq(1) + + get api_organization_users_path(id_or_slug), params: { page: 2, per_page: 1 }, headers: headers + expect(response.parsed_body.length).to eq(1) + + get api_organization_users_path(id_or_slug), params: { page: 3, per_page: 1 }, headers: headers + expect(response.parsed_body.length).to eq(0) end - expect(response_org_users["joined_at"]).to eq(org_user.created_at.strftime("%b %e, %Y")) - expect(response_org_users["profile_image"]).to eq(org_user.profile_image_url_for(length: 320)) + it "respects API_PER_PAGE_MAX limit set in ENV variable" do + allow(ApplicationConfig).to receive(:[]).and_return(nil) + allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://") + allow(ApplicationConfig).to receive(:[]).with("API_PER_PAGE_MAX").and_return(2) - expect(response_org_users.key?("email")).to be false + create(:organization_membership, user: create(:user), organization: organization) + create(:organization_membership, user: create(:user), organization: organization) + create(:organization_membership, user: create(:user), organization: organization) + + get api_organization_users_path(id_or_slug), params: { per_page: 10 } + expect(response.parsed_body.count).to eq(2) + end + + it "returns the correct json representation of the organizations users", :aggregate_failures do + get api_organization_users_path(id_or_slug), headers: headers + + response_org_users = response.parsed_body.first + + expect(response_org_users["type_of"]).to eq("user") + + %w[id username name twitter_username github_username].each do |attr| + expect(response_org_users[attr]).to eq(org_user.public_send(attr)) + end + + org_user_profile = org_user.profile + %w[summary location website_url].each do |attr| + expect(response_org_users[attr]).to eq(org_user_profile.public_send(attr)) + end + + expect(response_org_users["joined_at"]).to eq(org_user.created_at.strftime("%b %e, %Y")) + expect(response_org_users["profile_image"]).to eq(org_user.profile_image_url_for(length: 320)) + expect(response_org_users.key?("email")).to be false + end + end + + context "when looking up by the organization's id" do + let(:id_or_slug) { organization.id } + + it_behaves_like "a lookup by ID or slug" + end + + context "when looking up by the organization's username" do + let(:id_or_slug) { organization.username } + + it_behaves_like "a lookup by ID or slug" end end @@ -365,67 +378,81 @@ RSpec.describe "Api::V1::Organizations" do end end - describe "GET /api/organizations/:username/articles" do + describe "GET /api/organizations/:organization_id_or_slug/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", headers: headers - 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 }, - headers: headers - expect(response.parsed_body.length).to eq(1) - - get api_organization_articles_path(organization.username), - params: { page: 2, per_page: 1 }, - headers: headers - expect(response.parsed_body.length).to eq(1) - - get api_organization_articles_path(organization.username), - params: { page: 3, per_page: 1 }, - headers: headers - 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), headers: headers - 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)) + shared_examples "a lookup by ID or slug" do + it "returns 404 if the organization is not found" do + get "/api/organizations/invalid-id-or-username/articles", headers: headers + expect(response).to have_http_status(:not_found) end - expect(response_article["tag_list"]).to match_array(article.tag_list) + it "supports pagination" do + create(:article, organization: organization) - %w[name username twitter_username github_username].each do |attr| - expect(response_article["user"][attr]).to eq(org_user.public_send(attr)) + get api_organization_articles_path(id_or_slug), + params: { page: 1, per_page: 1 }, + headers: headers + expect(response.parsed_body.length).to eq(1) + + get api_organization_articles_path(id_or_slug), + params: { page: 2, per_page: 1 }, + headers: headers + expect(response.parsed_body.length).to eq(1) + + get api_organization_articles_path(id_or_slug), + params: { page: 3, per_page: 1 }, + headers: headers + expect(response.parsed_body.length).to eq(0) end - expect(response_article["user"]["website_url"]).to eq(org_user.profile.website_url) + it "returns the correct json representation of the organizations articles", :aggregate_failures do + get api_organization_articles_path(id_or_slug), headers: headers + response_article = response.parsed_body.first + expect(response_article["type_of"]).to eq("article") - %w[name username slug].each do |attr| - expect(response_article["organization"][attr]).to eq(organization.public_send(attr)) + %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].each do |attr| + expect(response_article["user"][attr]).to eq(org_user.public_send(attr)) + end + + expect(response_article["user"]["website_url"]).to eq(org_user.profile.website_url) + + %w[name username slug].each do |attr| + expect(response_article["organization"][attr]).to eq(organization.public_send(attr)) + end + end + + it "respects API_PER_PAGE_MAX limit set in ENV variable" do + allow(ApplicationConfig).to receive(:[]).and_return(nil) + allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://") + allow(ApplicationConfig).to receive(:[]).with("API_PER_PAGE_MAX").and_return(2) + + create_list(:article, 3, organization: organization) + + get api_organization_articles_path(id_or_slug), params: { per_page: 10 }, headers: headers + expect(response.parsed_body.count).to eq(2) end end - it "respects API_PER_PAGE_MAX limit set in ENV variable" do - allow(ApplicationConfig).to receive(:[]).and_return(nil) - allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://") - allow(ApplicationConfig).to receive(:[]).with("API_PER_PAGE_MAX").and_return(2) + context "when looking up by the organization's ID" do + let(:id_or_slug) { organization.id } - create_list(:article, 3, organization: organization) + it_behaves_like "a lookup by ID or slug" + end - get api_organization_articles_path(organization.username), params: { per_page: 10 }, headers: headers - expect(response.parsed_body.count).to eq(2) + context "when looking up by the organization's username" do + let(:id_or_slug) { organization.username } + + it_behaves_like "a lookup by ID or slug" end end end