pg_rails 7.0.8.pre.alpha.56 → 7.0.8.pre.alpha.58

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEngine
4
+ class EmailObserver
5
+ def self.delivered_email(message)
6
+ # content = get_content(message)
7
+ # subject = message.subject
8
+
9
+ # message.to puede ser un string o un array de strings
10
+ # to = [message.to].flatten.join(', ')
11
+
12
+ message_id = message.message_id
13
+ mailer = message.delivery_handler.to_s
14
+ status = get_status(message)
15
+ return if message['email'].blank?
16
+
17
+ # El objeto Email ya estaba creado. Agarro ese objeto y le actualizo las cosas
18
+ email = message['email'].unparsed_value
19
+
20
+ email.encoded_eml.attach({ io: StringIO.new(message.encoded), filename: "email-#{email.id}.eml" })
21
+
22
+ email.update_columns(message_id:, mailer:, status:) # rubocop:disable Rails/SkipsModelValidations
23
+ # else
24
+ # TODO: crear email
25
+ # pg_warn 'El mail no tenía objeto Email asociado, se creó uno on the fly.', :warn
26
+ # # No hay objeto Email, entonces le creo uno para que quede logueado.
27
+ # # TODO: el content puede ser TXT
28
+ # Email.create!(message_id: message.message_id, body_html: message.encoded,
29
+ # subject: subject, recipient: recipient,
30
+ # date: DateTime.now, mailer: message.delivery_handler.to_s, associated: associated(message),
31
+ # status: get_status(message), observations: get_observations(message))
32
+ end
33
+
34
+ def self.get_status(message)
35
+ message.perform_deliveries ? :sent : :blocked
36
+ end
37
+
38
+ # def self.get_html_or_text(message)
39
+ # message.body.parts.find { |p| p.content_type.match(/multipart/).present? }
40
+ # part = message.body.parts.find { |p| p.content_type.match(/html/).present? }
41
+ # part = message.body.parts.find { |p| p.content_type.match(/text/).present? } if part.nil?
42
+ # part = message if part.nil?
43
+ # part.body.raw_source
44
+ # end
45
+
46
+ # def self.associated(message)
47
+ # return if message['associated'].blank?
48
+
49
+ # message['associated'].unparsed_value
50
+ # end
51
+
52
+ # def self.get_observations(message)
53
+ # return if message['observations'].blank?
54
+
55
+ # message['observations'].unparsed_value
56
+ # end
57
+
58
+ # def self.get_content(message)
59
+ # multipart = message.body.parts.find { |p| p.content_type.match(/multipart/).present? }
60
+ # part = multipart.presence || message
61
+ # get_html_or_text(part)
62
+ # rescue StandardError => e
63
+ # pg_err e
64
+ # ''
65
+ # end
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ require 'fileutils'
2
+ require 'mailgun'
3
+ require 'active_support'
4
+ require 'active_support/core_ext/numeric/time'
5
+ require 'json'
6
+
7
+ module PgEngine
8
+ module Mailgun
9
+ class LogSync
10
+ def self.download # rubocop:disable Metrics/AbcSize
11
+ domain = ENV.fetch('MAILGUN_DOMAIN')
12
+
13
+ key = Rails.application.credentials.dig(:mailgun, :api_key)
14
+ mg_client = ::Mailgun::Client.new(key)
15
+ items = []
16
+ end_time = DateTime.now
17
+ start_time = DateTime.now - 3.days
18
+ range = 1.day
19
+ current = end_time - range
20
+ loop do
21
+ get = "#{domain}/events?begin=#{current.to_i}&end=#{(current + range).to_i}&limit=300"
22
+ result = mg_client.get(get)
23
+ result.to_h!
24
+ items.push(result.body['items'])
25
+ # puts "Current: #{current}. Items count: #{result.body['items'].length}"
26
+ current -= range
27
+
28
+ break if current < start_time
29
+ end
30
+
31
+ FileUtils.mkdir_p(dir)
32
+
33
+ File.write("#{dir}/#{domain}-#{Time.zone.now.strftime('%Y-%m-%d_%H.%M.%S')}.json", items.flatten.to_json)
34
+ end
35
+
36
+ def self.dir
37
+ @dir ||= if Rails.env.test?
38
+ File.expand_path 'tmp/mailgun_logs', Rails.root
39
+ else
40
+ File.expand_path 'log/mailgun_logs', Rails.root
41
+ end
42
+ end
43
+
44
+ def self.sync_redis
45
+ json = []
46
+ Dir["#{dir}/*.json"].each do |file|
47
+ json.push(*JSON.parse(File.read(file)))
48
+ end
49
+
50
+ json.each do |i|
51
+ # [
52
+ # Time.at(i["timestamp"]).strftime('%y-%m-%d %H:%M %z'),
53
+ # i["message"]["headers"]["message-id"],
54
+ # # i["message"]["headers"]["message-id"][3..15],
55
+ # i["event"],
56
+ # i["recipient"],
57
+ # i["message"]["headers"]["from"],
58
+ # i["message"]["headers"]["subject"],
59
+ # ]
60
+ message_id = i['message']['headers']['message-id']
61
+ email = Email.where(message_id:).first
62
+ if email
63
+ email.logs << i.to_json
64
+ else
65
+ pg_warn "No existe el mail con message_id = #{message_id}", :warn
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -4,6 +4,8 @@ require_relative 'pg_engine/engine'
4
4
  require_relative 'pg_engine/core_ext'
5
5
  require_relative 'pg_engine/error'
6
6
  require_relative 'pg_engine/configuracion'
7
+ require_relative 'pg_engine/email_observer'
8
+ require_relative 'pg_engine/mailgun/log_sync'
7
9
  require_relative 'pg_engine/route_helpers'
8
10
  require_relative 'pg_engine/utils/pg_logger'
9
11
  require_relative 'pg_engine/utils/pdf_preview_generator'
@@ -45,6 +47,8 @@ require 'sassc'
45
47
  require 'image_processing'
46
48
  require 'hashid/rails'
47
49
  require 'redis'
50
+ require 'kredis'
51
+ require 'mailgun-ruby'
48
52
 
49
53
  if Rails.env.local?
50
54
  require 'letter_opener'
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generado con pg_rails
4
+
5
+ require 'rails_helper'
6
+
7
+ # This spec was generated by rspec-rails when you ran the scaffold generator.
8
+ # It demonstrates how one might use RSpec to specify the controller code that
9
+ # was generated by Rails when you ran the scaffold generator.
10
+ #
11
+ # It assumes that the implementation code is generated by the rails scaffold
12
+ # generator. If you are using any extension libraries to generate different
13
+ # controller code, this generated spec may or may not pass.
14
+ #
15
+ # It only uses APIs available in rails and/or rspec-rails. There are a number
16
+ # of tools you can use to make these specs even more expressive, but we're
17
+ # sticking to rails and rspec-rails APIs to keep things simple and stable.
18
+ #
19
+ # Compared to earlier versions of this generator, there is very limited use of
20
+ # stubs and message expectations in this spec. Stubs are only used when there
21
+ # is no simpler way to get a handle on the object needed for the example.
22
+ # Message expectations are only used when there is no simpler way to specify
23
+ # that an instance is receiving a specific message.
24
+ #
25
+ # Also compared to earlier versions of this generator, there are no longer any
26
+ # expectations of assigns and templates rendered. These features have been
27
+ # removed from Rails core in Rails 5, but can be added back in via the
28
+ # `rails-controller-testing` gem.
29
+
30
+ RSpec.describe Admin::EmailsController do
31
+ render_views
32
+ # This should return the minimal set of attributes required to create a valid
33
+ # Email. As you add validations to Email, be sure to
34
+ # adjust the attributes here as well.
35
+ let(:valid_attributes) do
36
+ attributes_for(:email)
37
+ end
38
+
39
+ let(:invalid_attributes) do
40
+ {
41
+ from_address: nil
42
+ }
43
+ end
44
+
45
+ let(:logged_user) { create :user, :developer }
46
+
47
+ before do
48
+ sign_in logged_user if logged_user.present?
49
+ end
50
+
51
+ describe 'routing' do
52
+ it 'routes GET index correctly' do
53
+ route = { get: '/a/emails' }
54
+ expect(route).to route_to(controller: 'admin/emails', action: 'index')
55
+ end
56
+ end
57
+
58
+ describe 'GET #index' do
59
+ subject do
60
+ get :index, params: {}
61
+ end
62
+
63
+ before { create :email }
64
+
65
+ it 'returns a success response' do
66
+ subject
67
+ expect(response).to be_successful
68
+ end
69
+
70
+ context 'when user is not logged in' do
71
+ let(:logged_user) { nil }
72
+
73
+ it 'redirects to login path' do
74
+ subject
75
+ expect(response).to redirect_to(new_user_session_path)
76
+ end
77
+ end
78
+
79
+ context 'when se pide el excel' do
80
+ subject do
81
+ get :index, params: {}, format: 'xlsx'
82
+ end
83
+
84
+ it 'returns a success response' do
85
+ subject
86
+ expect(response).to be_successful
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'GET #show' do
92
+ it 'returns a success response' do
93
+ email = create(:email)
94
+ email.encoded_eml.attach({ io: StringIO.new(Faker::Lorem.sentence), filename: 'email.eml' })
95
+ get :show, params: { id: email.to_param }
96
+ expect(response).to be_successful
97
+ end
98
+ end
99
+
100
+ describe 'GET #new' do
101
+ it 'returns a success response' do
102
+ get :new, params: {}
103
+ expect(response).to be_successful
104
+ end
105
+ end
106
+
107
+ describe 'GET #edit' do
108
+ it 'returns a success response' do
109
+ email = create(:email)
110
+ get :edit, params: { id: email.to_param }
111
+ expect(response).to be_successful
112
+ end
113
+ end
114
+
115
+ describe 'POST #create' do
116
+ context 'with valid params' do
117
+ it 'creates a new Email' do
118
+ expect do
119
+ post :create, params: { email: valid_attributes }
120
+ end.to change(Email, :count).by(1)
121
+ end
122
+
123
+ it 'redirects to the created email' do
124
+ post :create, params: { email: valid_attributes }
125
+ expect(response).to redirect_to([:admin, Email.last])
126
+ end
127
+ end
128
+
129
+ context 'with invalid params' do
130
+ it 'returns a unprocessable_entity response' do
131
+ post :create, params: { email: invalid_attributes }
132
+ expect(response).to have_http_status(:unprocessable_entity)
133
+ end
134
+
135
+ it 'renders the new template' do
136
+ post :create, params: { email: invalid_attributes }
137
+ expect(response).to render_template(:new)
138
+ end
139
+ end
140
+ end
141
+
142
+ describe 'PUT #update' do
143
+ context 'with valid params' do
144
+ let(:new_attributes) do
145
+ attributes_for(:email)
146
+ end
147
+
148
+ it 'updates the requested email' do
149
+ email = create(:email)
150
+ put :update, params: { id: email.to_param, email: new_attributes }
151
+ email.reload
152
+ expect(email.from_address).to eq new_attributes[:from_address]
153
+ end
154
+
155
+ it 'redirects to the email' do
156
+ email = create(:email)
157
+ put :update, params: { id: email.to_param, email: valid_attributes }
158
+ expect(response).to redirect_to([:admin, email])
159
+ end
160
+ end
161
+
162
+ context 'with invalid params' do
163
+ it 'returns a unprocessable_entity response' do
164
+ email = create(:email)
165
+ put :update, params: { id: email.to_param, email: invalid_attributes }
166
+ expect(response).to have_http_status(:unprocessable_entity)
167
+ end
168
+
169
+ it 'renders the edit template' do
170
+ email = create(:email)
171
+ put :update, params: { id: email.to_param, email: invalid_attributes }
172
+ expect(response).to render_template(:edit)
173
+ end
174
+ end
175
+ end
176
+
177
+ describe 'DELETE #destroy' do
178
+ subject do
179
+ request.headers['Accept'] = 'text/vnd.turbo-stream.html,text/html'
180
+ delete :destroy, params: { id: email.to_param, redirect_to: redirect_url }
181
+ end
182
+
183
+ let!(:email) { create :email }
184
+ let(:redirect_url) { nil }
185
+
186
+ it 'destroys the requested email' do
187
+ expect { subject }.to change(Email, :count).by(-1)
188
+ end
189
+
190
+ it 'quita el elemento de la lista' do
191
+ subject
192
+ expect(response.body).to include('turbo-stream action="remove"')
193
+ end
194
+
195
+ context 'si hay redirect_to' do
196
+ let(:redirect_url) { admin_emails_url }
197
+
198
+ it 'redirects to the emails list' do
199
+ subject
200
+ expect(response).to redirect_to(admin_emails_url)
201
+ end
202
+ end
203
+ end
204
+ end
@@ -43,10 +43,10 @@ RSpec.describe Admin::UsersController do
43
43
  }
44
44
  end
45
45
 
46
- let(:logger_user) { create :user, :admin }
46
+ let(:logged_user) { create :user, :admin }
47
47
 
48
48
  before do
49
- sign_in logger_user
49
+ sign_in logged_user
50
50
  end
51
51
 
52
52
  describe 'GET #index' do
@@ -66,7 +66,7 @@ RSpec.describe Admin::UsersController do
66
66
 
67
67
  it do
68
68
  subject
69
- expect(assigns(:collection)).to eq [logger_user]
69
+ expect(assigns(:collection)).to eq [logged_user]
70
70
  end
71
71
  end
72
72
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generado con pg_rails
4
+
5
+ FactoryBot.define do
6
+ factory :email do
7
+ # accepted_at { "2024-05-06 16:41:06" }
8
+ # delivered_at { "2024-05-06 16:41:06" }
9
+ # opened_at { "2024-05-06 16:41:06" }
10
+ from_address { Faker::Internet.email }
11
+ from_name { Faker::Name.name }
12
+ reply_to { Faker::Internet.email }
13
+ to { Faker::Internet.email }
14
+ subject { Faker::Lorem.sentence }
15
+ body_input { Faker::Lorem.sentence }
16
+ # tags { Faker::Lorem.sentence }
17
+ # associated
18
+ # message_id { Faker::Lorem.sentence }
19
+ # mailer { Faker::Lorem.sentence }
20
+ # status_detail { Faker::Lorem.sentence }
21
+ # status { rand(1..9999) }
22
+
23
+ # trait :associated_existente do
24
+ # associated { nil }
25
+ # associated_id { Associated.pluck(:id).sample }
26
+ # end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails_helper'
2
+ require 'fileutils'
3
+
4
+ describe PgEngine::Mailgun::LogSync do
5
+ let(:instancia) { described_class }
6
+
7
+ describe '#download',
8
+ vcr: { cassette_name: 'mailgun/log_sync_download',
9
+ match_requests_on: %i[method host] } do
10
+ subject do
11
+ instancia.download
12
+ instancia.sync_redis
13
+ end
14
+
15
+ after do
16
+ Dir["#{instancia.dir}/*.json"].each { |file| File.delete(file) }
17
+ end
18
+
19
+ let!(:email) { create :email, message_id: '66393f1bc7d4_47a5108ec1628f@notebook.mail' }
20
+
21
+ it do
22
+ expect { subject }.to change { email.logs.to_a.length }.from(0).to(3)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generado con pg_rails
4
+
5
+ require 'rails_helper'
6
+
7
+ RSpec.describe Email do
8
+ let(:email) { create(:email) }
9
+
10
+ it 'se persiste' do
11
+ expect(email).to be_persisted
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ html
2
+ body style="font-family: sans-serif"
3
+ = yield
4
+
5
+ footer style="margin-top: 2em;"
6
+ - if @footer_href.present?
7
+ = link_to @footer_href, rel: 'noreferrer', target: :_blank do
8
+ - if @footer_image_src.present?
9
+ = image_tag @footer_image_src, alt: @footer_image_alt
10
+ - else
11
+ = @footer_image_alt
@@ -0,0 +1,4 @@
1
+ = yield
2
+
3
+ | PgRails
4
+ | https://example.com.ar
@@ -0,0 +1,6 @@
1
+ RSpec.configure do |config|
2
+ config.before(:each) do
3
+ Kredis.redis
4
+ Kredis.clear_all
5
+ end
6
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.0.8-alpha.56'
4
+ VERSION = '7.0.8-alpha.58'
5
5
  end
@@ -170,7 +170,7 @@ RSpec.describe <%= controller_class_name %>Controller do
170
170
  <% else -%>
171
171
  post :create, params: { <%= nombre_tabla_completo_singular %>: valid_attributes }
172
172
  <% end -%>
173
- expect(response).to redirect_to(<%= class_name %>.last.decorate.target_object)
173
+ expect(response).to redirect_to([:<%= ns_prefix.first %>, <%= class_name %>.last])
174
174
  end
175
175
  end
176
176
  <% if attributes.any? { |at| at.required? } -%>
@@ -218,7 +218,7 @@ RSpec.describe <%= controller_class_name %>Controller do
218
218
  <% else -%>
219
219
  put :update, params: { id: <%= file_name %>.to_param, <%= nombre_tabla_completo_singular %>: valid_attributes }
220
220
  <% end -%>
221
- expect(response).to redirect_to(<%= file_name %>.decorate.target_object)
221
+ expect(response).to redirect_to([:<%= ns_prefix.first %>, <%= file_name %>.decorate.target_object])
222
222
  end
223
223
  end
224
224
  <% if attributes.any? { |at| at.required? } -%>