Skip to content

Commit

Permalink
[WIP] Ticket CV2-5067: Refactoring code so it is not media-specific a…
Browse files Browse the repository at this point in the history
…nd sending email
  • Loading branch information
caiosba committed Aug 22, 2024
1 parent d46c869 commit 9fc4288
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 21 deletions.
9 changes: 5 additions & 4 deletions app/graph/mutations/export_mutations.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
module ExportMutations
class ExportList < Mutations::BaseMutation
argument :query, GraphQL::Types::String, required: true
argument :type, GraphQL::Types::String, required: true # 'media', 'feed' or 'article'

field :success, GraphQL::Types::Boolean, null: true

def resolve(query:)
def resolve(query:, type:)
ability = context[:ability]
team = Team.find_if_can(Team.current.id, ability)
if ability.cannot?(:export_list, team)
{ success: false }
else
search = CheckSearch.new(query, nil, team.id)
if search.number_of_results > CheckConfig.get(:export_csv_maximum_number_of_results, 10000, :integer)
export = ListExport.new(type.to_sym, query, team.id)
if export.number_of_rows > CheckConfig.get(:export_csv_maximum_number_of_results, 10000, :integer)
{ success: false }
else
CheckSearch.delay.export_to_csv(query, team.id)
export.generate_csv_and_send_email_in_background(User.current)
{ success: true }
end
end
Expand Down
12 changes: 12 additions & 0 deletions app/mailers/export_list_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class ExportListMailer < ApplicationMailer
layout nil

def send_csv(csv_file_url, user)
@csv_file_url = csv_file_url
@user = user
@expire_in = CheckConfig.get('export_csv_expire', 7.days.to_i, :integer) / (60 * 60 * 24)
subject = I18n.t('mails_notifications.export_list.subject')
Rails.logger.info "Sending export e-mail to #{@user.email}"
mail(to: @user.email, email_type: 'export_list', subject: subject)
end
end
124 changes: 124 additions & 0 deletions app/views/export_list_mailer/send_csv.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<%= render "shared/header" %>

<style>
@media only screen and (max-width: 481px) {
.notify-title a {
display: block !important;
width: auto !important;
}
}


@media only screen and (max-width: 601px) {
.notify-title__content {
padding: 0 30px;
}
}


@media only screen and (max-width: 481px) {
.notify-title__content {
padding: 0 !important;
}
}
</style>

<!--~~// Notify-title module start \\~~-->
<!--————————————————————————————-->
<div class="notify-title" style="text-align: <%= @direction[:align] %> !important; direction: <%= @direction[:dir] %>">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; font-size: 44px; line-height: 1; text-decoration: none !important;">&nbsp;</td>
</tr>
</table>
<div class="container " width="600" style="margin: 0 auto; text-align: <%= @direction[:align] %>; width: 600px;">
<table class="container__table" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; margin: 0 auto; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed;">
<tr>
<th class="col-1" style="font-weight: normal; mso-line-height-rule: exactly; padding: 0;" width="600px" valign="top" align="<%= @direction[:align] %>">
<div class="notify-title__content" style="text-align:<%= @direction[:align] %>;">
<div class="h3 notify-title__proj" style="font-size: 21px; letter-spacing: -0.2px; line-height: 29px;">
<%= I18n.t(:"mails_notifications.export_list.hello", name: @user.name) %>
</div>
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; font-size: 13px; line-height: 1; text-decoration: none !important;">&nbsp;</td>
</tr>
</table>
<div class="h1 notify-title__header" style="font-size: 40px; font-weight: bold; letter-spacing: -0.8px; line-height: 40px;">
<%= I18n.t("mails_notifications.export_list.subject") %>
</div>
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; font-size: 12px; line-height: 1; text-decoration: none !important;">&nbsp;</td>
</tr>
</table>
<div class="text-gray" style="color: #757575 !important;">
<div class="h3" style="font-size: 21px; letter-spacing: -0.2px; line-height: 30px;">
<%= I18n.t(:"mails_notifications.export_list.body", days: @expire_in) %>
</div>
</div>
</div>

<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; font-size: 28px; line-height: 1; text-decoration: none !important;">&nbsp;</td>
</tr>
</table>

<!--————————————————————————————-->
<!--~~\\ Notify-title module end //~~-->

<div class="container wide" width="600" style="margin: 0 auto; text-align: <%= @direction[:align] %>; width: 600px;">

<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="notify-title__button text-white" style="background: #2E77FC; border-collapse: collapse; border-radius: 4px; color: #f1f1f1 !important; display: inline-block; padding-bottom: 15px; padding-left: 25px; padding-right: 23px; padding-top: 15px;"
width="auto">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; padding-right: 14px;">
<span class="span" style="font-size: 17px; font-weight: bold; line-height: 20px;">
<%=
link_to(I18n.t('mails_notifications.export_list.button_label'),
@csv_file_url,
:style => "text-decoration: none !important;color: #fff !important;"
)
%>
</span>
</td>
<td style="border-collapse: collapse;" align="right">
<%= image_tag("https://images.ctfassets.net/g118h5yoccvd/#{@direction[:arrow]}", width: "7", alt: "arrow-icon", style: "-ms-interpolation-mode: bicubic; border: 0 none; height: auto; line-height: 100%; outline: none; text-decoration: none;") %>
</td>
</tr>
</table>
</td>
</tr>
</table>

<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; font-size: 23px; line-height: 1; text-decoration: none !important;">&nbsp;</td>
</tr>
</table>
</div>
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="border-collapse: collapse; font-size: 23px; line-height: 1; text-decoration: none !important;">&nbsp;</td>
</tr>
</table>
<style>
@media only screen and (max-width: 601px) {
th.footer {
padding: 0 30px !important;
}
}


@media only screen and (max-width: 481px) {
th.footer {
padding: 0 !important;
}
}
</style>

<%= render "shared/footer" %>
12 changes: 12 additions & 0 deletions app/views/export_list_mailer/send_csv.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%= I18n.t('mails_notifications.export_list.hello', name: @user.name) %>

<%= I18n.t('mails_notifications.export_list.subject') %>

<%= I18n.t('mails_notifications.export_list.body', days: @expire_in ) %>

<%= I18n.t('mails_notifications.export_list.button_label') %>: <%= @csv_file_url %>

...

<%= strip_tags I18n.t("mails_notifications.copyright_html", app_name: CheckConfig.get('app_name')) %>
https://meedan.com
2 changes: 1 addition & 1 deletion config/initializers/plugins.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Load classes on boot, in production, that otherwise wouldn't be auto-loaded by default
CcDeville && Bot::Keep && Workflow::Workflow.workflows && CheckS3 && Bot::Tagger && Bot::Fetch && Bot::Smooch && Bot::Slack && Bot::Alegre && CheckChannels && RssFeed && UrlRewriter && ClusterTeam
CcDeville && Bot::Keep && Workflow::Workflow.workflows && CheckS3 && Bot::Tagger && Bot::Fetch && Bot::Smooch && Bot::Slack && Bot::Alegre && CheckChannels && RssFeed && UrlRewriter && ClusterTeam && ListExport
5 changes: 5 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,11 @@ en:
constitutes acceptance of our updated Terms of Service.
term_button: Terms of Service
more_info: This is a one-time required legal notice sent to all Check users, even those who have unsubscribed by optional announcements.
export_list:
subject: Your Check data export is ready
hello: Hello %{name}
body: Your data export is ready. Click on the button below to download it. Please note that the link is valid only for %{days} days.
button_label: Download export
mail_security:
device_subject: 'Security alert: New login to %{app_name} from %{browser} on %{platform}'
ip_subject: 'Security alert: New or unusual %{app_name} login'
Expand Down
4 changes: 2 additions & 2 deletions lib/check_s3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ def self.delete(*paths)
client.delete_objects(bucket: CheckConfig.get('storage_bucket'), delete: { objects: objects })
end

def self.write_presigned(path, content_type, content)
def self.write_presigned(path, content_type, content, expires_in)
self.write(path, content_type, content)
bucket = CheckConfig.get('storage_bucket')
client = Aws::S3::Client.new
s3 = Aws::S3::Resource.new(client: client)
obj = s3.bucket(bucket).object(path)
obj.presigned_url(:get, expires_in: CheckConfig.get('export_csv_expire', 7.days.to_i, :integer))
obj.presigned_url(:get, expires_in: expires_in)
end
end
13 changes: 2 additions & 11 deletions lib/check_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def medias_get_search_result(query)
@options['es_id'] ? $repository.find([@options['es_id']]).compact : $repository.search(query: query, collapse: collapse, sort: sort, size: @options['eslimit'], from: @options['esoffset']).results
end

def self.export_to_csv(query, team_id)
def self.get_exported_data(query, team_id)
team = Team.find(team_id)
search = CheckSearch.new(query, nil, team_id)

Expand Down Expand Up @@ -369,16 +369,7 @@ def self.export_to_csv(query, team_id)
end
data << row
end

# Convert to CSV
csv_string = CSV.generate do |csv|
data.each do |row|
csv << row
end
end

# Save to S3
CheckS3.write_presigned("export/item/#{team.slug}/#{Time.now.to_i}/#{Digest::MD5.hexdigest(query)}.csv", 'text/csv', csv_string)
data
end

private
Expand Down
49 changes: 49 additions & 0 deletions lib/list_export.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class ListExport
TYPES = [:article, :feed, :media]

def initialize(type, query, team_id)
@type = type
@query = query
@team_id = team_id
raise "Invalid export type '#{type}'. Should be one of: #{TYPES}" unless TYPES.include?(type)
end

def number_of_rows
case @type
when :media
CheckSearch.new(@query, nil, @team_id).number_of_results
end
end

def generate_csv_and_send_email_in_background(user)
ListExport.delay.generate_csv_and_send_email(self, user.id)
end

def generate_csv_and_send_email(user)
# Convert to CSV
csv_string = CSV.generate do |csv|
self.export_data.each do |row|
csv << row
end
end

# Save to S3
csv_file_url = CheckS3.write_presigned("export/#{@type}/#{@team_id}/#{Time.now.to_i}/#{Digest::MD5.hexdigest(@query)}.csv", 'text/csv', csv_string, CheckConfig.get('export_csv_expire', 7.days.to_i, :integer))

# Send to e-mail
ExportListMailer.delay.send_csv(csv_file_url, user)
end

def self.generate_csv_and_send_email(export, user_id)
export.generate_csv_and_send_email(User.find(user_id))
end

private

def export_data
case @type
when :media
CheckSearch.get_exported_data(@query, @team_id)
end
end
end
6 changes: 3 additions & 3 deletions test/controllers/graphql_controller_11_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def teardown
create_team_user team: t, user: u, role: 'admin'
authenticate_with_user(u)

query = "mutation { exportList(input: { query: \"{}\" }) { success } }"
query = "mutation { exportList(input: { query: \"{}\", type: \"media\" }) { success } }"
post :create, params: { query: query, team: t.slug }
assert_response :success
assert JSON.parse(@response.body)['data']['exportList']['success']
Expand All @@ -178,7 +178,7 @@ def teardown
create_team_user team: t, user: u, role: 'editor'
authenticate_with_user(u)

query = "mutation { exportList(input: { query: \"{}\" }) { success } }"
query = "mutation { exportList(input: { query: \"{}\", type: \"media\" }) { success } }"
post :create, params: { query: query, team: t.slug }
assert_response :success
assert !JSON.parse(@response.body)['data']['exportList']['success']
Expand All @@ -191,7 +191,7 @@ def teardown
authenticate_with_user(u)

stub_configs({ 'export_csv_maximum_number_of_results' => -1 }) do
query = "mutation { exportList(input: { query: \"{}\" }) { success } }"
query = "mutation { exportList(input: { query: \"{}\", type: \"media\" }) { success } }"
post :create, params: { query: query, team: t.slug }
assert_response :success
assert !JSON.parse(@response.body)['data']['exportList']['success']
Expand Down

0 comments on commit 9fc4288

Please sign in to comment.