From 53d3091f7391349809728109654d46956f4340fd Mon Sep 17 00:00:00 2001 From: Andy Zhao Date: Mon, 8 Apr 2019 19:57:13 -0400 Subject: [PATCH] Add single article functionality to articles analytics (#2339) * Add specs for single article functionality * Add single article functionality * Use article_id instead of single_article_id * Add published: true when querying for one article * Use article_id in specs oops --- .../api/v0/analytics_controller.rb | 12 +++---- app/services/analytics_service.rb | 14 ++++++-- .../analytics/analytics_service_spec.rb | 6 ++++ spec/support/api_analytics_shared_examples.rb | 33 +++++++++++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/v0/analytics_controller.rb b/app/controllers/api/v0/analytics_controller.rb index 23b59ef83..d1f09413c 100644 --- a/app/controllers/api/v0/analytics_controller.rb +++ b/app/controllers/api/v0/analytics_controller.rb @@ -11,9 +11,9 @@ module Api org = Organization.find_by(id: params[:organization_id]) raise UnauthorizedError unless org && belongs_to_org?(user, org) - AnalyticsService.new(org).totals + AnalyticsService.new(org, article_id: params[:article_id]).totals else - AnalyticsService.new(user).totals + AnalyticsService.new(user, article_id: params[:article_id]).totals end render json: data.to_json end @@ -28,9 +28,9 @@ module Api org = Organization.find_by(id: params[:organization_id]) raise UnauthorizedError unless org && belongs_to_org?(user, org) - AnalyticsService.new(org, start_date: params[:start], end_date: params[:end]).stats_grouped_by_day + AnalyticsService.new(org, start_date: params[:start], end_date: params[:end], article_id: params[:article_id]).stats_grouped_by_day else - AnalyticsService.new(user, start_date: params[:start], end_date: params[:end]).stats_grouped_by_day + AnalyticsService.new(user, start_date: params[:start], end_date: params[:end], article_id: params[:article_id]).stats_grouped_by_day end render json: data.to_json end @@ -42,9 +42,9 @@ module Api org = Organization.find_by(id: params[:organization_id]) raise UnauthorizedError unless org && belongs_to_org?(user, org) - AnalyticsService.new(org, start_date: 1.day.ago).stats_grouped_by_day + AnalyticsService.new(org, start_date: 1.day.ago, article_id: params[:article_id]).stats_grouped_by_day else - AnalyticsService.new(user, start_date: 1.day.ago).stats_grouped_by_day + AnalyticsService.new(user, start_date: 1.day.ago, article_id: params[:article_id]).stats_grouped_by_day end render json: data.to_json end diff --git a/app/services/analytics_service.rb b/app/services/analytics_service.rb index 85107b212..1ad53fcff 100644 --- a/app/services/analytics_service.rb +++ b/app/services/analytics_service.rb @@ -1,6 +1,7 @@ class AnalyticsService - def initialize(user_or_org, start_date: "", end_date: "") + def initialize(user_or_org, start_date: "", end_date: "", article_id: nil) @user_or_org = user_or_org + @article_id = article_id @start_date = Time.zone.parse(start_date.to_s)&.beginning_of_day @end_date = Time.zone.parse(end_date.to_s)&.end_of_day || Time.current.end_of_day @@ -71,8 +72,15 @@ class AnalyticsService attr_reader :user_or_org, :start_date, :end_date, :article_data, :reaction_data, :comment_data, :follow_data, :page_view_data def load_data - @article_data = Article.where("#{user_or_org.class.name.downcase}_id" => user_or_org.id, published: true) - article_ids = @article_data.pluck(:id) + if @article_id + @article_data = Article.where(id: @article_id, published: true, "#{user_or_org.class.name.downcase}_id" => user_or_org.id) + raise UnauthorizedError if @article_data.blank? + + article_ids = @article_id + else + @article_data = Article.where("#{user_or_org.class.name.downcase}_id" => user_or_org.id, published: true) + article_ids = @article_data.pluck(:id) + end if @start_date && @end_date @reaction_data = Reaction.where(reactable_id: article_ids, reactable_type: "Article"). diff --git a/spec/services/analytics/analytics_service_spec.rb b/spec/services/analytics/analytics_service_spec.rb index b913a59b2..94ea4d92b 100644 --- a/spec/services/analytics/analytics_service_spec.rb +++ b/spec/services/analytics/analytics_service_spec.rb @@ -2,6 +2,8 @@ require "rails_helper" RSpec.describe AnalyticsService, type: :service do let(:user) { create(:user) } + let(:second_user) { create(:user) } + let(:article) { create(:article, user: second_user) } let(:organization) { create(:organization) } describe "initialization" do @@ -12,6 +14,10 @@ RSpec.describe AnalyticsService, type: :service do it "raises an error if end date is invalid" do expect(-> { described_class.new(user, end_date: "2000-") }).to raise_error(ArgumentError) end + + it "raises an error if an article id is invalid" do + expect(-> { described_class.new(user, article_id: article.id) }).to raise_error(UnauthorizedError) + end end describe "#totals" do diff --git a/spec/support/api_analytics_shared_examples.rb b/spec/support/api_analytics_shared_examples.rb index 69b73372c..2e71c850e 100644 --- a/spec/support/api_analytics_shared_examples.rb +++ b/spec/support/api_analytics_shared_examples.rb @@ -6,6 +6,9 @@ RSpec.shared_examples "GET /api/analytics/:endpoint authorization examples" do | let(:pro_api_token) { create(:api_secret, user: pro_user) } let(:pro_org_member) { create(:user, :pro, :org_member) } let(:org_member_token) { create(:api_secret, user: pro_org_member) } + let(:article) { create(:article, user: user) } + let(:pro_user_article) { create(:article, user: pro_user) } + let(:pro_org_article) { create(:article, user: pro_user, organization: org) } context "when an invalid token is given" do before { get "/api/analytics/#{endpoint}?#{params}", headers: { "api-key" => "abadskajdlsak" } } @@ -70,4 +73,34 @@ RSpec.shared_examples "GET /api/analytics/:endpoint authorization examples" do | expect(response.status).to eq 401 end end + + context "when attempting to view someone else's article analytics" do + it "responds with status 401 unauthorized" do + get "/api/analytics/#{endpoint}?article_id=#{article.id}#{params}" + expect(response.status).to eq 401 + end + end + + context "when viewing as current user" do + it "responds with status 200 OK" do + sign_in pro_user + get "/api/analytics/#{endpoint}?#{params}" + expect(response.status).to eq 200 + end + end + + context "when viewing your own single article's analytics" do + it "responds with status 200 OK" do + sign_in pro_user + get "/api/analytics/#{endpoint}?article_id=#{pro_user_article.id}#{params}" + expect(response.status).to eq 200 + end + end + + context "when viewing your own organizaiton's single article's analytics" do + it "responds with status 200 OK" do + sign_in pro_org_member + get "/api/analytics/#{endpoint}?article_id=#{pro_org_article.id}#{params}" + end + end end