marty 6.1.0 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +17 -3
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +3 -2
- data/Gemfile +2 -1
- data/app/assets/javascripts/marty/extjs/extensions/marty.js +23 -1
- data/app/assets/stylesheets/marty/application.css +4 -1
- data/app/assets/stylesheets/marty/dark_mode.css +17 -0
- data/app/components/marty/auth_app.rb +10 -1
- data/app/components/marty/auth_app/client/auth_app.js +4 -0
- data/app/components/marty/extras/layout.rb +2 -2
- data/app/components/marty/extras/misc.rb +1 -1
- data/app/components/marty/log_view.rb +0 -1
- data/app/components/marty/main_auth_app.rb +9 -12
- data/app/components/marty/promise_view.rb +5 -0
- data/app/components/marty/promise_view/client/promise_view.js +11 -0
- data/app/components/marty/schedule_jobs_dashboard.rb +30 -96
- data/app/components/marty/schedule_jobs_grid.rb +118 -0
- data/app/controllers/marty/application_controller.rb +6 -2
- data/app/models/marty/base.rb +48 -48
- data/app/models/marty/config.rb +14 -2
- data/app/models/marty/user.rb +12 -0
- data/app/services/marty/background_job/fetch_missing_in_schedule_cron_jobs.rb +19 -0
- data/app/services/marty/enums/report.rb +18 -0
- data/app/services/marty/promises/delorean/create.rb +16 -27
- data/app/services/marty/promises/ruby/create.rb +10 -1
- data/app/views/layouts/marty/application.html.erb +4 -1
- data/app/views/marty/diagnostic/op.html.erb +3 -1
- data/config/locales/en.yml +2 -0
- data/db/migrate/512_add_promise_priority.rb +9 -0
- data/db/migrate/513_add_priority_to_promise_view.rb +44 -0
- data/db/migrate/514_remove_marty_events.rb +13 -0
- data/delorean/enum_report.dl +11 -0
- data/delorean/table_report.dl +7 -0
- data/docker-compose.dummy.yml +7 -4
- data/lib/marty.rb +5 -2
- data/lib/marty/api/base.rb +15 -9
- data/lib/marty/cache_adapters.rb +2 -0
- data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -5
- data/lib/marty/cache_adapters/memory_and_redis.rb +93 -0
- data/lib/marty/cache_adapters/redis.rb +63 -0
- data/lib/marty/delayed_job/scheduled_job_plugin.rb +33 -0
- data/lib/marty/diagnostic/database.rb +1 -2
- data/lib/marty/logger.rb +50 -17
- data/lib/marty/monkey.rb +26 -6
- data/lib/marty/promise_ruby_job.rb +2 -0
- data/lib/marty/rails_app.rb +29 -0
- data/lib/marty/railtie.rb +1 -0
- data/lib/marty/version.rb +1 -1
- data/marty.gemspec +1 -0
- data/spec/controllers/job_controller_spec.rb +2 -2
- data/spec/dummy/app/components/gemini/cm_auth_app.rb +12 -0
- data/spec/dummy/app/components/gemini/simple_view.rb +17 -0
- data/spec/dummy/app/jobs/test_failing_job.rb +14 -0
- data/spec/dummy/app/models/gemini/helper.rb +90 -1
- data/spec/dummy/config/application.rb +1 -0
- data/spec/dummy/db/migrate/20191101132729_add_activity_flag_to_simple.rb +10 -0
- data/spec/dummy/delorean/blame_report.dl +1 -0
- data/spec/dummy/delorean/enum_report.dl +1 -0
- data/spec/dummy/delorean/marty_fields.dl +1 -0
- data/spec/dummy/delorean/table_report.dl +1 -0
- data/spec/features/data_blame_report_spec.rb +66 -0
- data/spec/features/data_grid_spec.rb +1 -1
- data/spec/features/enum_values_report_spec.rb +76 -0
- data/spec/features/inline_editing_spec.rb +33 -0
- data/spec/features/rule_spec.rb +1 -1
- data/spec/features/schedule_jobs_dashboard_spec.rb +1 -1
- data/spec/features/scripting_spec.rb +1 -1
- data/spec/features/user_list_report_spec.rb +74 -0
- data/spec/fixtures/misc/struct_compare_tests.txt +15 -5
- data/spec/job_helper.rb +39 -0
- data/spec/jobs/cron_job_spec.rb +91 -0
- data/spec/lib/mcfly_model_spec.rb +9 -0
- data/spec/models/promise_spec.rb +168 -1
- data/spec/other/diagnostic/delayed_job_workers_spec.rb +1 -1
- data/spec/performance/caching_spec.rb +99 -0
- data/spec/services/background_job/fetch_missing_in_schedule_cron_jobs_spec.rb +34 -0
- data/spec/support/delayed_job_helpers.rb +3 -3
- data/spec/support/shared_connection.rb +9 -1
- data/spec/support/structure_compare.rb +19 -3
- metadata +39 -6
- data/app/components/marty/event_view.rb +0 -129
- data/app/models/marty/event.rb +0 -317
- data/spec/dummy/db/migrate/20160923183516_add_bulk_pricing_event_ops.rb +0 -8
- data/spec/dummy/delorean/blame_report.dl +0 -268
- data/spec/dummy/delorean/marty_fields.dl +0 -63
- data/spec/dummy/delorean/table_report.dl +0 -34
- data/spec/models/event_spec.rb +0 -272
@@ -52,7 +52,9 @@
|
|
52
52
|
<body>
|
53
53
|
<div style="text-align:center">
|
54
54
|
<h1 class="application <%= 'error' unless @result['errors'].empty? %>">
|
55
|
-
<%=
|
55
|
+
<%=
|
56
|
+
ENV['DIAG_TITLE'] || ::Marty::RailsApp.application_name
|
57
|
+
%> Diagnostic
|
56
58
|
</h1>
|
57
59
|
<h3><i><%= DateTime.now %></i></h3>
|
58
60
|
<%== @result['display'] %>
|
data/config/locales/en.yml
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
class AddPriorityToPromiseView < ActiveRecord::Migration[4.2]
|
2
|
+
def up
|
3
|
+
execute <<~SQL
|
4
|
+
DROP VIEW IF EXISTS marty_vw_promises;
|
5
|
+
CREATE OR REPLACE VIEW marty_vw_promises
|
6
|
+
AS
|
7
|
+
SELECT
|
8
|
+
id,
|
9
|
+
title,
|
10
|
+
user_id,
|
11
|
+
cformat,
|
12
|
+
parent_id,
|
13
|
+
job_id,
|
14
|
+
status,
|
15
|
+
start_dt,
|
16
|
+
end_dt,
|
17
|
+
priority
|
18
|
+
FROM marty_promises;
|
19
|
+
|
20
|
+
GRANT SELECT ON marty_vw_promises TO public;
|
21
|
+
SQL
|
22
|
+
end
|
23
|
+
|
24
|
+
def down
|
25
|
+
execute <<~SQL
|
26
|
+
DROP VIEW IF EXISTS marty_vw_promises;
|
27
|
+
CREATE OR REPLACE VIEW marty_vw_promises
|
28
|
+
AS
|
29
|
+
SELECT
|
30
|
+
id,
|
31
|
+
title,
|
32
|
+
user_id,
|
33
|
+
cformat,
|
34
|
+
parent_id,
|
35
|
+
job_id,
|
36
|
+
status,
|
37
|
+
start_dt,
|
38
|
+
end_dt
|
39
|
+
FROM marty_promises;
|
40
|
+
|
41
|
+
GRANT SELECT ON marty_vw_promises TO public;
|
42
|
+
SQL
|
43
|
+
end
|
44
|
+
end
|
data/delorean/table_report.dl
CHANGED
data/docker-compose.dummy.yml
CHANGED
@@ -17,9 +17,11 @@ services:
|
|
17
17
|
- "HEADLESS=true"
|
18
18
|
- "PGTZ=America/Los_Angeles"
|
19
19
|
- "BUNDLER_VERSION=2.0.1"
|
20
|
+
- "MARTY_REDIS_URL=redis:6379/1"
|
20
21
|
# env_file: ".rbenv-vars"
|
21
22
|
depends_on:
|
22
23
|
- "postgres"
|
24
|
+
- "redis"
|
23
25
|
volumes:
|
24
26
|
- .:/opt/app:delegated
|
25
27
|
- '.bash_history.docker:/root/.bash_history'
|
@@ -34,12 +36,13 @@ services:
|
|
34
36
|
volumes:
|
35
37
|
- postgresql-data:/var/lib/postgresql/data
|
36
38
|
|
37
|
-
|
38
|
-
image:
|
39
|
+
redis:
|
40
|
+
image: 'redis:5.0.6-alpine'
|
39
41
|
volumes:
|
40
|
-
-
|
42
|
+
- redis-data:/data
|
41
43
|
|
42
44
|
volumes:
|
43
|
-
postgresql-data:
|
44
45
|
bundle_box:
|
46
|
+
postgresql-data:
|
47
|
+
redis-data:
|
45
48
|
|
data/lib/marty.rb
CHANGED
@@ -15,6 +15,7 @@ require 'marty/cache_adapters'
|
|
15
15
|
require 'marty/monkey'
|
16
16
|
require 'marty/promise_job'
|
17
17
|
require 'marty/json_schema'
|
18
|
+
require 'marty/rails_app'
|
18
19
|
|
19
20
|
# This does not get loaded in via bundler unless it is included in the
|
20
21
|
# application's Gemfile. Requiring it here removes the need to add it
|
@@ -25,7 +26,9 @@ require 'delayed_cron_job'
|
|
25
26
|
require 'pathname'
|
26
27
|
|
27
28
|
module Marty
|
28
|
-
|
29
|
-
|
29
|
+
class << self
|
30
|
+
def root
|
31
|
+
Pathname.new(File.expand_path('..', __dir__))
|
32
|
+
end
|
30
33
|
end
|
31
34
|
end
|
data/lib/marty/api/base.rb
CHANGED
@@ -53,14 +53,19 @@ class Marty::Api::Base
|
|
53
53
|
params = params.deep_dup
|
54
54
|
|
55
55
|
schema_key = [params[:tag], params[:script], params[:node], params[:attr]]
|
56
|
-
input_schema =
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
Marty::JsonSchema.get_schema(*schema_key)
|
62
|
-
|
63
|
-
|
56
|
+
input_schema = @@schemas[schema_key]
|
57
|
+
unless input_schema
|
58
|
+
begin
|
59
|
+
# get_schema will either return a hash with the schema,
|
60
|
+
# or a string with the error
|
61
|
+
result_schema = Marty::JsonSchema.get_schema(*schema_key)
|
62
|
+
|
63
|
+
# only store schema in cache when not error
|
64
|
+
@@schemas[schema_key] = result_schema if result_schema.is_a?(Hash)
|
65
|
+
input_schema = result_schema
|
66
|
+
rescue StandardError => e
|
67
|
+
return { error: e.message }
|
68
|
+
end
|
64
69
|
end
|
65
70
|
|
66
71
|
# if schema was found
|
@@ -165,7 +170,8 @@ class Marty::Api::Base
|
|
165
170
|
def self.filter_hash hash, filter_params
|
166
171
|
return unless hash
|
167
172
|
|
168
|
-
|
173
|
+
pf_class = ::Marty::RailsApp.parameter_filter_class
|
174
|
+
pf = pf_class.new(filter_params)
|
169
175
|
pf.filter(hash.stringify_keys)
|
170
176
|
end
|
171
177
|
|
data/lib/marty/cache_adapters.rb
CHANGED
@@ -2,11 +2,7 @@ module Marty
|
|
2
2
|
module CacheAdapters
|
3
3
|
class McflyRubyCache < ::Delorean::Cache::Adapters::RubyCache
|
4
4
|
def cache_item?(klass:, method_name:, args:)
|
5
|
-
|
6
|
-
|
7
|
-
return false if Mcfly.is_infinity(ts)
|
8
|
-
|
9
|
-
true
|
5
|
+
!Mcfly.is_infinity(args&.first)
|
10
6
|
end
|
11
7
|
end
|
12
8
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Marty
|
4
|
+
module CacheAdapters
|
5
|
+
class MemoryAndRedis < ::Delorean::Cache::Adapters::Base
|
6
|
+
POST = '__RedisCache'
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
size_per_class: 1000,
|
10
|
+
redis_url: Rails.application.config.marty.redis_url,
|
11
|
+
redis_expires_in: 48.hours
|
12
|
+
)
|
13
|
+
@size_per_class = size_per_class
|
14
|
+
|
15
|
+
@redis_adapter = ::Marty::CacheAdapters::Redis.new(
|
16
|
+
redis_url: redis_url,
|
17
|
+
expires_in: redis_expires_in
|
18
|
+
)
|
19
|
+
|
20
|
+
@memory_adapter = ::Marty::CacheAdapters::McflyRubyCache.new(
|
21
|
+
size_per_class: size_per_class
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def cache_item(klass:, cache_key:, item:)
|
26
|
+
@redis_adapter.cache_item(
|
27
|
+
klass: klass,
|
28
|
+
cache_key: cache_key,
|
29
|
+
item: item
|
30
|
+
)
|
31
|
+
|
32
|
+
@memory_adapter.cache_item(
|
33
|
+
klass: klass,
|
34
|
+
cache_key: cache_key,
|
35
|
+
item: item
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
# When cache is found in local memory, we simply return the cached item.
|
40
|
+
# Otherwise we look into Redis, if item is cached there,
|
41
|
+
# we copy it to local memory to speed up future lookups.
|
42
|
+
def fetch_item(klass:, cache_key:, default: nil)
|
43
|
+
memory_item = @memory_adapter.fetch_item(
|
44
|
+
klass: klass,
|
45
|
+
cache_key: cache_key,
|
46
|
+
default: default
|
47
|
+
)
|
48
|
+
|
49
|
+
return memory_item if memory_item != default
|
50
|
+
|
51
|
+
redis_item = @redis_adapter.fetch_item(
|
52
|
+
klass: klass,
|
53
|
+
cache_key: cache_key,
|
54
|
+
default: default
|
55
|
+
)
|
56
|
+
|
57
|
+
return default if redis_item == default
|
58
|
+
|
59
|
+
@memory_adapter.cache_item(
|
60
|
+
klass: klass,
|
61
|
+
cache_key: cache_key,
|
62
|
+
item: redis_item
|
63
|
+
)
|
64
|
+
|
65
|
+
redis_item
|
66
|
+
end
|
67
|
+
|
68
|
+
def cache_key(klass:, method_name:, args:)
|
69
|
+
r = ["#{klass.name}#{POST}", method_name] + args.map do |arg|
|
70
|
+
arg.respond_to?(:id) ? arg.id : arg
|
71
|
+
|
72
|
+
arg
|
73
|
+
end.freeze
|
74
|
+
|
75
|
+
Marshal.dump r
|
76
|
+
end
|
77
|
+
|
78
|
+
def clear!(klass:)
|
79
|
+
@redis_adapter.clear!(klass: klass)
|
80
|
+
@memory_adapter.clear!(klass: klass)
|
81
|
+
end
|
82
|
+
|
83
|
+
def clear_all!
|
84
|
+
@redis_adapter.clear_all!
|
85
|
+
@memory_adapter.clear_all!
|
86
|
+
end
|
87
|
+
|
88
|
+
def cache_item?(klass:, method_name:, args:)
|
89
|
+
!Mcfly.is_infinity(args&.first)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis'
|
4
|
+
|
5
|
+
module Marty
|
6
|
+
module CacheAdapters
|
7
|
+
class Redis < ::Delorean::Cache::Adapters::Base
|
8
|
+
POST = '__RedisCache'
|
9
|
+
|
10
|
+
def initialize(
|
11
|
+
size_per_class: 1000,
|
12
|
+
redis_url: Rails.application.config.marty.redis_url,
|
13
|
+
expires_in: 48.hours
|
14
|
+
)
|
15
|
+
@redis = ::Redis.new(url: "redis://#{redis_url}")
|
16
|
+
@expires_in = expires_in
|
17
|
+
end
|
18
|
+
|
19
|
+
def cache_item(klass:, cache_key:, item:)
|
20
|
+
@redis.set(
|
21
|
+
cache_key,
|
22
|
+
Marshal.dump(item),
|
23
|
+
ex: @expires_in.seconds.to_i
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_item(klass:, cache_key:, default: nil)
|
28
|
+
r = @redis.get(cache_key)
|
29
|
+
|
30
|
+
return default if r.nil?
|
31
|
+
|
32
|
+
Marshal.load(r)
|
33
|
+
end
|
34
|
+
|
35
|
+
def cache_key(klass:, method_name:, args:)
|
36
|
+
r = ["#{klass.name}#{POST}", method_name] + args.map do |arg|
|
37
|
+
next arg.id if arg.respond_to?(:id)
|
38
|
+
|
39
|
+
arg
|
40
|
+
end.freeze
|
41
|
+
|
42
|
+
Marshal.dump r
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear!(klass:)
|
46
|
+
keys = @redis.keys("*#{klass.name}#{POST}*")
|
47
|
+
@redis.pipelined do
|
48
|
+
keys.each do |key|
|
49
|
+
@redis.del key
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def clear_all!
|
55
|
+
@redis.flushall
|
56
|
+
end
|
57
|
+
|
58
|
+
def cache_item?(klass:, method_name:, args:)
|
59
|
+
!Mcfly.is_infinity(args&.first)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Marty
|
2
|
+
module DelayedJob
|
3
|
+
class ScheduledJobPlugin < Delayed::Plugin
|
4
|
+
class << self
|
5
|
+
def cron?(job)
|
6
|
+
job.cron.present?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
callbacks do |lifecycle|
|
11
|
+
# We want to nullify cron column if job schedule was turned off
|
12
|
+
# while the job was running, so it won't add a new record to
|
13
|
+
# delayed_jobs table
|
14
|
+
lifecycle.before(:error) do |worker, job, &block|
|
15
|
+
if cron?(job)
|
16
|
+
begin
|
17
|
+
job_class_str = job.handler.split("\n").find do |line|
|
18
|
+
line.include? 'job_class'
|
19
|
+
end
|
20
|
+
job_class_name = job_class_str.gsub('job_class:', '').strip
|
21
|
+
job_class = job_class_name.constantize
|
22
|
+
job.cron = job_class.cron_expression
|
23
|
+
rescue StandardError
|
24
|
+
end
|
25
|
+
else
|
26
|
+
# No cron job - proceed as normal
|
27
|
+
block.call(worker, job)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -22,8 +22,7 @@ module Marty::Diagnostic::Database
|
|
22
22
|
def self.db_schema
|
23
23
|
current = ActiveRecord::Migrator.current_version
|
24
24
|
raise "Migration is needed.\nCurrent Version: #{current}" if
|
25
|
-
|
26
|
-
ActiveRecord::Base.connection.migration_context.needs_migration?
|
25
|
+
::Marty::RailsApp.needs_migration?
|
27
26
|
|
28
27
|
current.to_s
|
29
28
|
end
|
data/lib/marty/logger.rb
CHANGED
@@ -1,27 +1,60 @@
|
|
1
1
|
class Marty::Logger
|
2
2
|
include Delorean::Model
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
[:debug, :info, :warn, :error, :fatal, :unknown].include?(m)
|
7
|
-
|
8
|
-
Marty::Util.logger.send(m, args[0]) if Marty::Util.logger.respond_to?(m)
|
9
|
-
log(m, *args)
|
4
|
+
delorean_fn :dllog, sig: [2, 20] do |*args|
|
5
|
+
info args[0], args[1..-1]
|
10
6
|
end
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
class << self
|
9
|
+
def log_event(event_name, *args)
|
10
|
+
if Marty::Util.logger.respond_to?(event_name)
|
11
|
+
Marty::Util.logger.send(
|
12
|
+
event_name,
|
13
|
+
args[0]
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
log(event_name, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def debug(*args)
|
21
|
+
log_event(:debug, *args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def info(*args)
|
25
|
+
log_event(:info, *args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def warn(*args)
|
29
|
+
log_event(:warn, *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def error(*args)
|
33
|
+
log_event(:error, *args)
|
34
|
+
end
|
15
35
|
|
16
|
-
|
36
|
+
def fatal(*args)
|
37
|
+
log_event(:fatal, *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def unknown(*args)
|
41
|
+
log_event(:unknown, *args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def log(type, message, details = nil)
|
45
|
+
Marty::Log.write_log(type, message, details)
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_logging(error_message, error_data)
|
17
49
|
yield
|
18
|
-
|
19
|
-
error(
|
20
|
-
|
21
|
-
|
22
|
-
|
50
|
+
rescue StandardError => e
|
51
|
+
error(
|
52
|
+
error_message,
|
53
|
+
'message' => e.message,
|
54
|
+
'data' => error_data
|
55
|
+
)
|
23
56
|
|
24
|
-
|
25
|
-
|
57
|
+
raise "#{error_message}: #{e.message}"
|
58
|
+
end
|
26
59
|
end
|
27
60
|
end
|