marty 2.6.4 → 2.6.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d525319e98d55b25d7ac47f40a085b1d3759f67bf400bfcecd3908fb6ed8c6e8
4
- data.tar.gz: e3276f7391957c7fa615f2af1702944ff7a43aefd253d9e660e5c0f3cb637f1d
3
+ metadata.gz: 1cabd5f09c9dce0fee4129f0bf6fe90b9c92443db0e6ce2ba6703a56c5b1e067
4
+ data.tar.gz: 336101e0514a022e49c96709664a881121d42b6d54497f9eede865c89d4470b9
5
5
  SHA512:
6
- metadata.gz: 17a54c4a4e197cd114665cf83113eac00d16350501739e435080b68666ce8f78845a09d38af5cede910187b44736f4a8a0e715d2a37df7b19c541b202361e560
7
- data.tar.gz: 92f6af0854f0d5965203669164c63370a7ad70a5d1666d9115d13a540a11828f4cfe64a69c45ac0c27a9c40e660cdff23e77440113b2cd2bef8b734248b31bd3
6
+ metadata.gz: 6b5cae6730109621e2a8334ec31a1b4a045cf11d551bfa6e13aa9784454a4c67e4573818a2d6a1ed92c00bedd63d51f896fe3be017868c3a4f0baf84f96a97d8
7
+ data.tar.gz: 59a70afb17b02d6b944c0c492f7d1db9105c17e6f69aed36fda844bf9e2400cf3a76722fbec5e202cb3fed579fd3c1a241e67faa58750996e2c9b7a0cf0be173
@@ -12,7 +12,7 @@ before_script:
12
12
  stage: test
13
13
  # Use only the following CI runners
14
14
  tags:
15
- - cf-gitlabci-runner-eks-shared-dev
15
+ - gitlabci-runner-eks-shared-dev
16
16
 
17
17
  rubocop:
18
18
  extends: .base-test
@@ -28,6 +28,7 @@ rspec-features:
28
28
  extends: .base-test
29
29
  script:
30
30
  - ln -s /opt/support/extjs /cm_tech/marty/spec/dummy/public/extjs
31
+ - chromedriver-update 73.0.3683.68
31
32
  # - bundle exec rspec --pattern "spec/features/**/*_spec.rb"
32
33
  # FIXME: rule_spec is excluded, because chrome doesn't work with big window size in docker
33
34
  # And test fails with 1400/1400 resolution
@@ -1122,6 +1122,7 @@ Style/StringLiteralsInInterpolation:
1122
1122
  Style/StructInheritance:
1123
1123
  Exclude:
1124
1124
  - 'lib/marty/promise_job.rb'
1125
+ - 'lib/marty/promise_ruby_job.rb'
1125
1126
 
1126
1127
  # Offense count: 75
1127
1128
  # Cop supports --auto-correct.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- marty (2.6.2)
4
+ marty (2.6.5)
5
5
  aws-sigv4 (~> 1.0, >= 1.0.2)
6
6
  axlsx (= 3.0.0pre)
7
7
  coderay
@@ -62,7 +62,9 @@ GEM
62
62
  io-like (~> 0.3.0)
63
63
  arel (8.0.0)
64
64
  ast (2.4.0)
65
- aws-sigv4 (1.0.3)
65
+ aws-eventstream (1.0.3)
66
+ aws-sigv4 (1.1.0)
67
+ aws-eventstream (~> 1.0, >= 1.0.2)
66
68
  axlsx (3.0.0.pre)
67
69
  htmlentities (~> 4.3, >= 4.3.4)
68
70
  mimemagic (~> 0.3)
@@ -0,0 +1,5 @@
1
+ class Marty::EnumPromiseType < Marty::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['delorean', 'ruby']
5
+ end
@@ -10,44 +10,16 @@ class Marty::Promise < Marty::Base
10
10
  Marty::Promise.load_result(res, force)
11
11
  end
12
12
 
13
- def self.load_result(obj, force = false)
14
- if force && obj.respond_to?(:__force__)
15
- obj = obj.__force__
16
- end
17
-
18
- case obj
19
- when Array
20
- obj.map { |x| load_result(x, force) }
21
- when Hash
22
- p = obj['__promise__']
23
-
24
- if p && obj.length == 1
25
- load_result(Marty::PromiseProxy.new(*p), force)
26
- else
27
- obj.each_with_object({}) { |(k, v), h| h[k] = load_result(v, force) }
28
- end
29
- else
30
- obj
31
- end
32
- end
33
-
34
13
  has_many :children,
35
14
  foreign_key: 'parent_id',
36
15
  class_name: 'Marty::Promise',
37
16
  dependent: :destroy
38
17
 
39
- validates_presence_of :title
18
+ validates_presence_of :title, :promise_type
40
19
 
41
20
  belongs_to :parent, class_name: 'Marty::Promise'
42
21
  belongs_to :user, class_name: 'Marty::User'
43
22
 
44
- def self.cleanup(all = false)
45
- where('start_dt < ? AND parent_id IS NULL',
46
- DateTime.now - (all ? 0.hours : 4.hours)).destroy_all
47
- rescue StandardError => exc
48
- Marty::Util.logger.error("promise GC error: #{exc}")
49
- end
50
-
51
23
  def raw_conn
52
24
  self.class.connection.raw_connection
53
25
  end
@@ -162,8 +134,8 @@ class Marty::Promise < Marty::Base
162
134
  work_off_job(job)
163
135
  rescue StandardError => exc
164
136
  # log "OFFERR #{exc}"
165
- res = Delorean::Engine.grok_runtime_exception(exc)
166
- last.set_result(res)
137
+ error = exception_to_result(exc)
138
+ last.set_result(error)
167
139
  end
168
140
  # log "OFF1 #{Process.pid} #{last}"
169
141
  end
@@ -211,4 +183,48 @@ class Marty::Promise < Marty::Base
211
183
  result: promise.result
212
184
  }
213
185
  end
186
+
187
+ def delorean?
188
+ promise_type == 'delorean'
189
+ end
190
+
191
+ class << self
192
+ def load_result(obj, force = false)
193
+ if force && obj.respond_to?(:__force__)
194
+ obj = obj.__force__
195
+ end
196
+
197
+ case obj
198
+ when Array
199
+ obj.map { |x| load_result(x, force) }
200
+ when Hash
201
+ p = obj['__promise__']
202
+
203
+ if p && obj.length == 1
204
+ load_result(Marty::PromiseProxy.new(*p), force)
205
+ else
206
+ obj.each_with_object({}) { |(k, v), h| h[k] = load_result(v, force) }
207
+ end
208
+ else
209
+ obj
210
+ end
211
+ end
212
+
213
+ def cleanup(all = false)
214
+ where(
215
+ 'start_dt < ? AND parent_id IS NULL',
216
+ DateTime.now - (all ? 0.hours : 4.hours)
217
+ ).destroy_all
218
+ rescue StandardError => exc
219
+ Marty::Util.logger.error("promise GC error: #{exc}")
220
+ end
221
+
222
+ def exception_to_result(promise:, exception:)
223
+ if promise.delorean?
224
+ return Delorean::Engine.grok_runtime_exception(exception)
225
+ end
226
+
227
+ { 'error' => exception.message, 'backtrace' => exception.backtrace }
228
+ end
229
+ end
214
230
  end
@@ -0,0 +1,6 @@
1
+ module Marty
2
+ module Promises
3
+ module Delorean
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,75 @@
1
+ module Marty
2
+ module Promises
3
+ module Delorean
4
+ module Create
5
+ def self.call(params:, script:, node_name:, attr:, args:, tag:)
6
+ default_timeout = Marty::Promise::DEFAULT_PROMISE_TIMEOUT
7
+
8
+ title = params['p_title'] || "#{script}::#{node_name.demodulize}"
9
+ timeout = params['p_timeout'] || default_timeout
10
+ hook = params['p_hook']
11
+
12
+ promise = Marty::Promise.create(
13
+ title: title,
14
+ user_id: params[:_user_id],
15
+ parent_id: params[:_parent_id],
16
+ promise_type: 'delorean'
17
+ )
18
+
19
+ params[:_promise_id] = promise.id
20
+
21
+ begin
22
+ promise_job = Marty::PromiseJob.new(
23
+ promise,
24
+ title,
25
+ script,
26
+ tag,
27
+ node_name,
28
+ params,
29
+ args,
30
+ hook
31
+ )
32
+
33
+ job = Delayed::Job.enqueue(promise_job)
34
+ rescue StandardError => exc
35
+ # log "CALLERR #{exc}"
36
+ res = ::Delorean::Engine.grok_runtime_exception(exc)
37
+ promise.set_start
38
+ promise.set_result(res)
39
+ # log "CALLERRSET #{res}"
40
+ raise
41
+ end
42
+
43
+ # keep a reference to the job. This is needed in case we want to
44
+ # work off a promise job that we're waiting for and which hasn't
45
+ # been reserved yet.
46
+ promise.job_id = job.id
47
+ promise.save!
48
+
49
+ evh = params['p_event']
50
+ if evh
51
+ event, klass, subject_id, operation = evh.values_at(
52
+ 'event',
53
+ 'klass',
54
+ 'id',
55
+ 'operation'
56
+ )
57
+
58
+ if event
59
+ event.promise_id = promise.id
60
+ event.save!
61
+ else
62
+ Marty::Event.create!(
63
+ promise_id: promise.id,
64
+ klass: klass,
65
+ subject_id: subject_id,
66
+ enum_event_operation: operation
67
+ )
68
+ end
69
+ end
70
+ Marty::PromiseProxy.new(promise.id, timeout, attr)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,6 @@
1
+ module Marty
2
+ module Promises
3
+ module Ruby
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,44 @@
1
+ module Marty
2
+ module Promises
3
+ module Ruby
4
+ module Create
5
+ def self.call(module_name:, method_name:, method_args:, params: {})
6
+ default_timeout = Marty::Promise::DEFAULT_PROMISE_TIMEOUT
7
+
8
+ promise_params = params.with_indifferent_access
9
+
10
+ title = promise_params['p_title'] || "#{module_name}.#{method_name}"
11
+ timeout = promise_params['p_timeout'] || default_timeout
12
+ hook = promise_params['p_hook']
13
+
14
+ promise = Marty::Promise.create(
15
+ title: title,
16
+ user_id: promise_params[:_user_id],
17
+ parent_id: promise_params[:_parent_id],
18
+ promise_type: 'ruby'
19
+ )
20
+
21
+ begin
22
+ promise_job = Marty::PromiseRubyJob.new(
23
+ promise,
24
+ title,
25
+ module_name,
26
+ method_name,
27
+ method_args,
28
+ hook
29
+ )
30
+
31
+ job = Delayed::Job.enqueue(promise_job)
32
+ rescue StandardError => exc
33
+ res = { 'error' => exc.message }
34
+ promise.set_start
35
+ promise.set_result(res)
36
+ raise
37
+ end
38
+
39
+ Marty::PromiseProxy.new(promise.id, timeout, 'result')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ class AddPromiseTypeEnum < ActiveRecord::Migration[4.2]
2
+ def up
3
+ values = Marty::EnumPromiseType::VALUES
4
+ str_values = values.map {|v| ActiveRecord::Base.connection.quote v}.join ','
5
+ execute <<-SQL
6
+ CREATE TYPE marty_promise_types AS ENUM (#{str_values})
7
+ SQL
8
+ end
9
+
10
+ def down
11
+ execute <<-SQL
12
+ DROP TYPE marty_promise_types
13
+ SQL
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ class AddPromiseTypeToPromises < ActiveRecord::Migration[4.2]
2
+ def up
3
+ add_column :marty_promises, :promise_type, :marty_promise_types
4
+
5
+ Marty::Promise.update_all(promise_type: 'delorean')
6
+
7
+ change_column :marty_promises, :promise_type, :marty_promise_types, null: false
8
+ end
9
+
10
+ def down
11
+ remove_column :marty_promises, :promise_type
12
+ end
13
+ end
@@ -22,6 +22,70 @@ end
22
22
 
23
23
  ######################################################################
24
24
 
25
+ # Patch Delorean to work with promises
26
+
27
+ class Delorean::BaseModule::NodeCall
28
+ def initialize(_e, engine, node, params)
29
+ super
30
+
31
+ # If call has a promise_id (i.e. is from a promise) then that's
32
+ # our parent. Otherwise, we use its parent as our parent.
33
+ params[:_parent_id] = _e[:_promise_id] || _e[:_parent_id]
34
+ params[:_user_id] = _e[:_user_id] || Mcfly.whodunnit.try(:id)
35
+ end
36
+
37
+ # def log(msg)
38
+ # open('/tmp/dj.out', 'a') { |f| f.puts msg }
39
+ # end
40
+
41
+ # Monkey-patch '|' method for Delorean NodeCall to create promise
42
+ # jobs and return promise proxy objects.
43
+ def |(args)
44
+ if args.is_a?(String)
45
+ attr = args
46
+ args = [attr]
47
+ else
48
+ raise 'bad arg to %' unless args.is_a?(Array)
49
+
50
+ attr = nil
51
+ end
52
+ script, tag = engine.module_name, engine.sset.tag
53
+ nn = node.is_a?(Class) ? node.name : node.to_s
54
+ begin
55
+ # make sure params is serialzable before starting a Job
56
+ JSON.dump(params)
57
+ rescue StandardError => exc
58
+ raise "non-serializable parameters: #{params} #{exc}"
59
+ end
60
+
61
+ Marty::Promises::Delorean::Create.call(
62
+ params: params,
63
+ script: script,
64
+ node_name: nn,
65
+ attr: attr,
66
+ args: args,
67
+ tag: tag
68
+ )
69
+ end
70
+ end
71
+
72
+ class Delorean::Engine
73
+ def background_eval(node, params, attrs, event = {})
74
+ raise 'background_eval bad params' unless params.is_a?(Hash)
75
+
76
+ unless event.empty?
77
+ params['p_event'] = event.each_with_object({}) do |(k, v), h|
78
+ h[k.to_s] = v
79
+ end
80
+ end
81
+ nc = Delorean::BaseModule::NodeCall.new({}, self, node, params)
82
+ # start the background promise
83
+ nc | attrs
84
+ end
85
+ end
86
+
87
+ ######################################################################
88
+
25
89
  class Hash
26
90
  # define addition on hashes -- useful in Delorean code.
27
91
  def +(x)
@@ -59,4 +59,33 @@ module Marty::Permissions
59
59
  def has_any_perm?
60
60
  !(current_user_roles & ALL_ROLES).empty?
61
61
  end
62
+
63
+ # FIXME: for backwards compatibility returns true
64
+ # if permission is not specified in has_marty_permissions
65
+
66
+ NETZKE_ENDPOINTS = [:create, :read, :update, :delete].freeze
67
+
68
+ def can_call_endpoint?(endpoint)
69
+ # Netzke endpoints access is controlled by Netzke permissions
70
+ return true if NETZKE_ENDPOINTS.include?(endpoint.to_sym)
71
+
72
+ return true unless respond_to?(:marty_permissions)
73
+ return true unless marty_permissions.key?(endpoint.to_sym)
74
+
75
+ can_perform_action?(endpoint)
76
+ end
77
+
78
+ # FIXME: hack to override Netzke invoke endpoint
79
+ # for classes with Marty::Permissions
80
+ def self.extended(mod)
81
+ mod.class_exec do
82
+ def invoke_endpoint(endpoint, params, configs = [])
83
+ return super(endpoint, params, configs) if self.class.can_call_endpoint?(endpoint)
84
+
85
+ self.client = Netzke::Core::EndpointResponse.new
86
+ client.netzke_notify 'Permission Denied'
87
+ client
88
+ end
89
+ end
90
+ end
62
91
  end
@@ -1,100 +1,5 @@
1
1
  require 'delorean_lang'
2
2
 
3
- class Delorean::BaseModule::NodeCall
4
- def initialize(_e, engine, node, params)
5
- super
6
-
7
- # If call has a promise_id (i.e. is from a promise) then that's
8
- # our parent. Otherwise, we use its parent as our parent.
9
- params[:_parent_id] = _e[:_promise_id] || _e[:_parent_id]
10
- params[:_user_id] = _e[:_user_id] || Mcfly.whodunnit.try(:id)
11
- end
12
-
13
- # def log(msg)
14
- # open('/tmp/dj.out', 'a') { |f| f.puts msg }
15
- # end
16
-
17
- # Monkey-patch '|' method for Delorean NodeCall to create promise
18
- # jobs and return promise proxy objects.
19
- def |(args)
20
- if args.is_a?(String)
21
- attr = args
22
- args = [attr]
23
- else
24
- raise 'bad arg to %' unless args.is_a?(Array)
25
-
26
- attr = nil
27
- end
28
- script, tag = engine.module_name, engine.sset.tag
29
- nn = node.is_a?(Class) ? node.name : node.to_s
30
- begin
31
- # make sure params is serialzable before starting a Job
32
- JSON.dump(params)
33
- rescue StandardError => exc
34
- raise "non-serializable parameters: #{params} #{exc}"
35
- end
36
-
37
- # log "||||| #{args.inspect} #{params.inspect}"
38
-
39
- title = params['p_title'] || "#{script}::#{nn.demodulize}"
40
- timeout = params['p_timeout'] || Marty::Promise::DEFAULT_PROMISE_TIMEOUT
41
- hook = params['p_hook']
42
- promise = Marty::Promise.
43
- create(title: title,
44
- user_id: params[:_user_id],
45
- parent_id: params[:_parent_id],
46
- )
47
- params[:_promise_id] = promise.id
48
- begin
49
- job = Delayed::Job.enqueue Marty::PromiseJob.
50
- new(promise, title, script, tag, nn, params, args, hook)
51
- rescue StandardError => exc
52
- # log "CALLERR #{exc}"
53
- res = Delorean::Engine.grok_runtime_exception(exc)
54
- promise.set_start
55
- promise.set_result(res)
56
- # log "CALLERRSET #{res}"
57
- raise
58
- end
59
-
60
- # keep a reference to the job. This is needed in case we want to
61
- # work off a promise job that we're waiting for and which hasn't
62
- # been reserved yet.
63
- promise.job_id = job.id
64
- promise.save!
65
-
66
- evh = params['p_event']
67
- if evh
68
- event, klass, subject_id, operation = evh.values_at('event', 'klass',
69
- 'id', 'operation')
70
- if event
71
- event.promise_id = promise.id
72
- event.save!
73
- else
74
- event = Marty::Event.
75
- create!(promise_id: promise.id,
76
- klass: klass,
77
- subject_id: subject_id,
78
- enum_event_operation: operation)
79
- end
80
- end
81
- Marty::PromiseProxy.new(promise.id, timeout, attr)
82
- end
83
- end
84
-
85
- class Delorean::Engine
86
- def background_eval(node, params, attrs, event = {})
87
- raise 'background_eval bad params' unless params.is_a?(Hash)
88
-
89
- params['p_event'] = event.each_with_object({}) do |(k, v), h|
90
- h[k.to_s] = v
91
- end unless event.empty?
92
- nc = Delorean::BaseModule::NodeCall.new({}, self, node, params)
93
- # start the background promise
94
- nc | attrs
95
- end
96
- end
97
-
98
3
  class Marty::PromiseJob < Struct.new(:promise,
99
4
  :title,
100
5
  :sname,
@@ -141,12 +46,15 @@ class Marty::PromiseJob < Struct.new(:promise,
141
46
  # log "ERR- #{Process.pid} #{promise.id} #{Time.now.to_f} #{exc}"
142
47
  end
143
48
  promise.set_result(res)
49
+ process_hook(res)
50
+ end
144
51
 
145
- begin
146
- hook.run(params: params, result: res) if hook
147
- rescue StandardError => exc
52
+ def process_hook(res)
53
+ return unless hook
54
+
55
+ hook.run(params: params, result: res)
56
+ rescue StandardError => exc
148
57
  Marty::Util.logger.error "promise hook failed: #{exc}"
149
- end
150
58
  end
151
59
 
152
60
  def max_attempts
@@ -0,0 +1,47 @@
1
+ class Marty::PromiseRubyJob < Struct.new(:promise,
2
+ :title,
3
+ :module_name,
4
+ :method_name,
5
+ :method_args,
6
+ :hook,
7
+ )
8
+
9
+ def enqueue(job)
10
+ config = Rails.configuration.marty
11
+ hooks = config.promise_job_enqueue_hooks
12
+
13
+ return if hooks.blank?
14
+
15
+ hooks.each do |hook|
16
+ hook.call(job)
17
+ end
18
+ end
19
+
20
+ def perform
21
+ promise.set_start
22
+
23
+ begin
24
+ Mcfly.whodunnit = promise.user
25
+
26
+ mod = module_name.constantize
27
+ res = { 'result' => mod.send(method_name, *method_args) }
28
+ rescue StandardError => exc
29
+ res = ::Marty::Promise.exception_to_result(promise: promise, exception: exc)
30
+ end
31
+
32
+ promise.set_result(res)
33
+ process_hook(res)
34
+ end
35
+
36
+ def process_hook(res)
37
+ return unless hook
38
+
39
+ hook.run(params: method_args, result: res)
40
+ rescue StandardError => exc
41
+ Marty::Util.logger.error "promise hook failed: #{exc}"
42
+ end
43
+
44
+ def max_attempts
45
+ 1
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = '2.6.4'
2
+ VERSION = '2.6.5'
3
3
  end
@@ -4,7 +4,8 @@ class Gemini::LoanProgramView < Marty::GridAppendOnly
4
4
  has_marty_permissions create: :dev,
5
5
  read: :any,
6
6
  update: :dev,
7
- delete: :dev
7
+ delete: :dev,
8
+ test_access: :admin
8
9
 
9
10
  def configure(c)
10
11
  super
@@ -22,7 +23,27 @@ class Gemini::LoanProgramView < Marty::GridAppendOnly
22
23
  c.store_config.merge!({sorters: [{property: :name, direction: 'ASC'}]})
23
24
  end
24
25
 
26
+ client_class do |c|
27
+ c.netzke_on_test_access = l(<<-JS)
28
+ function() {
29
+ this.server.testAccess({})
30
+ }
31
+ JS
32
+ end
33
+
34
+ def default_bbar
35
+ super + [:test_access]
36
+ end
37
+
38
+ action :test_access do |a|
39
+ a.text = a.tooltip = 'Test Access'
40
+ end
41
+
25
42
  attribute :enum_state do |c|
26
43
  enum_column(c, Gemini::EnumState)
27
44
  end
45
+
46
+ endpoint :test_access do |c|
47
+ client.netzke_notify 'You have admin access'
48
+ end
28
49
  end
@@ -3,5 +3,21 @@ module Gemini
3
3
  self.table_name = 'gemini_bud_categories'
4
4
  has_mcfly append_only: true
5
5
  mcfly_validates_uniqueness_of :name
6
+
7
+ def self.create_from_promise_keyword_attrs(name:, group_id:)
8
+ create!(name: name, group_id: group_id).id
9
+ end
10
+
11
+ def self.create_from_promise_regular_attrs(name, group_id)
12
+ create!(name: name, group_id: group_id).id
13
+ end
14
+
15
+ def self.create_from_promise_mixed_attrs(name, group_id:)
16
+ create!(name: name, group_id: group_id).id
17
+ end
18
+
19
+ def self.create_from_promise_error
20
+ raise 'Something went wrong'
21
+ end
6
22
  end
7
23
  end
@@ -0,0 +1,9 @@
1
+ module Gemini
2
+ module PromiseHook
3
+ module TestHook
4
+ def self.run(opts)
5
+ Marty::Logger.log('TestHook', 'was called')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Endpoint access', js: true do
4
+ before do
5
+ populate_test_users
6
+ end
7
+
8
+ context 'as admin' do
9
+ before do
10
+ log_in_as('admin1')
11
+ wait_for_ajax
12
+ end
13
+
14
+ it 'can access test_access endpoint' do
15
+ press('Pricing Config.')
16
+ press('Loan Programs')
17
+ press('Test Access')
18
+ wait_for_ajax
19
+ expect(page).to have_content 'You have admin access'
20
+ end
21
+ end
22
+
23
+ context 'as dev' do
24
+ before do
25
+ log_in_as('dev1')
26
+ wait_for_ajax
27
+ end
28
+
29
+ it 'can not access test_access endpoint' do
30
+ press('Pricing Config.')
31
+ press('Loan Programs')
32
+ press('Test Access')
33
+ wait_for_ajax
34
+ expect(page).to have_content 'Permission Denied'
35
+ end
36
+ end
37
+ end
@@ -6,14 +6,20 @@ describe 'Jobs Dashboard', type: :feature, js: true, capybara: true do
6
6
  firstname: 'other',
7
7
  lastname: 'other',
8
8
  active: true)
9
- Marty::Promise.create title: 'Test Job 1',
9
+ Marty::Promise.create!(
10
+ title: 'Test Job 1',
10
11
  user: Marty::User.find_by(login: 'marty'),
11
12
  cformat: 'csv',
12
- start_dt: Time.now
13
- Marty::Promise.create title: 'Test Job 2',
13
+ start_dt: Time.now,
14
+ promise_type: 'delorean'
15
+ )
16
+ Marty::Promise.create!(
17
+ title: 'Test Job 2',
14
18
  user: other_user,
15
19
  cformat: 'csv',
16
- start_dt: Time.now
20
+ start_dt: Time.now,
21
+ promise_type: 'delorean'
22
+ )
17
23
 
18
24
  visit '/'
19
25
  all 'span', text: 'Sign in', minimum: 1
@@ -194,8 +194,9 @@ describe 'McflyModel' do
194
194
  1, 2)
195
195
  end
196
196
  end
197
- # x time should be 30x or more than y time
198
- expect(x.real / y.real).to be > 30
197
+ # x time should be 25x or more than y time
198
+ # Used to be 30x, but 30x sometimes fails on CI
199
+ expect(x.real / y.real).to be > 25
199
200
  end
200
201
  end
201
202
  end
@@ -28,6 +28,7 @@ describe Marty::Promise, slow: true do
28
28
  end
29
29
 
30
30
  after(:each) do
31
+ Marty::Log.delete_all
31
32
  Marty::Promise.where('parent_id IS NULL').destroy_all
32
33
  Timecop.return
33
34
  end
@@ -60,4 +61,149 @@ describe Marty::Promise, slow: true do
60
61
  expect(Marty::VwPromise.live_search('marty').size).to eq(10)
61
62
  expect(Marty::VwPromise.live_search('Admin').size).to eq(10)
62
63
  end
64
+
65
+ describe 'delorean' do
66
+ it 'processes result' do
67
+ expect(Marty::Promise.where(title: 'PromiseB').exists?).to be false
68
+
69
+ engine = Marty::ScriptSet.new.get_engine(NAME_B)
70
+ engine.background_eval(
71
+ 'Y',
72
+ {
73
+ 'p_title' => NAME_B,
74
+ 'p_hook' => Gemini::PromiseHook::TestHook
75
+ },
76
+ ['result']
77
+ )
78
+
79
+ promise = Marty::Promise.find_by(title: 'PromiseB')
80
+ promise.wait_for_result(Marty::Promise::DEFAULT_PROMISE_TIMEOUT)
81
+ promise.reload
82
+
83
+ expected = [{ 'a' => 1, 'b' => 1 }, { 'a' => 2, 'b' => 4 }, { 'a' => 3, 'b' => 9 }]
84
+ expect(promise.status).to be true
85
+ expect(promise.promise_type).to eq 'delorean'
86
+ expect(promise.result['error']).to be nil
87
+ expect(promise.result['result']).to eq expected
88
+
89
+ sleep 0.1 # Wait while hooks are executed after Promise was updated
90
+ log = Marty::Log.find_by(message_type: 'TestHook')
91
+ expect(log.message).to eq 'was called'
92
+ end
93
+
94
+ it 'fails on exception' do
95
+ expect(Marty::Promise.where(title: 'PromiseJ').exists?).to be false
96
+
97
+ engine = Marty::ScriptSet.new.get_engine(NAME_J)
98
+ engine.background_eval('FAILER', { 'p_title' => NAME_J }, ['a'])
99
+
100
+ promise = Marty::Promise.find_by(title: 'PromiseJ')
101
+ promise.wait_for_result(Marty::Promise::DEFAULT_PROMISE_TIMEOUT)
102
+ promise.reload
103
+
104
+ expect(promise.status).to be false
105
+ expect(promise.promise_type).to eq 'delorean'
106
+ expect(promise.result['error']).to eq 'I had an error'
107
+ end
108
+ end
109
+
110
+ describe 'ruby' do
111
+ let(:user) { Marty::User.find_by(login: 'marty') }
112
+
113
+ it 'processes result with regular attrs' do
114
+ Marty::Promises::Ruby::Create.call(
115
+ module_name: 'Gemini::BudCategory',
116
+ method_name: 'create_from_promise_regular_attrs',
117
+ method_args: ['test name', 1],
118
+ params: {
119
+ p_title: 'test_title',
120
+ _user_id: user.id,
121
+ p_hook: Gemini::PromiseHook::TestHook
122
+ }
123
+ )
124
+
125
+ promise = Marty::Promise.where(promise_type: 'ruby').last
126
+ promise.wait_for_result(Marty::Promise::DEFAULT_PROMISE_TIMEOUT)
127
+
128
+ promise.reload
129
+
130
+ bud_category = Gemini::BudCategory.order(:id).last
131
+ expect(bud_category.name).to eq 'test name'
132
+
133
+ sleep 0.1 # Wait while hooks are executed after Promise was updated
134
+ log = Marty::Log.find_by(message_type: 'TestHook')
135
+
136
+ expect(promise.status).to be true
137
+ expect(promise.promise_type).to eq 'ruby'
138
+ expect(promise.result['result']).to eq bud_category.id
139
+ expect(log.message).to eq 'was called'
140
+ end
141
+
142
+ it 'processes result with keyword attrs' do
143
+ Marty::Promises::Ruby::Create.call(
144
+ module_name: 'Gemini::BudCategory',
145
+ method_name: 'create_from_promise_keyword_attrs',
146
+ method_args: [group_id: 1, name: 'test name 2'],
147
+ params: {
148
+ _user_id: user.id,
149
+ }
150
+ )
151
+
152
+ promise = Marty::Promise.where(promise_type: 'ruby').last
153
+ promise.wait_for_result(Marty::Promise::DEFAULT_PROMISE_TIMEOUT)
154
+
155
+ promise.reload
156
+
157
+ bud_category = Gemini::BudCategory.order(:id).last
158
+ expect(bud_category.name).to eq 'test name 2'
159
+
160
+ expect(promise.status).to be true
161
+ expect(promise.promise_type).to eq 'ruby'
162
+ expect(promise.result['result']).to eq bud_category.id
163
+ end
164
+
165
+ it 'processes result with mixed attrs' do
166
+ Marty::Promises::Ruby::Create.call(
167
+ module_name: 'Gemini::BudCategory',
168
+ method_name: 'create_from_promise_mixed_attrs',
169
+ method_args: ['test name 3', { group_id: 1 }],
170
+ params: {
171
+ _user_id: user.id,
172
+ }
173
+ )
174
+
175
+ promise = Marty::Promise.where(promise_type: 'ruby').last
176
+ promise.wait_for_result(Marty::Promise::DEFAULT_PROMISE_TIMEOUT)
177
+
178
+ promise.reload
179
+
180
+ bud_category = Gemini::BudCategory.order(:id).last
181
+ expect(bud_category.name).to eq 'test name 3'
182
+
183
+ expect(promise.status).to be true
184
+ expect(promise.promise_type).to eq 'ruby'
185
+ expect(promise.result['result']).to eq bud_category.id
186
+ end
187
+
188
+ it 'fails on exception' do
189
+ Marty::Promises::Ruby::Create.call(
190
+ module_name: 'Gemini::BudCategory',
191
+ method_name: 'create_from_promise_error',
192
+ method_args: [],
193
+ params: {
194
+ _user_id: user.id,
195
+ }
196
+ )
197
+
198
+ promise = Marty::Promise.where(promise_type: 'ruby').last
199
+ promise.wait_for_result(Marty::Promise::DEFAULT_PROMISE_TIMEOUT)
200
+
201
+ promise.reload
202
+
203
+ expect(promise.status).to be false
204
+ expect(promise.promise_type).to eq 'ruby'
205
+ expect(promise.result['error']).to eq 'Something went wrong'
206
+ expect(promise.result['backtrace']).to_not be_empty
207
+ end
208
+ end
63
209
  end
@@ -150,7 +150,7 @@ module Marty; module RSpec; module Components
150
150
  def select_row_range(st, en)
151
151
  resid = run_js(<<-JS, 10.0)
152
152
  #{ext_var(grid, 'grid')}
153
- grid.getSelectionModel().selectRange(#{st-1}, #{en-1});
153
+ grid.getSelectionModel().selectRange(#{st - 1}, #{en - 1});
154
154
  JS
155
155
  wait_for_ajax
156
156
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marty
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.4
4
+ version: 2.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arman Bostani
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2019-04-16 00:00:00.000000000 Z
17
+ date: 2019-05-02 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: pg
@@ -311,6 +311,7 @@ files:
311
311
  - app/models/marty/delorean_rule.rb
312
312
  - app/models/marty/enum.rb
313
313
  - app/models/marty/enum_event_operation.rb
314
+ - app/models/marty/enum_promise_type.rb
314
315
  - app/models/marty/event.rb
315
316
  - app/models/marty/grid_index_boolean.rb
316
317
  - app/models/marty/grid_index_int4range.rb
@@ -332,6 +333,10 @@ files:
332
333
  - app/models/marty/user.rb
333
334
  - app/models/marty/user_role.rb
334
335
  - app/models/marty/vw_promise.rb
336
+ - app/services/marty/promises/delorean.rb
337
+ - app/services/marty/promises/delorean/create.rb
338
+ - app/services/marty/promises/ruby.rb
339
+ - app/services/marty/promises/ruby/create.rb
335
340
  - app/views/layouts/marty/application.html.erb
336
341
  - app/views/marty/diagnostic/diag.html.erb
337
342
  - app/views/marty/diagnostic/op.html.erb
@@ -381,6 +386,8 @@ files:
381
386
  - db/migrate/411_create_vw_promises.rb
382
387
  - db/migrate/500_add_api_class_to_marty_api_config.rb
383
388
  - db/migrate/501_create_dg_plpgsql_v1_fns.rb
389
+ - db/migrate/502_add_promise_type_enum.rb
390
+ - db/migrate/503_add_promise_type_to_promises.rb
384
391
  - db/seeds.rb
385
392
  - db/sql/lookup_grid_distinct_v1.sql
386
393
  - db/sql/query_grid_dir_v1.sql
@@ -411,6 +418,7 @@ files:
411
418
  - lib/marty/permissions.rb
412
419
  - lib/marty/promise_job.rb
413
420
  - lib/marty/promise_proxy.rb
421
+ - lib/marty/promise_ruby_job.rb
414
422
  - lib/marty/railtie.rb
415
423
  - lib/marty/relation.rb
416
424
  - lib/marty/rpc_call.rb
@@ -537,6 +545,7 @@ files:
537
545
  - spec/dummy/lib/assets/.gitkeep
538
546
  - spec/dummy/lib/class_list.rb
539
547
  - spec/dummy/lib/gemini/my_rule_script_set.rb
548
+ - spec/dummy/lib/gemini/promise_hook/test_hook.rb
540
549
  - spec/dummy/lib/gemini/xyz_rule_script_set.rb
541
550
  - spec/dummy/log/.gitkeep
542
551
  - spec/dummy/public/400.html
@@ -1551,6 +1560,7 @@ files:
1551
1560
  - spec/dummy/tmp/.gitkeep
1552
1561
  - spec/features/auth_app_spec.rb
1553
1562
  - spec/features/data_import_spec.rb
1563
+ - spec/features/endpoint_access.rb
1554
1564
  - spec/features/enum_spec.rb
1555
1565
  - spec/features/javascripts/job_dashboard_live_search.js.coffee
1556
1566
  - spec/features/javascripts/login.js.coffee