marty 2.6.4 → 2.6.5

Sign up to get free protection for your applications and to get access to all the features.
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