pg_rails 7.0.8.pre.alpha.56 → 7.0.8.pre.alpha.57

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,66 @@
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
+ content_eml = message.encoded
16
+ return if message['email'].blank?
17
+
18
+ # El objeto Email ya estaba creado. Agarro ese objeto y le actualizo las cosas
19
+ email = message['email'].unparsed_value
20
+
21
+ email.update_columns(message_id:, mailer:, status:, content_eml:) # rubocop:disable Rails/SkipsModelValidations
22
+ # else
23
+ # TODO: crear email
24
+ # pg_warn 'El mail no tenía objeto Email asociado, se creó uno on the fly.', :warn
25
+ # # No hay objeto Email, entonces le creo uno para que quede logueado.
26
+ # # TODO: el content puede ser TXT
27
+ # Email.create!(message_id: message.message_id, body_html: message.encoded,
28
+ # subject: subject, recipient: recipient,
29
+ # date: DateTime.now, mailer: message.delivery_handler.to_s, associated: associated(message),
30
+ # status: get_status(message), observations: get_observations(message))
31
+ end
32
+
33
+ def self.get_status(message)
34
+ message.perform_deliveries ? :sent : :blocked
35
+ end
36
+
37
+ # def self.get_html_or_text(message)
38
+ # message.body.parts.find { |p| p.content_type.match(/multipart/).present? }
39
+ # part = message.body.parts.find { |p| p.content_type.match(/html/).present? }
40
+ # part = message.body.parts.find { |p| p.content_type.match(/text/).present? } if part.nil?
41
+ # part = message if part.nil?
42
+ # part.body.raw_source
43
+ # end
44
+
45
+ # def self.associated(message)
46
+ # return if message['associated'].blank?
47
+
48
+ # message['associated'].unparsed_value
49
+ # end
50
+
51
+ # def self.get_observations(message)
52
+ # return if message['observations'].blank?
53
+
54
+ # message['observations'].unparsed_value
55
+ # end
56
+
57
+ # def self.get_content(message)
58
+ # multipart = message.body.parts.find { |p| p.content_type.match(/multipart/).present? }
59
+ # part = multipart.presence || message
60
+ # get_html_or_text(part)
61
+ # rescue StandardError => e
62
+ # pg_err e
63
+ # ''
64
+ # end
65
+ end
66
+ 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,216 @@
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 #content_eml' do
59
+ subject do
60
+ get :content_eml, params: { id: email.id }
61
+ end
62
+
63
+ let(:email) { create :email }
64
+
65
+ it do
66
+ subject
67
+ expect(response).to have_http_status(:ok)
68
+ end
69
+ end
70
+
71
+ describe 'GET #index' do
72
+ subject do
73
+ get :index, params: {}
74
+ end
75
+
76
+ before { create :email }
77
+
78
+ it 'returns a success response' do
79
+ subject
80
+ expect(response).to be_successful
81
+ end
82
+
83
+ context 'when user is not logged in' do
84
+ let(:logged_user) { nil }
85
+
86
+ it 'redirects to login path' do
87
+ subject
88
+ expect(response).to redirect_to(new_user_session_path)
89
+ end
90
+ end
91
+
92
+ context 'when se pide el excel' do
93
+ subject do
94
+ get :index, params: {}, format: 'xlsx'
95
+ end
96
+
97
+ it 'returns a success response' do
98
+ subject
99
+ expect(response).to be_successful
100
+ end
101
+ end
102
+ end
103
+
104
+ describe 'GET #show' do
105
+ it 'returns a success response' do
106
+ email = create(:email)
107
+ get :show, params: { id: email.to_param }
108
+ expect(response).to be_successful
109
+ end
110
+ end
111
+
112
+ describe 'GET #new' do
113
+ it 'returns a success response' do
114
+ get :new, params: {}
115
+ expect(response).to be_successful
116
+ end
117
+ end
118
+
119
+ describe 'GET #edit' do
120
+ it 'returns a success response' do
121
+ email = create(:email)
122
+ get :edit, params: { id: email.to_param }
123
+ expect(response).to be_successful
124
+ end
125
+ end
126
+
127
+ describe 'POST #create' do
128
+ context 'with valid params' do
129
+ it 'creates a new Email' do
130
+ expect do
131
+ post :create, params: { email: valid_attributes }
132
+ end.to change(Email, :count).by(1)
133
+ end
134
+
135
+ it 'redirects to the created email' do
136
+ post :create, params: { email: valid_attributes }
137
+ expect(response).to redirect_to([:admin, Email.last])
138
+ end
139
+ end
140
+
141
+ context 'with invalid params' do
142
+ it 'returns a unprocessable_entity response' do
143
+ post :create, params: { email: invalid_attributes }
144
+ expect(response).to have_http_status(:unprocessable_entity)
145
+ end
146
+
147
+ it 'renders the new template' do
148
+ post :create, params: { email: invalid_attributes }
149
+ expect(response).to render_template(:new)
150
+ end
151
+ end
152
+ end
153
+
154
+ describe 'PUT #update' do
155
+ context 'with valid params' do
156
+ let(:new_attributes) do
157
+ attributes_for(:email)
158
+ end
159
+
160
+ it 'updates the requested email' do
161
+ email = create(:email)
162
+ put :update, params: { id: email.to_param, email: new_attributes }
163
+ email.reload
164
+ expect(email.from_address).to eq new_attributes[:from_address]
165
+ end
166
+
167
+ it 'redirects to the email' do
168
+ email = create(:email)
169
+ put :update, params: { id: email.to_param, email: valid_attributes }
170
+ expect(response).to redirect_to([:admin, email])
171
+ end
172
+ end
173
+
174
+ context 'with invalid params' do
175
+ it 'returns a unprocessable_entity response' do
176
+ email = create(:email)
177
+ put :update, params: { id: email.to_param, email: invalid_attributes }
178
+ expect(response).to have_http_status(:unprocessable_entity)
179
+ end
180
+
181
+ it 'renders the edit template' do
182
+ email = create(:email)
183
+ put :update, params: { id: email.to_param, email: invalid_attributes }
184
+ expect(response).to render_template(:edit)
185
+ end
186
+ end
187
+ end
188
+
189
+ describe 'DELETE #destroy' do
190
+ subject do
191
+ request.headers['Accept'] = 'text/vnd.turbo-stream.html,text/html'
192
+ delete :destroy, params: { id: email.to_param, redirect_to: redirect_url }
193
+ end
194
+
195
+ let!(:email) { create :email }
196
+ let(:redirect_url) { nil }
197
+
198
+ it 'destroys the requested email' do
199
+ expect { subject }.to change(Email, :count).by(-1)
200
+ end
201
+
202
+ it 'quita el elemento de la lista' do
203
+ subject
204
+ expect(response.body).to include('turbo-stream action="remove"')
205
+ end
206
+
207
+ context 'si hay redirect_to' do
208
+ let(:redirect_url) { admin_emails_url }
209
+
210
+ it 'redirects to the emails list' do
211
+ subject
212
+ expect(response).to redirect_to(admin_emails_url)
213
+ end
214
+ end
215
+ end
216
+ 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,29 @@
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
+ # content_eml { Faker::Lorem.sentence }
19
+ # message_id { Faker::Lorem.sentence }
20
+ # mailer { Faker::Lorem.sentence }
21
+ # status_detail { Faker::Lorem.sentence }
22
+ # status { rand(1..9999) }
23
+
24
+ # trait :associated_existente do
25
+ # associated { nil }
26
+ # associated_id { Associated.pluck(:id).sample }
27
+ # end
28
+ end
29
+ 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.57'
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? } -%>