web47core 2.0.1 → 2.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +111 -41
  3. data/app/controllers/status_controller.rb +8 -3
  4. data/app/helpers/core_form_helper.rb +6 -6
  5. data/app/helpers/core_helper.rb +1 -1
  6. data/app/helpers/core_link_helper.rb +47 -8
  7. data/app/helpers/core_nav_bar_helper.rb +5 -4
  8. data/app/helpers/core_table_helper.rb +80 -0
  9. data/app/helpers/model_modal_helper.rb +3 -3
  10. data/app/views/common/_create_actions.html.haml +12 -0
  11. data/app/views/common/_update_actions.html.haml +10 -0
  12. data/app/views/cron/_edit.html.haml +15 -17
  13. data/app/views/cron/_index.html.haml +74 -67
  14. data/app/views/delayed_job_metrics/_index.html.haml +27 -0
  15. data/app/views/delayed_job_metrics/index.html.haml +1 -0
  16. data/app/views/delayed_job_workers/_index.html.haml +27 -0
  17. data/app/views/delayed_job_workers/index.html.haml +1 -0
  18. data/app/views/delayed_jobs/_index.html.haml +47 -52
  19. data/app/views/delayed_jobs/_show.html.haml +15 -13
  20. data/app/views/system_configurations/_edit.html.haml +14 -9
  21. data/app/views/system_configurations/_show.html.haml +18 -12
  22. data/config/brakeman.ignore +26 -0
  23. data/config/brakeman.yml +2 -0
  24. data/config/locales/en.yml +21 -3
  25. data/lib/app/controllers/concerns/core_delayed_job_metrics_controller.rb +35 -0
  26. data/lib/app/controllers/concerns/core_delayed_job_workers_controller.rb +35 -0
  27. data/lib/app/controllers/concerns/core_delayed_jobs_controller.rb +2 -3
  28. data/lib/app/jobs/cron/command.rb +1 -4
  29. data/lib/app/jobs/cron/record_delayed_job_metrics.rb +25 -0
  30. data/lib/app/jobs/cron/restart_orphaned_delayed_jobs.rb +44 -0
  31. data/lib/app/jobs/cron/server.rb +32 -15
  32. data/lib/app/jobs/cron/switchboard_sync_configuration.rb +2 -0
  33. data/lib/app/jobs/cron/switchboard_sync_models.rb +2 -0
  34. data/lib/app/jobs/cron/trim_collection.rb +1 -1
  35. data/lib/app/jobs/cron/trim_delayed_job_metrics.rb +29 -0
  36. data/lib/app/jobs/cron/trim_delayed_job_workers.rb +39 -0
  37. data/lib/app/jobs/cron/trim_failed_delayed_jobs.rb +2 -0
  38. data/lib/app/models/api_token.rb +9 -0
  39. data/lib/app/models/command_job.rb +12 -11
  40. data/lib/app/models/concerns/api_tokenable.rb +38 -0
  41. data/lib/app/models/concerns/aws_configuration.rb +65 -0
  42. data/lib/app/models/concerns/cdn_url.rb +7 -0
  43. data/lib/app/models/concerns/core_smtp_configuration.rb +65 -0
  44. data/lib/app/models/concerns/core_system_configuration.rb +18 -201
  45. data/lib/app/models/concerns/delayed_job_configuration.rb +24 -0
  46. data/lib/app/models/concerns/email_able.rb +6 -2
  47. data/lib/app/models/concerns/google_sso_configuration.rb +32 -0
  48. data/lib/app/models/concerns/search_able.rb +16 -0
  49. data/lib/app/models/concerns/server_process_able.rb +69 -0
  50. data/lib/app/models/concerns/slack_configuration.rb +38 -0
  51. data/lib/app/models/concerns/standard_model.rb +5 -2
  52. data/lib/app/models/concerns/switchboard_configuration.rb +43 -0
  53. data/lib/app/models/concerns/twilio_configuration.rb +37 -0
  54. data/lib/app/models/concerns/zendesk_configuration.rb +92 -0
  55. data/lib/app/models/{delayed_job.rb → delayed/backend/delayed_job.rb} +41 -0
  56. data/lib/app/models/delayed/jobs/metric.rb +61 -0
  57. data/lib/app/models/delayed/jobs/run.rb +40 -0
  58. data/lib/app/models/delayed/jobs/worker.rb +43 -0
  59. data/lib/app/models/delayed/plugins/time_keeper.rb +33 -0
  60. data/lib/app/models/delayed/worker.rb +24 -0
  61. data/lib/app/models/email_notification.rb +2 -1
  62. data/lib/app/models/email_template.rb +5 -6
  63. data/lib/app/models/notification.rb +12 -2
  64. data/lib/app/models/sms_notification.rb +9 -6
  65. data/lib/app/models/template.rb +12 -12
  66. data/lib/web47core/version.rb +1 -1
  67. data/lib/web47core.rb +33 -9
  68. metadata +114 -69
@@ -74,6 +74,47 @@ module Delayed
74
74
  rescue StandardError
75
75
  ''
76
76
  end
77
+
78
+ #
79
+ # Is this job running?
80
+ #
81
+ def running?
82
+ locked_by.present?
83
+ end
84
+
85
+ #
86
+ # Retrieve the host we are running on
87
+ #
88
+ def worker
89
+ Delayed::Jobs::Worker.where(host_name: worker_host_name, pid: worker_pid).first
90
+ rescue StandardError => error
91
+ App47Logger.log_warn 'Unable to fetch Worker information', error
92
+ nil
93
+ end
94
+
95
+ def worker_host_name
96
+ parts = locked_by.split
97
+ case parts.count
98
+ when 2
99
+ parts.first
100
+ when 3
101
+ parts[1]
102
+ else
103
+ raise "Unknown locked_by for delayed job #{parts}"
104
+ end.split(':').last
105
+ end
106
+
107
+ def worker_pid
108
+ parts = locked_by.split
109
+ case parts.count
110
+ when 2
111
+ parts.last
112
+ when 3
113
+ parts[2]
114
+ else
115
+ raise "Unknown locked_by for delayed job #{parts}"
116
+ end.split(':').last
117
+ end
77
118
  end
78
119
  end
79
120
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Delayed
4
+ module Jobs
5
+ #
6
+ # Hold the job metric information for a specific job, used to determine if the job should be auto restarted
7
+ #
8
+ class Metric
9
+ include StandardModel
10
+
11
+ #
12
+ # Fields
13
+ #
14
+ field :name, type: String
15
+ field :count, type: Integer, default: 0
16
+ field :max, type: Float, default: 0
17
+ field :min, type: Float, default: 1_000_000_000
18
+ field :total, type: Float, default: 0
19
+ field :last_run_at, type: Time
20
+
21
+ #
22
+ # Validations
23
+ #
24
+ validates :name, uniqueness: true, presence: true
25
+
26
+ #
27
+ # Max allowed time for a currently running job
28
+ #
29
+ def max_allowed_seconds
30
+ send(max_allowed_method) * max_allowed_factor
31
+ rescue StandardError
32
+ max * 5
33
+ end
34
+
35
+ #
36
+ # Return the average time to run this job
37
+ #
38
+ def avg
39
+ count.zero? ? 0 : total / count
40
+ rescue StandardError
41
+ 0
42
+ end
43
+
44
+ private
45
+
46
+ #
47
+ # Pull the method from system configuration to evalutate max allowed time
48
+ #
49
+ def max_allowed_method
50
+ @max_allowed_method ||= SystemConfiguration.delayed_job_max_allowed_method
51
+ end
52
+
53
+ #
54
+ # Pull the factor for max allowed when evaluating allowed time
55
+ #
56
+ def max_allowed_factor
57
+ @max_allowed_factor ||= SystemConfiguration.delayed_job_max_allowed_factor
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Delayed
4
+ module Jobs
5
+ #
6
+ # Single execution of a Job
7
+ #
8
+ class Run
9
+ include StandardModel
10
+ #
11
+ # Fields
12
+ #
13
+ field :name, type: String
14
+ field :locked_at, type: Time
15
+ field :locked_by, type: String
16
+ field :duration, type: Float, default: 0
17
+ #
18
+ # Relationships
19
+ #
20
+ belongs_to :worker, class_name: 'Delayed::Jobs::Worker'
21
+ #
22
+ # Validations
23
+ #
24
+ validates :name, presence: true
25
+ validates :locked_at, presence: true
26
+ validates :duration, numericality: { greater_than_or_equal_to: 0 }
27
+ #
28
+ # Callbacks
29
+ #
30
+ before_validation :record_duration
31
+
32
+ #
33
+ # Finish execution
34
+ #
35
+ def record_duration
36
+ self.duration = duration.zero? ? Time.now.utc - locked_at : duration
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Delayed
4
+ module Jobs
5
+ #
6
+ # Capture a specific worker process
7
+ #
8
+ class Worker
9
+ include StandardModel
10
+ include ServerProcessAble
11
+ #
12
+ # Relationships
13
+ #
14
+ has_many :runs, class_name: 'Delayed::Jobs::Run', dependent: :restrict
15
+
16
+ #
17
+ # Record a job relative to the worker
18
+ #
19
+ def record_job(job)
20
+ stop
21
+ runs.create!(name: job.display_name, locked_at: job.locked_at, locked_by: job.locked_by)
22
+ end
23
+
24
+ #
25
+ # Is this worker dead?, meaning has it reported in a timely manner
26
+ #
27
+ def dead?
28
+ last_check_in_at.present? ? check_in_limit > last_check_in_at : false
29
+ rescue StandardError
30
+ true
31
+ end
32
+
33
+ private
34
+
35
+ #
36
+ # Return the check in time limit based on if we are running or not
37
+ #
38
+ def check_in_limit
39
+ running? ? 3.hour.ago.utc : 1.minute.ago.utc
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ module Delayed
2
+ module Plugins
3
+ #
4
+ # Add callbacks to the lifecycle to capture two main aspects
5
+ # 1. When are workers checking in
6
+ # 2. How long jobs are taking
7
+ #
8
+ class TimeKeeper < Plugin
9
+ callbacks do |lifecycle|
10
+ lifecycle.before(:perform) do |_worker, job|
11
+ job.worker.start
12
+ rescue StandardError => error
13
+ App47Logger.log_warn 'before job perform', error
14
+ end
15
+ lifecycle.after(:perform) do |_worker, job|
16
+ job.worker.record_job(job)
17
+ rescue StandardError => error
18
+ App47Logger.log_warn 'after job perform', error
19
+ end
20
+ lifecycle.before(:loop) do
21
+ Delayed::Jobs::Worker.find_or_create_server.check_in
22
+ rescue StandardError => error
23
+ App47Logger.log_warn 'before worker loop', error
24
+ end
25
+ lifecycle.after(:loop) do
26
+ Delayed::Jobs::Worker.find_or_create_server.check_in
27
+ rescue StandardError => error
28
+ App47Logger.log_warn 'after worker loop', error
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Delayed
4
+ #
5
+ # Override the start and stop of the worker itself to capture start/end of the worker lifecycle
6
+ #
7
+ class Worker
8
+ #
9
+ # Starting the worker
10
+ #
11
+ def start
12
+ super
13
+ Delayed::Jobs::Worker.find_or_create_server
14
+ end
15
+
16
+ #
17
+ # Stop the worker
18
+ #
19
+ def stop
20
+ Delayed::Jobs::Worker.find_server&.stop
21
+ super
22
+ end
23
+ end
24
+ end
@@ -17,6 +17,7 @@
17
17
  #
18
18
  # email = EmailNotification.new
19
19
  # email.to = 'user@abc.com'
20
+ # email.recipient_name = 'users name'
20
21
  # email.sender = 'me@abc.com'
21
22
  # email.subject = 'Today is the day!'
22
23
  # email.message = 'Day is today!'
@@ -217,7 +218,7 @@ class EmailNotification < Notification
217
218
  return html_message if html_message.include?(tracker_url)
218
219
 
219
220
  image_tag = "<img style='height:0;width:0;border:none;display:none;' src='#{tracker_url}' alt=''/></body>"
220
- html_message.sub(%r{<\/body>}, image_tag)
221
+ html_message.sub(%r{</body>}, image_tag)
221
222
  end
222
223
 
223
224
  def subject_from_haml_text(haml_text, locals)
@@ -17,16 +17,15 @@ class EmailTemplate < Template
17
17
 
18
18
  #
19
19
  # Make sure the template is wrapped in html
20
+ #
20
21
  def htmlize_template
21
- if template.present? && !template.strip.start_with?("<")
22
- self.template = "<body><pre>#{template}</pre></body>"
23
- end
22
+ self.template = "<body><pre>#{template}</pre></body>" if template.present? && !template.strip.start_with?('<')
24
23
  end
25
24
 
26
25
  def valid_liquid_template
27
- super && Liquid::Template.parse(self.subject).nil?
28
- rescue Exception => error
29
- self.errors.add(:subject, "Invalid liquid text in subject: #{error.message}")
26
+ super && Liquid::Template.parse(subject).nil?
27
+ rescue StandardError => error
28
+ errors.add(:subject, "Invalid liquid text in subject: #{error.message}")
30
29
  false
31
30
  end
32
31
 
@@ -5,6 +5,7 @@
5
5
  #
6
6
  class Notification
7
7
  include StandardModel
8
+ include SearchAble
8
9
  #
9
10
  # Constants
10
11
  #
@@ -42,6 +43,7 @@ class Notification
42
43
  field :error_message, type: String
43
44
  field :last_viewed_at, type: Time
44
45
  field :viewed_count, type: Integer, default: 0
46
+ field :recipient_name, type: String
45
47
  #
46
48
  # Relationships
47
49
  #
@@ -100,10 +102,11 @@ class Notification
100
102
  #
101
103
  def finish_processing(processing_message = nil)
102
104
  if processing_message.present?
103
- set state: STATE_INVALID, error_message: processing_message
105
+ assign_attributes state: STATE_INVALID, error_message: processing_message
104
106
  else
105
- set state: STATE_PROCESSED, error_message: ''
107
+ assign_attributes state: STATE_PROCESSED, error_message: ''
106
108
  end
109
+ save(validate: false)
107
110
  end
108
111
 
109
112
  #
@@ -242,6 +245,13 @@ class Notification
242
245
  _type
243
246
  end
244
247
 
248
+ #
249
+ # Support search fields
250
+ #
251
+ def search_fields
252
+ %w[to state recipient_name]
253
+ end
254
+
245
255
  private
246
256
 
247
257
  #
@@ -35,12 +35,15 @@ class SmsNotification < Notification
35
35
  account_sid = config.twilio_account_id
36
36
  auth_token = config.twilio_auth_token
37
37
  client = Twilio::REST::Client.new account_sid, auth_token
38
-
39
- twilio_message = client.account.messages.create(
40
- body: message,
41
- to: to,
42
- from: config.twilio_phone_number
43
- )
38
+ twilio_message = if client.respond_to?(:account)
39
+ client.account.messages.create(body: message,
40
+ to: to,
41
+ from: config.twilio_phone_number)
42
+ else
43
+ client.messages.create(body: message,
44
+ to: to,
45
+ from: config.twilio_phone_number)
46
+ end
44
47
  # We are saved in the calling class, no reason to save again
45
48
  set sid: twilio_message.sid
46
49
  end
@@ -20,18 +20,6 @@ class Template
20
20
  validates :template, presence: true
21
21
  validate :valid_liquid_template
22
22
 
23
- private
24
-
25
- #
26
- # Ensure that the template is correct from a liquid statement
27
- #
28
- def valid_liquid_template
29
- Liquid::Template.parse(self.template).nil?
30
- rescue Exception => error
31
- self.errors.add(:template, "Invalid liquid text in template: #{error.message}")
32
- false
33
- end
34
-
35
23
  #
36
24
  # Retrieve the template out of the project
37
25
  #
@@ -45,4 +33,16 @@ class Template
45
33
  rescue StandardError
46
34
  nil
47
35
  end
36
+
37
+ private
38
+
39
+ #
40
+ # Ensure that the template is correct from a liquid statement
41
+ #
42
+ def valid_liquid_template
43
+ Liquid::Template.parse(template).nil?
44
+ rescue StandardError => error
45
+ errors.add(:template, "Invalid liquid text in template: #{error.message}")
46
+ false
47
+ end
48
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Web47core
4
- VERSION = '2.0.1'
4
+ VERSION = '2.2.15'
5
5
  end
data/lib/web47core.rb CHANGED
@@ -1,30 +1,48 @@
1
1
  require 'web47core/config'
2
2
  require 'app/models/concerns/app47_logger'
3
+ require 'app/models/concerns/api_tokenable'
4
+ require 'app/models/concerns/aws_configuration'
5
+ require 'app/models/concerns/core_system_configuration'
6
+ require 'app/models/concerns/core_account'
3
7
  require 'app/models/concerns/cipher_able'
4
8
  require 'app/models/concerns/cdn_url'
9
+ require 'app/models/concerns/delayed_job_configuration'
5
10
  require 'app/models/concerns/email_able'
11
+ require 'app/models/concerns/encrypted_password'
12
+ require 'app/models/concerns/google_sso_configuration'
6
13
  require 'app/models/concerns/search_able'
7
14
  require 'app/models/concerns/role_able'
8
15
  require 'app/models/concerns/time_zone_able'
16
+ require 'app/models/concerns/slack_configuration'
17
+ require 'app/models/concerns/server_process_able'
18
+ require 'app/models/concerns/core_smtp_configuration'
9
19
  require 'app/models/concerns/standard_model'
10
20
  require 'app/models/concerns/switchboard_able'
11
- require 'app/models/concerns/core_system_configuration'
12
- require 'app/models/concerns/core_account'
21
+ require 'app/models/concerns/switchboard_configuration'
13
22
  require 'app/models/concerns/secure_fields'
14
- require 'app/models/concerns/encrypted_password'
23
+ require 'app/models/concerns/twilio_configuration'
24
+ require 'app/models/concerns/zendesk_configuration'
15
25
  require 'app/models/command_job'
16
26
  require 'app/models/command_job_log'
17
- require 'app/models/delayed_job'
18
- require 'app/models/redis_configuration'
27
+ #
28
+ # Delayed jobs
29
+ #
30
+ require 'app/models/delayed/backend/delayed_job'
31
+ require 'app/models/delayed/plugins/time_keeper'
32
+ require 'app/models/delayed/jobs/metric'
33
+ require 'app/models/delayed/jobs/run'
34
+ require 'app/models/delayed/jobs/worker'
35
+
36
+ require 'app/models/api_token'
19
37
  require 'app/models/notification'
20
- require 'app/models/sms_notification'
21
38
  require 'app/models/template'
22
- require 'app/models/notification_template'
23
39
  require 'app/models/email_notification'
24
40
  require 'app/models/email_template'
41
+ require 'app/models/redis_configuration'
42
+ require 'app/models/notification_template'
25
43
  require 'app/models/slack_notification'
26
- require 'app/models/smtp_configuration'
27
44
  require 'app/models/sms_notification'
45
+ require 'app/models/smtp_configuration'
28
46
  #
29
47
  # Cron
30
48
  #
@@ -33,8 +51,12 @@ require 'app/jobs/cron/job'
33
51
  require 'app/jobs/cron/tab'
34
52
  require 'app/jobs/cron/job_tab'
35
53
  require 'app/jobs/cron/command'
36
- require 'app/jobs/cron/server'
37
54
  require 'app/jobs/cron/trim_collection'
55
+ require 'app/jobs/cron/record_delayed_job_metrics'
56
+ require 'app/jobs/cron/restart_orphaned_delayed_jobs'
57
+ require 'app/jobs/cron/trim_delayed_job_metrics'
58
+ require 'app/jobs/cron/trim_delayed_job_workers'
59
+ require 'app/jobs/cron/server'
38
60
  require 'app/jobs/cron/switchboard_sync_configuration'
39
61
  require 'app/jobs/cron/switchboard_sync_models'
40
62
  require 'app/jobs/cron/trim_audit_logs'
@@ -60,3 +82,5 @@ require 'app/controllers/concerns/core_controller'
60
82
  require 'app/controllers/concerns/core_system_configuration_controller'
61
83
  require 'app/controllers/concerns/core_cron_controller'
62
84
  require 'app/controllers/concerns/core_delayed_jobs_controller'
85
+ require 'app/controllers/concerns/core_delayed_job_metrics_controller'
86
+ require 'app/controllers/concerns/core_delayed_job_workers_controller'