mno-enterprise-core 3.0.7 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/mno_enterprise/login-background.jpg +0 -0
  3. data/app/assets/stylesheets/mno_enterprise/mail.css +27 -0
  4. data/app/helpers/mno_enterprise/application_helper.rb +14 -0
  5. data/app/helpers/mno_enterprise/impersonate_helper.rb +1 -1
  6. data/app/models/mno_enterprise/app.rb +2 -1
  7. data/app/models/mno_enterprise/base_resource.rb +1 -2
  8. data/app/models/mno_enterprise/impac/dashboard.rb +3 -1
  9. data/app/models/mno_enterprise/impac/kpi.rb +1 -1
  10. data/app/models/mno_enterprise/impac/widget.rb +1 -1
  11. data/app/models/mno_enterprise/org_invite.rb +2 -1
  12. data/app/models/mno_enterprise/tenant.rb +2 -4
  13. data/app/models/mno_enterprise/user.rb +6 -0
  14. data/app/pdf/mno_enterprise/invoice_pdf.rb +29 -12
  15. data/app/views/system_notifications/confirmation-instructions.html.erb +26 -0
  16. data/app/views/system_notifications/confirmation-instructions.text.erb +10 -0
  17. data/app/views/system_notifications/delete-request-instructions.html.erb +31 -0
  18. data/app/views/system_notifications/delete-request-instructions.text.erb +15 -0
  19. data/app/views/system_notifications/organization-invite-existing-user.html.erb +30 -0
  20. data/app/views/system_notifications/organization-invite-existing-user.text.erb +13 -0
  21. data/app/views/system_notifications/organization-invite-new-user.html.erb +30 -0
  22. data/app/views/system_notifications/organization-invite-new-user.text.erb +14 -0
  23. data/app/views/system_notifications/reconfirmation-instructions.html.erb +27 -0
  24. data/app/views/system_notifications/reconfirmation-instructions.text.erb +10 -0
  25. data/app/views/system_notifications/registration-instructions.html.erb +27 -0
  26. data/app/views/system_notifications/registration-instructions.text.erb +9 -0
  27. data/app/views/system_notifications/reset-password-instructions.html.erb +27 -0
  28. data/app/views/system_notifications/reset-password-instructions.text.erb +11 -0
  29. data/app/views/system_notifications/unlock-instructions.html.erb +27 -0
  30. data/app/views/system_notifications/unlock-instructions.text.erb +10 -0
  31. data/config/initializers/audit_log.rb +2 -0
  32. data/config/locales/templates/dashboard/organization/en.yml +2 -0
  33. data/config/locales/views/auth/registrations/en.yml +1 -1
  34. data/lib/devise/hooks/impersonatable.rb +14 -0
  35. data/lib/devise_extension.rb +9 -2
  36. data/lib/generators/mno_enterprise/install/templates/config/application.yml +6 -2
  37. data/lib/generators/mno_enterprise/install/templates/config/initializers/mno_enterprise.rb +6 -1
  38. data/lib/generators/mno_enterprise/install/templates/config/settings.yml +2 -0
  39. data/lib/generators/mno_enterprise/install/templates/config/settings/uat.yml +2 -2
  40. data/lib/generators/mno_enterprise/install/templates/stylesheets/main.less +4 -0
  41. data/lib/her_extension/model/relation.rb +0 -13
  42. data/lib/mandrill_client.rb +13 -41
  43. data/lib/mno_enterprise/concerns/models/ability.rb +12 -0
  44. data/lib/mno_enterprise/concerns/models/organization.rb +5 -2
  45. data/lib/mno_enterprise/core.rb +21 -8
  46. data/lib/mno_enterprise/mail_adapters/adapter.rb +51 -0
  47. data/lib/mno_enterprise/mail_adapters/mandrill_adapter.rb +39 -0
  48. data/lib/mno_enterprise/mail_adapters/smtp_adapter.rb +21 -0
  49. data/lib/mno_enterprise/mail_adapters/sparkpost_adapter.rb +56 -0
  50. data/lib/mno_enterprise/mail_adapters/test_adapter.rb +21 -0
  51. data/lib/mno_enterprise/mail_client.rb +28 -0
  52. data/lib/mno_enterprise/smtp_client.rb +32 -0
  53. data/lib/mno_enterprise/testing_support/factories/apps.rb +1 -2
  54. data/lib/mno_enterprise/testing_support/factories/impac/kpis.rb +1 -0
  55. data/lib/mno_enterprise/testing_support/factories/tenant.rb +1 -7
  56. data/lib/mno_enterprise/testing_support/mno_enterprise_api_test_helper.rb +1 -2
  57. data/lib/mno_enterprise/version.rb +1 -1
  58. data/spec/lib/her_extension/model/relation_spec.rb +0 -71
  59. data/spec/lib/mandrill_client_spec.rb +27 -42
  60. data/spec/lib/mno_enterprise/mail_adapters/adapter_spec.rb +29 -0
  61. data/spec/lib/mno_enterprise/mail_adapters/mandrill_adapter_spec.rb +53 -0
  62. data/spec/lib/mno_enterprise/mail_adapters/smtp_adapter_spec.rb +29 -0
  63. data/spec/lib/mno_enterprise/mail_adapters/sparkpost_adapter_spec.rb +60 -0
  64. data/spec/lib/mno_enterprise/mail_client_spec.rb +9 -0
  65. data/spec/lib/mno_enterprise/smtp_client_spec.rb +39 -0
  66. data/spec/models/mno_enterprise/ability_spec.rb +21 -0
  67. data/spec/models/mno_enterprise/base_resource_spec.rb +0 -22
  68. metadata +76 -24
  69. data/lib/her_extension/middleware/mnoe_raise_error.rb +0 -21
  70. data/lib/her_extension/model/associations/belongs_to_association.rb +0 -25
@@ -17,9 +17,7 @@ require "her_extension/model/parse"
17
17
  require "her_extension/model/associations/association"
18
18
  require "her_extension/model/associations/association_proxy"
19
19
  require "her_extension/model/associations/has_many_association"
20
- require "her_extension/model/associations/belongs_to_association"
21
20
  require "her_extension/middleware/mnoe_api_v1_parse_json"
22
- require "her_extension/middleware/mnoe_raise_error"
23
21
  require "faraday_middleware"
24
22
  require "mno_enterprise/engine"
25
23
 
@@ -29,7 +27,6 @@ require 'mno_enterprise/database_extendable'
29
27
  require 'config'
30
28
  require 'figaro'
31
29
 
32
- require 'mandrill'
33
30
  require "mandrill_client"
34
31
 
35
32
  require 'accountingjs_serializer'
@@ -161,11 +158,26 @@ module MnoEnterprise
161
158
  #====================================
162
159
  # Emailing
163
160
  #====================================
161
+ # @deprecated: Use ENV['MANDRILL_API_KEY']
164
162
  # Mandrill Key for sending emails
165
- # Points to the default maestrano enterprise account
166
- mattr_accessor :mandrill_key
163
+ def self.mandrill_key
164
+ warn "[DEPRECATION] `mandrill_key` is deprecated. Use `ENV['MANDRILL_API_KEY']`."
165
+ @@mandrill_key
166
+ end
167
+ def self.mandrill_key=(mandrill_key)
168
+ warn "[DEPRECATION] `mandrill_key` is deprecated. Use `ENV['MANDRILL_API_KEY']`."
169
+ @@mandrill_key = mandrill_key
170
+ end
167
171
  @@mandrill_key = nil
168
172
 
173
+ # Adapter used to send emails
174
+ # Default to :mandrill
175
+ mattr_reader(:mail_adapter) { Rails.env.test? ? :test : :mandrill }
176
+ def self.mail_adapter=(adapter)
177
+ @@mail_adapter = adapter
178
+ MnoEnterprise::MailClient.adapter = self.mail_adapter
179
+ end
180
+
169
181
  # The support email address
170
182
  mattr_accessor :support_email
171
183
  @@support_email = "support@example.com"
@@ -236,6 +248,10 @@ module MnoEnterprise
236
248
  yield self
237
249
  self.configure_styleguide
238
250
  self.configure_api
251
+
252
+ # Mail config
253
+ # We can't use the setter before MailClient is loaded
254
+ self.mail_adapter = self.mail_adapter
239
255
  end
240
256
 
241
257
  # Create a JSON web token with the provided payload
@@ -294,9 +310,6 @@ module MnoEnterprise
294
310
 
295
311
  # Adapter
296
312
  c.use Faraday::Adapter::NetHttpNoProxy
297
-
298
- # Error Handling
299
- c.use Her::Middleware::MnoeRaiseError
300
313
  end
301
314
  end
302
315
  end
@@ -0,0 +1,51 @@
1
+ module MnoEnterprise
2
+ module MailAdapters
3
+ # @abstract Subclass and override {#client} and {#deliver} to implement
4
+ # a custom Mailer Adapter.
5
+ class Adapter
6
+ class << self
7
+ # Store the list of emails that are pending to be sent.
8
+ # @note Only used for testing
9
+ #
10
+ # @example
11
+ # expect { some_action }.to change(Adapter.base_deliveries,:count).by(1)
12
+ #
13
+ # @return [Array]
14
+ def base_deliveries
15
+ @base_deliveries ||= []
16
+ end
17
+
18
+ # Check whether mailers are in test mode or not.
19
+ # Emails should not be sent in test mode.
20
+ #
21
+ # @return [Boolean]
22
+ def test?
23
+ (Rails.configuration.action_mailer.delivery_method || '').to_sym == :test
24
+ end
25
+
26
+ def client
27
+ raise NotImplementedError
28
+ end
29
+
30
+ # Send a template
31
+ #
32
+ # @param [String] template the immutable name of the template to send
33
+ #
34
+ # @param [Hash] from a hash describing the sender
35
+ # @option from [String] :name optional from name to be used
36
+ # @option from [String] :email the sender email address
37
+ #
38
+ # @param [Array, Hash] to an array or hash describing the recipient
39
+ # @option to [String] :name optional recipient name
40
+ # @option to [String] :email the recipient email address
41
+ #
42
+ # @param [Hash] vars substitution variables
43
+ # @param [Hash] opts additional parameters to pass to the client
44
+ #
45
+ def deliver(template,from,to,vars = {},opts = {})
46
+ raise NotImplementedError
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ gem 'mandrill-api', '~> 1.0.53'
2
+ require 'mandrill'
3
+
4
+ module MnoEnterprise
5
+ module MailAdapters
6
+ class MandrillAdapter < Adapter
7
+ class << self
8
+ # Return a mandrill client configured with the right API key
9
+ def client
10
+ @client ||= Mandrill::API.new(ENV['MANDRILL_API_KEY'] || MnoEnterprise.mandrill_key)
11
+ end
12
+
13
+ # Send a template
14
+ # @see Adapter#deliver
15
+ def deliver(template, from, to, vars={}, opts={})
16
+ # Prepare message from args
17
+ message = { from_name: from[:name], from_email: from[:email]}
18
+ message[:to] = [to].flatten.map { |t| {name: t[:name], email: t[:email], type: (t[:type] || :to) } }
19
+ message[:global_merge_vars] = vars.map { |k,v| {name: k.to_s, content: v} }
20
+
21
+ # Merge additional mandrill options
22
+ message.merge!(opts)
23
+
24
+ self.send_template(template,[],message)
25
+ end
26
+
27
+ # Send the provided template with options
28
+ # MandrillClient.send_template(template_name(string), template_content(array), message(hash))
29
+ def send_template(*args)
30
+ if self.test?
31
+ self.base_deliveries.push(args)
32
+ else
33
+ self.client.messages.send_template(*args)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ module MnoEnterprise
2
+ module MailAdapters
3
+ # SMTP Adapter for MnoEnterprise::MailClient
4
+ class SmtpAdapter < Adapter
5
+ class << self
6
+ # Return a smtp client configured with the SMTP settings
7
+ # @return [SmtpClient]
8
+ def client
9
+ @client = MnoEnterprise::SmtpClient.send :new
10
+ end
11
+
12
+ # Send a template
13
+ # @See Adapter#deliver
14
+ def deliver(template, from, to, vars={}, opts={})
15
+ client.deliver(template, from, to, vars, opts).deliver
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,56 @@
1
+ gem 'sparkpost'
2
+ require 'sparkpost'
3
+
4
+ module MnoEnterprise
5
+ module MailAdapters
6
+ # SparkPost Adapter for MnoEnterprise::MailClient
7
+ class SparkpostAdapter < Adapter
8
+ class << self
9
+ # Return a sparkpost client configured with the right API key
10
+ # api key is set in ENV through ENV['SPARKPOST_API_KEY']
11
+ # @return [SparkPost::Client]
12
+ def client
13
+ @client ||= SparkPost::Client.new
14
+ end
15
+
16
+ # Send a template
17
+ # @see Adapter#deliver
18
+ def deliver(template, from, to, vars={}, opts={})
19
+ # Prepare message from args
20
+ message = {
21
+ recipients: prepare_recipients(to),
22
+ content: {
23
+ from: from,
24
+ template_id: template
25
+ },
26
+ substitution_data: vars
27
+ }
28
+
29
+ # Merge additional options
30
+ message.merge!(opts)
31
+
32
+ # Send
33
+ send_template(template,[],message)
34
+ end
35
+
36
+ # Send the provided template with options
37
+ # SparkpostClient.send_template(template_name(string), template_content(array), message(hash))
38
+ def send_template(template_name, _, message)
39
+ if test?
40
+ base_deliveries.push([template_name, message])
41
+ else
42
+ message[:content][:template_id] = template_name
43
+ client.transmission.send_payload(message)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # TODO: Use delegate?
50
+ def prepare_recipients(recipients)
51
+ client.transmission.prepare_recipients(recipients)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,21 @@
1
+ module MnoEnterprise
2
+ module MailAdapters
3
+ # Test Adapter for MnoEnterprise::MailClient
4
+ # Add messages to an internal array instead of sending them
5
+ class TestAdapter < Adapter
6
+ class << self
7
+ # Send a template
8
+ # @see Adapter#deliver
9
+ def deliver(*args)
10
+ send_template(*args)
11
+ end
12
+
13
+ # Send the provided template with options
14
+ # SparkpostClient.send_template(template_name(string), template_content(array), message(hash))
15
+ def send_template(*args)
16
+ base_deliveries.push(*args)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module MnoEnterprise
2
+ # Abstract the email sending logic
3
+ #
4
+ class MailClient
5
+ cattr_reader(:adapter)
6
+
7
+ # Specify the mail adapter. The default email adapter is the :mandrill adapter.
8
+ def self.adapter=(name_or_adapter)
9
+ @@adapter = \
10
+ case name_or_adapter
11
+ when Symbol, String
12
+ load_adapter(name_or_adapter)
13
+ else
14
+ name_or_adapter if name_or_adapter.respond_to?(:deliver)
15
+ end
16
+ end
17
+
18
+ # @see Adapter#deliver
19
+ def self.deliver(*args)
20
+ adapter.deliver(*args)
21
+ end
22
+
23
+ private
24
+ def self.load_adapter(name)
25
+ "MnoEnterprise::MailAdapters::#{name.to_s.camelize}Adapter".constantize
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ require 'action_mailer/railtie'
2
+
3
+ module MnoEnterprise
4
+ # Base class (instantiable) for SMTP adapter
5
+ class SmtpClient < ActionMailer::Base
6
+ # Send SMTP template - terminal mailing part
7
+ def deliver(template, from, to, vars={}, opts={})
8
+ @info = vars
9
+ @info[:company] = from[:name]
10
+
11
+ mail(
12
+ from: format_sender(from),
13
+ to: to[:email],
14
+ subject: humanize(template),
15
+ template_path: 'system_notifications',
16
+ template_name: template
17
+ )
18
+ end
19
+
20
+ # Returns Actionmailer-compliant :from string
21
+ # @Format : "Sender name <sender@email.com>"
22
+ def format_sender(from)
23
+ "#{from[:name]} <#{from[:email]}>"
24
+ end
25
+
26
+ # Returns humanized template subject
27
+ # @i.e. "reset-password-instructions" to "Reset password instructions"
28
+ def humanize(template_slug)
29
+ template_slug.tr("-", "_").humanize
30
+ end
31
+ end
32
+ end
@@ -22,7 +22,7 @@ FactoryGirl.define do
22
22
  stack 'cube'
23
23
  terms_url "http://opensource.org/licenses/MIT"
24
24
  appinfo { {} }
25
-
25
+ sequence(:rank) { |n| n }
26
26
  pricing_plans {{
27
27
  'default' =>[{name: 'Monthly Plan', price: '20.0', currency: 'AUD', factor: '/month'}]
28
28
  }}
@@ -41,5 +41,4 @@ FactoryGirl.define do
41
41
  # Properly build the resource with Her
42
42
  initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
43
43
  end
44
-
45
44
  end
@@ -4,6 +4,7 @@ FactoryGirl.define do
4
4
  factory :impac_kpi, class: MnoEnterprise::Impac::Kpi do
5
5
 
6
6
  sequence(:id) { |n| n }
7
+ sequence(:name) { |n| "Random Kpi #{n}" }
7
8
  dashboard { build(:impac_dashboard).attributes }
8
9
  endpoint "an/endpoint"
9
10
  element_watched "a_watchable"
@@ -1,8 +1,6 @@
1
1
  FactoryGirl.define do
2
2
 
3
- # Tenant from an old MnoHub (<= v1.0.2)
4
- # TODO: Remove once all mnohub are migrated to newer versions
5
- factory :old_tenant, class: MnoEnterprise::Tenant do
3
+ factory :tenant, class: MnoEnterprise::Tenant do
6
4
  last_portfolio_amount Money.new(65644,'AUD')
7
5
  last_customers_invoicing_amount Money.new(687994,'AUD')
8
6
  last_customers_outstanding_amount Money.new(178986,'AUD')
@@ -11,8 +9,4 @@ FactoryGirl.define do
11
9
  # Properly build the resource with Her
12
10
  initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
13
11
  end
14
-
15
- factory :tenant, parent: :old_tenant do
16
- current_billing_amount Money.new(123456, 'AUD')
17
- end
18
12
  end
@@ -124,8 +124,7 @@ module MnoEnterpriseApiTestHelper
124
124
 
125
125
  # Response
126
126
  c.use Her::Middleware::MnoeApiV1ParseJson
127
- c.use Her::Middleware::MnoeRaiseError
128
-
127
+
129
128
  # Add stubs on the test adapter
130
129
  c.use MnoeFaradayTestAdapter do |receiver|
131
130
  @_stub_list.each do |key,stub|
@@ -1,3 +1,3 @@
1
1
  module MnoEnterprise
2
- VERSION = '3.0.7'
2
+ VERSION = '3.1.0'
3
3
  end
@@ -3,76 +3,5 @@ require 'rails_helper'
3
3
  module MnoEnterprise
4
4
  RSpec.describe Her::Model::Relation do
5
5
  pending "add specs for Her::Model::Relation monkey patch: #{__FILE__}"
6
-
7
- before(:all) { Object.const_set('DummyClass', Class.new).send(:include, Her::Model) }
8
- let(:dummy_class) { DummyClass }
9
-
10
- describe '.where' do
11
- it 'adds the filter to params[:filter]' do
12
- rel = Her::Model::Relation.new(dummy_class).where('uid.in' => [1, 2], 'foo' => 'bar')
13
- expect(rel.params[:filter]).to eq('uid.in' => [1, 2], 'foo' => 'bar')
14
- end
15
-
16
- it 'replaces empty array values with nil' do
17
- rel = Her::Model::Relation.new(dummy_class).where('id.in' => [])
18
- expect(rel.params[:filter]).to eq('id.in' => nil)
19
- end
20
- end
21
-
22
- # We mostly test our request expectations
23
- describe '.first_or_create' do
24
- let(:relation) { Her::Model::Relation.new(dummy_class) }
25
- subject { relation.where(foo: 'bar').first_or_create(baz: 'bar') }
26
-
27
- context 'when matching record' do
28
- let(:record) { dummy_class.new(foo: 'bar') }
29
- before do
30
- expect(dummy_class).to receive(:request).with(hash_including(filter: {foo: 'bar'}, _method: :get)).and_return([record])
31
- end
32
-
33
- it 'returns the record' do
34
- expect(subject).to eq(record)
35
- end
36
- end
37
-
38
- context 'when no matching record' do
39
- before do
40
- expect(dummy_class).to receive(:request).with(hash_including(filter: {foo: 'bar'}, _method: :get)).and_return([])
41
- expect(dummy_class).to receive(:request).with(hash_including(foo: 'bar', baz: 'bar', _method: :post)).and_return(dummy_class.new)
42
- end
43
-
44
- it 'creates a new record' do
45
- expect(subject).to eq(dummy_class.new(foo: 'bar', baz: 'bar'))
46
- end
47
- end
48
- end
49
-
50
- # We mostly test our request expectations
51
- describe '.first_or_initialize' do
52
- let(:relation) { Her::Model::Relation.new(dummy_class) }
53
- subject { relation.where(foo: 'bar').first_or_initialize(baz: 'bar') }
54
-
55
- context 'when matching record' do
56
- let(:record) { dummy_class.new(foo: 'bar') }
57
- before do
58
- expect(dummy_class).to receive(:request).with(hash_including(filter: {foo: 'bar'}, _method: :get)).and_return([record])
59
- end
60
-
61
- it 'returns the record' do
62
- expect(subject).to eq(record)
63
- end
64
- end
65
-
66
- context 'when no matching record' do
67
- before do
68
- expect(dummy_class).to receive(:request).with(hash_including(filter: {foo: 'bar'}, _method: :get)).and_return([])
69
- # No POST stub
70
- end
71
-
72
- it 'build a new record' do
73
- expect(subject).to eq(dummy_class.new(foo: 'bar', baz: 'bar'))
74
- end
75
- end
76
- end
77
6
  end
78
7
  end