mno-enterprise-core 3.0.7 → 3.1.0

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.
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