Export comments (#1471)
* Use name attribute * Export comments * Update copy * Fix time-dependent test * Remove reactions_count from the export * Only select needed attributes from the DB * Add the path of the commentable to the export Comments are attached to something but from the export that is not easily identifiable. We add the "commentable_path" to give some context.
This commit is contained in:
parent
f1e7f12e1d
commit
a0c9576bec
8 changed files with 184 additions and 59 deletions
|
|
@ -11,9 +11,8 @@ module Exporter
|
|||
def export(slug: nil)
|
||||
articles = user.articles
|
||||
articles = articles.where(slug: slug) if slug.present?
|
||||
json_articles = jsonify_articles(articles)
|
||||
|
||||
{ "articles.json" => json_articles }
|
||||
{ "#{name}.json" => jsonify(articles) }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -41,7 +40,6 @@ module Exporter
|
|||
published
|
||||
published_at
|
||||
published_from_feed
|
||||
reactions_count
|
||||
show_comments
|
||||
slug
|
||||
social_image
|
||||
|
|
@ -54,13 +52,14 @@ module Exporter
|
|||
]
|
||||
end
|
||||
|
||||
def jsonify_articles(articles)
|
||||
def jsonify(articles)
|
||||
articles_to_jsonify = []
|
||||
# load articles in batches, we don't want to hog the DB
|
||||
# if a user has lots and lots of articles
|
||||
articles.find_each do |article|
|
||||
|
||||
# load articles in batches and select only needed attributes
|
||||
articles.select([:id] + allowed_attributes).find_each do |article|
|
||||
articles_to_jsonify << article
|
||||
end
|
||||
|
||||
articles_to_jsonify.to_json(only: allowed_attributes)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
49
app/services/exporter/comments.rb
Normal file
49
app/services/exporter/comments.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
module Exporter
|
||||
class Comments
|
||||
attr_reader :name
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user)
|
||||
@name = :comments
|
||||
@user = user
|
||||
end
|
||||
|
||||
def export(id_code: nil)
|
||||
comments = user.comments
|
||||
comments = comments.where(id_code: id_code) if id_code.present?
|
||||
|
||||
{ "#{name}.json" => jsonify(comments) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_attributes
|
||||
%i[
|
||||
body_markdown
|
||||
created_at
|
||||
deleted
|
||||
edited
|
||||
edited_at
|
||||
id_code
|
||||
markdown_character_count
|
||||
positive_reactions_count
|
||||
processed_html
|
||||
receive_notifications
|
||||
]
|
||||
end
|
||||
|
||||
def jsonify(comments)
|
||||
comments_to_jsonify = []
|
||||
|
||||
# the commentable polymorphic attributes are added to the select for the "includes" to work
|
||||
attributes_to_select = %i[id commentable_id commentable_type] + allowed_attributes
|
||||
comments.includes(:commentable).select(attributes_to_select).find_each do |comment|
|
||||
# merge final json with the path of the commentable
|
||||
comments_to_jsonify << comment.as_json(only: allowed_attributes).
|
||||
merge(commentable_path: comment.commentable&.path)
|
||||
end
|
||||
|
||||
comments_to_jsonify.to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,6 +6,7 @@ module Exporter
|
|||
|
||||
EXPORTERS = [
|
||||
Articles,
|
||||
Comments,
|
||||
].freeze
|
||||
|
||||
def initialize(user)
|
||||
|
|
|
|||
|
|
@ -86,25 +86,25 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<h2>Export posts</h2>
|
||||
<h2>Export content</h2>
|
||||
|
||||
<% if @user.export_requested? %>
|
||||
<h4 style="font-weight:400">
|
||||
You have recently requested an export of your data.
|
||||
You have recently requested an export of your content.
|
||||
Please check your email.
|
||||
</h4>
|
||||
<% else %>
|
||||
<h4 style="font-weight:400">
|
||||
You can request an export of all your data.
|
||||
Currently we only support the export of posts.
|
||||
It will be emailed to your inbox.
|
||||
You can request an export of all your content.
|
||||
Currently we only support the export of your posts and comments.
|
||||
They will be emailed to your inbox.
|
||||
</h4>
|
||||
|
||||
<%= form_for(@user) do |f| %>
|
||||
<div class="checkbox-field">
|
||||
<div class="sub-field">
|
||||
<%= f.check_box :export_requested %>
|
||||
<%= f.label :export_requested, "Request an export of your data" %>
|
||||
<%= f.label :export_requested, "Request an export of your content" %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ RSpec.describe "Registrations", type: :request do
|
|||
context "when logged in" do
|
||||
it "redirects to /dashboard" do
|
||||
login_as user
|
||||
get "/enter"
|
||||
expect(response).to redirect_to("/dashboard?signed-in-already&t=#{Time.current.to_i}")
|
||||
|
||||
Timecop.freeze(Time.current) do
|
||||
get "/enter"
|
||||
expect(response).to redirect_to("/dashboard?signed-in-already&t=#{Time.current.to_i}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ RSpec.describe Exporter::Articles do
|
|||
published
|
||||
published_at
|
||||
published_from_feed
|
||||
reactions_count
|
||||
show_comments
|
||||
slug
|
||||
social_image
|
||||
|
|
|
|||
114
spec/services/exporter/comments_spec.rb
Normal file
114
spec/services/exporter/comments_spec.rb
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Exporter::Comments do
|
||||
let(:user) { create(:user) }
|
||||
let(:article) { create(:article) }
|
||||
let(:podcast_episode) { create(:podcast_episode) }
|
||||
let(:comment) { create(:comment, user: user, commentable: article) }
|
||||
let(:podcast_episode_comment) { create(:comment, user: user, commentable: podcast_episode) }
|
||||
let(:other_user) { create(:user) }
|
||||
let(:other_user_comment) { create(:comment, user: other_user, commentable: article) }
|
||||
|
||||
def valid_instance(user)
|
||||
described_class.new(user)
|
||||
end
|
||||
|
||||
def expected_fields
|
||||
%w[
|
||||
body_markdown
|
||||
created_at
|
||||
deleted
|
||||
edited
|
||||
edited_at
|
||||
id_code
|
||||
markdown_character_count
|
||||
positive_reactions_count
|
||||
processed_html
|
||||
receive_notifications
|
||||
commentable_path
|
||||
]
|
||||
end
|
||||
|
||||
def load_comments(data)
|
||||
JSON.parse(data["comments.json"])
|
||||
end
|
||||
|
||||
describe "#initialize" do
|
||||
it "accepts a user" do
|
||||
exporter = valid_instance(user)
|
||||
expect(exporter.user).to be(user)
|
||||
end
|
||||
|
||||
it "names itself comments" do
|
||||
exporter = valid_instance(user)
|
||||
expect(exporter.name).to eq(:comments)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#export" do
|
||||
context "when id code is unknown" do
|
||||
it "returns no comments if the id code is not found" do
|
||||
exporter = valid_instance(user)
|
||||
result = exporter.export(id_code: "not found")
|
||||
comments = load_comments(result)
|
||||
expect(comments).to be_empty
|
||||
end
|
||||
|
||||
it "no comments if id code belongs to another user" do
|
||||
exporter = valid_instance(user)
|
||||
result = exporter.export(id_code: other_user_comment.id_code)
|
||||
comments = load_comments(result)
|
||||
expect(comments).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "when id code is known" do
|
||||
it "returns the comment" do
|
||||
exporter = valid_instance(user)
|
||||
result = exporter.export(id_code: comment.id_code)
|
||||
comments = load_comments(result)
|
||||
expect(comments.length).to eq(1)
|
||||
end
|
||||
|
||||
it "returns only expected fields for the comment" do
|
||||
exporter = valid_instance(user)
|
||||
result = exporter.export(id_code: comment.id_code)
|
||||
comments = load_comments(result)
|
||||
expect(comments.first.keys).to eq(expected_fields)
|
||||
end
|
||||
end
|
||||
|
||||
context "when all comments are requested" do
|
||||
it "returns all the comments as json" do
|
||||
exporter = valid_instance(comment.user)
|
||||
result = exporter.export
|
||||
comments = load_comments(result)
|
||||
user.reload
|
||||
expect(comments.length).to eq(user.comments.size)
|
||||
end
|
||||
|
||||
it "returns only expected fields for the comment" do
|
||||
exporter = valid_instance(comment.user)
|
||||
result = exporter.export
|
||||
comments = load_comments(result)
|
||||
expect(comments.first.keys).to eq(expected_fields)
|
||||
end
|
||||
end
|
||||
|
||||
describe "commentable path" do
|
||||
it "contains the path of the article" do
|
||||
exporter = valid_instance(user)
|
||||
result = exporter.export(id_code: comment.id_code)
|
||||
comments = load_comments(result)
|
||||
expect(comments.first["commentable_path"]).to eq(article.path)
|
||||
end
|
||||
|
||||
it "contains the path of the podcast episode" do
|
||||
exporter = valid_instance(user)
|
||||
result = exporter.export(id_code: podcast_episode_comment.id_code)
|
||||
comments = load_comments(result)
|
||||
expect(comments.first["commentable_path"]).to eq(podcast_episode.path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -31,49 +31,9 @@ RSpec.describe Exporter::Service do
|
|||
exports
|
||||
end
|
||||
|
||||
def expected_fields
|
||||
[
|
||||
"body_html",
|
||||
"body_markdown",
|
||||
"cached_tag_list",
|
||||
"cached_user_name",
|
||||
"cached_user_username",
|
||||
"canonical_url",
|
||||
"comments_count",
|
||||
"created_at",
|
||||
"crossposted_at",
|
||||
"description",
|
||||
"edited_at",
|
||||
"feed_source_url",
|
||||
"language",
|
||||
"last_comment_at",
|
||||
"lat",
|
||||
"long",
|
||||
"main_image",
|
||||
"main_image_background_hex_color",
|
||||
"path",
|
||||
"positive_reactions_count",
|
||||
"processed_html",
|
||||
"published",
|
||||
"published_at",
|
||||
"published_from_feed",
|
||||
"reactions_count",
|
||||
"show_comments",
|
||||
"slug",
|
||||
"social_image",
|
||||
"title",
|
||||
"updated_at",
|
||||
"video",
|
||||
"video_closed_caption_track_url",
|
||||
"video_code",
|
||||
"video_source_url",
|
||||
"video_thumbnail_url",
|
||||
]
|
||||
end
|
||||
|
||||
describe "EXPORTERS" do
|
||||
it "is a list of supported exporters" do
|
||||
expect(described_class::EXPORTERS).to eq([Exporter::Articles])
|
||||
expect(described_class::EXPORTERS).to eq([Exporter::Articles, Exporter::Comments])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -89,14 +49,14 @@ RSpec.describe Exporter::Service do
|
|||
service = valid_instance(article.user)
|
||||
zipped_exports = service.export
|
||||
exports = extract_zipped_exports(zipped_exports)
|
||||
expect(exports.keys).to eq(["articles.json"])
|
||||
expect(exports.keys).to eq(["articles.json", "comments.json"])
|
||||
end
|
||||
|
||||
it "passes configuration to an exporter" do
|
||||
service = valid_instance(article.user)
|
||||
zipped_exports = service.export(config: { articles: { slug: article.slug } })
|
||||
exports = extract_zipped_exports(zipped_exports)
|
||||
expect(exports.length).to eq(1)
|
||||
expect(exports.length).to eq(described_class::EXPORTERS.size)
|
||||
end
|
||||
|
||||
context "when emailing the user" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue