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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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? } -%>