marty 3.0.0 → 3.0.1

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: 46bed6f3169beec2514318a672e7b116ef0031f679f774cb51bb14dc9fa935fd
4
- data.tar.gz: 74d430112df6bdf70bf9fa91d0efae7fbba4a0de08d53cb8f6cc9ab0d8029945
3
+ metadata.gz: cdb72d97f91a1f370754d64b41ca268addf4f001b061cc3e531f5993d7f5dae7
4
+ data.tar.gz: 1cefbf2a2e8d6e1176be63e876f7207fbc1fe8c07dd8b78b182ac4621e9f638b
5
5
  SHA512:
6
- metadata.gz: 8ba1fa3171e1887fe79331dcb623ef1936557e16b238117c84c3b6dff4d0b519db0596f64e0dd4fa4ec08bd2703e715c3d21bcf6d5d11c8cb46d1d4e1991a46d
7
- data.tar.gz: cdf887a7e66c5ce77f9c23f6467460d6b71b74f2e146fa7c7e1bfe79ad6d5534803a9930221376101683df68e360c0f30aba75072a7574c641fab4252210f818
6
+ metadata.gz: c82561e607fcb7ffd5e154e85279c7afd1c992867d495cee261310282fa55912247b09db6bff1d88d773a3e49fac0fe5f361fc2048c4f8cd4ea5f70a3d70db2c
7
+ data.tar.gz: 540081cb0f2474a32ebb47a5fc42741cedc717d2dc71ebc7bb772af7a8c24c475db5c6812b403b29b2997b95c1123ba576997b95af5cc84f5b1afa9163bc7168
data/.gitignore CHANGED
@@ -31,3 +31,8 @@ spec/dummy/.sass-cache
31
31
  # Files with command history for docker
32
32
  .bash_history.docker
33
33
  .pry_history.docker
34
+
35
+ # Keep empty for for ssh keys in docker
36
+ /.ssh-docker/*
37
+ !/.ssh-docker/.keep
38
+
File without changes
@@ -24,6 +24,9 @@ RUN curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrom
24
24
  && sed -i 's|HERE/chrome\"|HERE/chrome\" --disable-setuid-sandbox|g' /opt/google/chrome/google-chrome \
25
25
  && rm google-chrome.deb
26
26
 
27
+ # Install additional tools
28
+ RUN apt-get install -qq -y --no-install-recommends netcat-openbsd vim
29
+
27
30
  RUN gem install bundler
28
31
 
29
32
  ENV BUNDLE_PATH /bundle_box/bundle
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- marty (2.10.0)
4
+ marty (3.0.0)
5
5
  aws-sigv4 (~> 1.0, >= 1.0.2)
6
6
  axlsx (= 3.0.0pre)
7
7
  coderay
@@ -0,0 +1,39 @@
1
+ module Marty
2
+ class DataGridUserView < DataGridView
3
+ has_marty_permissions read: [:data_grid_editor, :admin, :dev]
4
+
5
+ def configure(c)
6
+ super
7
+
8
+ c.attributes =
9
+ [
10
+ :name,
11
+ :created_dt,
12
+ ]
13
+ c.editing = :inline
14
+ end
15
+
16
+ def default_bbar
17
+ [:edit_grid]
18
+ end
19
+
20
+ def get_records(params)
21
+ cur_perms = Mcfly.whodunnit.roles.map(&:to_sym)
22
+ model.where("permissions->'view' ?| ARRAY[:roles] OR "\
23
+ "permissions->'edit_data' ?| ARRAY[:roles] OR "\
24
+ "permissions->'edit_all' ?| ARRAY[:roles]",
25
+ roles: cur_perms).scoping do
26
+ super
27
+ end
28
+ end
29
+
30
+ def self.get_edit_permission(permissions)
31
+ cur_perms = current_user_roles.map(&:to_s)
32
+ ['edit_all', 'edit_data', 'view'].detect do |p|
33
+ permissions[p] - cur_perms != permissions[p]
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ DataGridUserView = Marty::DataGridUserView
@@ -98,6 +98,9 @@ module Marty; class DataGridView < McflyGridPanel
98
98
  :lenient,
99
99
  :data_type,
100
100
  :constraint,
101
+ :perm_view,
102
+ :perm_edit_data,
103
+ :perm_edit_all,
101
104
  :created_dt,
102
105
  ]
103
106
 
@@ -107,6 +110,19 @@ module Marty; class DataGridView < McflyGridPanel
107
110
  c.multi_select = false
108
111
  end
109
112
 
113
+ def set_perms(dg, data)
114
+ permstrs = %w[perm_view perm_edit_data perm_edit_all]
115
+ view, edit_data, edit_all = data.values_at(*permstrs).map do |plist|
116
+ Marty::RoleType.from_nice_names(plist)
117
+ end
118
+ dg.permissions = {
119
+ view: view.present? ? view : [],
120
+ edit_data: edit_data.present? ? edit_data : [],
121
+ edit_all: edit_all.present? ? edit_all : [],
122
+ }
123
+ dg.save!
124
+ end
125
+
110
126
  endpoint :add_window__add_form__submit do |params|
111
127
  data = ActiveSupport::JSON.decode(params[:data])
112
128
 
@@ -114,7 +130,8 @@ module Marty; class DataGridView < McflyGridPanel
114
130
  !config[:permissions][:create]
115
131
 
116
132
  begin
117
- DataGrid.create_from_import(data['name'], data['export'])
133
+ dg = DataGrid.create_from_import(data['name'], data['export'])
134
+ set_perms(dg, data)
118
135
  client.success = true
119
136
  client.netzke_on_submit_success
120
137
  rescue StandardError => exc
@@ -129,6 +146,7 @@ module Marty; class DataGridView < McflyGridPanel
129
146
 
130
147
  begin
131
148
  dg.update_from_import(data['name'], data['export'])
149
+ set_perms(dg, data)
132
150
  client.success = true
133
151
  client.netzke_on_submit_success
134
152
  rescue StandardError => exc
@@ -168,14 +186,8 @@ module Marty; class DataGridView < McflyGridPanel
168
186
  client.netzke_client_show_grid maxcount, res, 'Data Grid'
169
187
  end
170
188
 
171
- # placeholders for grid editing permission logic.
172
- # for now, this allows the rspec to control the permission
173
- def self.get_edit_edit_permission
174
- Marty::Config['grid_edit_edit_perm'] || 'edit_all'
175
- end
176
-
177
- def self.get_edit_save_permission
178
- Marty::Config['grid_edit_save_perm'] || 'edit_all'
189
+ def self.get_edit_permission(_permissions)
190
+ 'edit_all'
179
191
  end
180
192
 
181
193
  endpoint :edit_grid do |params|
@@ -193,9 +205,21 @@ module Marty; class DataGridView < McflyGridPanel
193
205
  vdim = md.map { |m| m['dir'] == 'v' && m['attr'] }.select { |v| v }
194
206
  hdim_en = hdim.map { |d| I18n.t('attributes.' + d, default: d) }
195
207
  vdim_en = vdim.map { |d| I18n.t('attributes.' + d, default: d) }
196
- name = "Editing Data Grid '#{dg.name}'"
197
- permission = Marty::DataGridView.get_edit_edit_permission
198
- client.edit_grid(record_id, hdim_en, vdim_en, res, name, permission)
208
+ perm = self.class.get_edit_permission(dg.permissions)
209
+ # should never happen
210
+ return client.netzke_notify('No permission to edit/view grid.') unless perm
211
+
212
+ doing = case perm
213
+ when 'view'
214
+ 'Viewing'
215
+ when 'edit_all'
216
+ 'Editing (all)'
217
+ when 'edit_data'
218
+ 'Editing (data only)'
219
+ end
220
+ name = "#{doing} Data Grid '#{dg.name}'"
221
+
222
+ client.edit_grid(record_id, hdim_en, vdim_en, res, name, perm)
199
223
  end
200
224
 
201
225
  endpoint :save_grid do |params|
@@ -213,10 +237,36 @@ module Marty; class DataGridView < McflyGridPanel
213
237
  def default_form_items
214
238
  [
215
239
  :name,
240
+ :perm_view, :perm_edit_data, :perm_edit_all,
216
241
  textarea_field(:export, height: 300, hide_label: true),
217
242
  ]
218
243
  end
219
244
 
245
+ ['view', 'edit_data', 'edit_all'].each do |p|
246
+ s = ('perm_' + p).to_s
247
+ attribute s do |c|
248
+ c.width = 100
249
+ c.flex = 1
250
+ c.label = I18n.t("data_grid_view_perms.#{s}")
251
+ c.type = :string
252
+ c.getter = lambda do |r|
253
+ Marty::RoleType.to_nice_names(r.permissions[p].sort)
254
+ end
255
+ store = Marty::RoleType.to_nice_names(::Marty::RoleType.get_all.sort.map)
256
+
257
+ # edit does not work without this dummy setter
258
+ c.setter = ->(r, v) {}
259
+
260
+ c.editor_config = {
261
+ multi_select: true,
262
+ empty_text: I18n.t('user_grid.select_roles'),
263
+ store: store,
264
+ type: :string,
265
+ xtype: :combo,
266
+ }
267
+ end
268
+ end
269
+
220
270
  component :edit_window do |c|
221
271
  super(c)
222
272
  c.width = 700
@@ -228,11 +278,11 @@ module Marty; class DataGridView < McflyGridPanel
228
278
  end
229
279
 
230
280
  attribute :name do |c|
231
- c.width = 120
281
+ c.width = 400
232
282
  end
233
283
 
234
284
  attribute :constraint do |c|
235
- c.width = 100
285
+ c.width = 150
236
286
  end
237
287
 
238
288
  attribute :hcols do |c|
@@ -4,6 +4,7 @@ require 'marty/api_log_view'
4
4
  require 'marty/config_view'
5
5
  require 'marty/data_grid_view'
6
6
  require 'marty/schedule_jobs_dashboard'
7
+ require 'marty/data_grid_user_view'
7
8
  require 'marty/event_view'
8
9
  require 'marty/import_type_view'
9
10
  require 'marty/new_posting_window'
@@ -96,6 +97,7 @@ class Marty::MainAuthApp < Marty::AuthApp
96
97
  icon_cls: 'fa fa-window-restore glyph',
97
98
  menu: [
98
99
  :data_grid_view,
100
+ :data_grid_user_view,
99
101
  :reporting,
100
102
  :scripting,
101
103
  :promise_view,
@@ -237,7 +239,14 @@ class Marty::MainAuthApp < Marty::AuthApp
237
239
  end
238
240
 
239
241
  action :data_grid_view do |a|
240
- a.text = I18n.t('data_grid_view', default: 'Data Grids')
242
+ a.text = I18n.t('data_grid_view')
243
+ a.handler = :netzke_load_component_by_action
244
+ a.icon_cls = 'fa fa-table glyph'
245
+ a.disabled = !self.class.has_any_perm?
246
+ end
247
+
248
+ action :data_grid_user_view do |a|
249
+ a.text = I18n.t('data_grid_user_view')
241
250
  a.handler = :netzke_load_component_by_action
242
251
  a.icon_cls = 'fa fa-table glyph'
243
252
  a.disabled = !self.class.has_any_perm?
@@ -388,6 +397,7 @@ class Marty::MainAuthApp < Marty::AuthApp
388
397
  component :config_view
389
398
 
390
399
  component :data_grid_view
400
+ component :data_grid_user_view
391
401
 
392
402
  component :event_view
393
403
 
@@ -33,9 +33,7 @@ module Marty; class UserView < Marty::Grid
33
33
  def self.set_roles(roles, user)
34
34
  roles = [] unless roles.present?
35
35
 
36
- roles = Marty::RoleType.get_all.select do |role|
37
- roles.include?(I18n.t("roles.#{role}", default: role))
38
- end
36
+ roles = ::Marty::RoleType.from_nice_names(roles)
39
37
 
40
38
  roles_in_user = user.user_roles.map(&:role)
41
39
  roles_to_delete = roles_in_user - roles
@@ -152,14 +150,10 @@ module Marty; class UserView < Marty::Grid
152
150
  c.type = :string
153
151
 
154
152
  c.getter = lambda do |r|
155
- r.user_roles.map do |ur|
156
- I18n.t("roles.#{ur.role}", default: ur.role)
157
- end
153
+ Marty::RoleType.to_nice_names(r.user_roles.map(&:role))
158
154
  end
159
155
 
160
- store = ::Marty::RoleType.get_all.sort.map do |role|
161
- I18n.t("roles.#{role}", default: role)
162
- end
156
+ store = ::Marty::RoleType.to_nice_names(::Marty::RoleType::VALUES.sort)
163
157
 
164
158
  c.editor_config = {
165
159
  multi_select: true,
@@ -360,13 +360,19 @@ class Marty::DataGrid < Marty::Base
360
360
  def export_array
361
361
  # add data type metadata row if not default
362
362
  lenstr = 'lenient' if lenient
363
- typestr = data_type unless [nil, DEFAULT_DATA_TYPE].member?(data_type) &&
364
- !constraint.present?
365
363
 
366
- len_dt = [lenstr, typestr].compact.join(' ')
367
-
368
- meta_rows = len_dt.present? || constraint.present? ?
369
- [[len_dt, constraint]] : []
364
+ typestr = data_type unless [nil, DEFAULT_DATA_TYPE].member?(data_type)
365
+ len_type = [lenstr, typestr].compact.join(' ')
366
+
367
+ meta_rows = if (lenient || typestr) && constraint
368
+ [[len_type, constraint]]
369
+ elsif lenient || typestr
370
+ [[len_type]]
371
+ elsif constraint
372
+ [['', constraint]]
373
+ else
374
+ []
375
+ end
370
376
 
371
377
  meta_rows += metadata.map do |inf|
372
378
  [inf['attr'], inf['type'], inf['dir'], inf['rs_keep'] || '']
@@ -5,6 +5,19 @@ class Marty::RoleType < Marty::Base
5
5
  'admin',
6
6
  'user_manager',
7
7
  'dev',
8
- 'viewer'
8
+ 'viewer',
9
+ 'data_grid_editor'
9
10
  ]
11
+
12
+ def self.from_nice_names(roles)
13
+ Marty::RoleType.get_all.select do |role|
14
+ roles.include?(I18n.t("roles.#{role}", default: role))
15
+ end
16
+ end
17
+
18
+ def self.to_nice_names(roles)
19
+ roles.map do |role|
20
+ I18n.t("roles.#{role}", default: role)
21
+ end
22
+ end
10
23
  end
@@ -15,8 +15,9 @@ module Marty
15
15
  end
16
16
 
17
17
  def self.call(params)
18
- user_perm = Marty::DataGridView.get_edit_save_permission
19
18
  rec_id = params['record_id']
19
+ dg = Marty::DataGrid.mcfly_pt('infinity').find_by(group_id: rec_id)
20
+ user_perm = Marty::DataGridView.get_edit_permission(dg.permissions)
20
21
  data = params['data']
21
22
  raise GridError.new('entered with view permissions', data, rec_id) if
22
23
  user_perm == 'view'
@@ -24,7 +25,6 @@ module Marty
24
25
  data_as_array = data.map do |row|
25
26
  row.keys.map { |key| row[key] }
26
27
  end
27
- dg = Marty::DataGrid.mcfly_pt('infinity').find_by(group_id: rec_id)
28
28
  vcnt = dg.metadata.select { |md| md['dir'] == 'v' }.count
29
29
  hcnt = dg.metadata.select { |md| md['dir'] == 'h' }.count
30
30
  cur_data_dim = [dg.data.length, dg.data[0].length]
@@ -18,8 +18,10 @@ en:
18
18
  create_posting: Create
19
19
  api_auth: API Authorization
20
20
  event_view: Event View
21
- data_grid_view: Data Grids
22
- data_grid: Data Grids
21
+ data_grid_view: Data Grids Admin
22
+ data_grid: Data Grids Admin
23
+ data_grid_user: Data Grids
24
+ data_grid_user_view: Data Grids
23
25
 
24
26
  data_import_view:
25
27
  import: Import
@@ -30,6 +32,7 @@ en:
30
32
  admin: Admin
31
33
  dev: Developer
32
34
  user_manager: User Manager
35
+ data_grid_editor: Data Grid Editor
33
36
 
34
37
  posting_types:
35
38
  BASE: BASE
@@ -176,3 +179,9 @@ en:
176
179
  grid:
177
180
  base:
178
181
  view_record: "%{model} (Read-only)"
182
+
183
+ data_grid_view_perms:
184
+ perm_view: Can View
185
+ perm_edit_data: Can Edit Data
186
+ perm_edit_all: Can Edit All
187
+
@@ -0,0 +1,16 @@
1
+ class AddDataGridPerms < ActiveRecord::Migration[4.2]
2
+ def self.up
3
+ default = {
4
+ view: [],
5
+ edit_data: [],
6
+ edit_all: []
7
+ }
8
+ table = :marty_data_grids
9
+ add_column table, :permissions, :jsonb, null: false, default: default
10
+ add_index table, "(permissions->'view')", using: "GIN",
11
+ name: :marty_data_grids_perm_idx
12
+ end
13
+ def self.down
14
+ remove_column :marty_data_grids, :permissions
15
+ end
16
+ end
@@ -24,6 +24,7 @@ services:
24
24
  - .:/opt/app:delegated
25
25
  - '.bash_history.docker:/root/.bash_history'
26
26
  - '.pry_history.docker:/root/.pry_history'
27
+ - '.ssh-docker:/root/.ssh'
27
28
  - bundle_box:/bundle_box
28
29
  tty: true
29
30
  stdin_open: true
@@ -1,7 +1,7 @@
1
1
  class Marty::DataConversion
2
2
  EXCEL_START_DATE = Date.parse('1/1/1900') - 2
3
3
 
4
- FLOAT_PAT = /^-?\d+(\.\d+)?$/
4
+ FLOAT_PAT = /\A-?\d+(\.?\d+)?([eE][-+]?[0-9]+)?\z/
5
5
 
6
6
  PATS = {
7
7
  integer: /^-?\d+(\.0+)?$/,
@@ -41,7 +41,7 @@ module Marty::Migrations
41
41
  SQL
42
42
 
43
43
  db_values = res.first['enum_range'].gsub(/[{"}]/, '').split(',')
44
- ex_values = klass::VALUES - db_values
44
+ ex_values = klass::VALUES.map(&:to_s) - db_values
45
45
 
46
46
  return if ex_values.empty?
47
47
 
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = '3.0.0'
2
+ VERSION = '3.0.1'
3
3
  end
@@ -35,15 +35,30 @@ class Marty::Diagnostic::Aws::Ec2Instance < Marty::Aws::Request
35
35
 
36
36
  def ec2_request action, params = {}
37
37
  resp = request({ action: action }, params)
38
- Hash.from_xml(resp)["#{action}Response"]
38
+ parsed = Hash.from_xml(resp)
39
+
40
+ # check AWS response for errors
41
+ error = parsed.dig('Response', 'Errors', 'Error')
42
+ raise Marty::Diagnostic::Aws::Error.new(action, error) if error
43
+
44
+ action_resp = parsed["#{action}Response"]
45
+ raise Marty::Diagnostic::Aws::Error.new(action, parsed) unless action_resp
46
+
47
+ action_resp
39
48
  end
40
49
 
41
50
  def get_tag
51
+ action = 'DescribeTags'
42
52
  params = { 'Filter.1.Name' => 'resource-id',
43
53
  'Filter.1.Value.1' => get_instance_id,
44
54
  'Filter.2.Name' => 'key',
45
55
  'Filter.2.Value.1' => 'Name' }
46
- ec2_request('DescribeTags', params)['tagSet']['item']['value']
56
+
57
+ action_resp = ec2_request(action, params)
58
+ tag = action_resp.dig('tagSet', 'item', 'value')
59
+ raise Marty::Diagnostic::Aws::Error.new(action, action_resp) unless tag
60
+
61
+ tag
47
62
  end
48
63
 
49
64
  def get_instances
@@ -0,0 +1,8 @@
1
+ class Marty::Diagnostic::Aws::Error < StandardError
2
+ attr_accessor :object
3
+
4
+ def initialize(action, object = nil)
5
+ super("#{action}: #{object.try(:[], 'Message') || 'Unexpected Response'}")
6
+ self.object = object
7
+ end
8
+ end
@@ -79,6 +79,7 @@ module Dummy
79
79
  :dev,
80
80
  :viewer,
81
81
  :user_manager,
82
+ :data_grid_editor,
82
83
  ]
83
84
  #config.marty.default_posting_type = 'BASE'
84
85
  config.secret_key_base = "SECRET_KEY_BASE"
@@ -2,6 +2,10 @@ require 'spec_helper'
2
2
  require 'marty_rspec'
3
3
 
4
4
  feature 'data grid view', js: true do
5
+ before(:all) do
6
+ self.use_transactional_tests = true
7
+ end
8
+
5
9
  before(:each) do
6
10
  marty_whodunnit
7
11
  Marty::Script.load_scripts
@@ -11,12 +15,22 @@ feature 'data grid view', js: true do
11
15
  n = File.basename(path, '.txt').camelize
12
16
  Marty::DataGrid.create_from_import(n, File.read(path), dt)
13
17
  end
18
+ u = Marty::User.create!(login: 'grid_user',
19
+ firstname: 'grid',
20
+ lastname: 'user',
21
+ active: true)
22
+ Marty::UserRole.create!(user_id: u.id, role: 'data_grid_editor')
23
+ @no_perm = { 'view' => [],
24
+ 'edit_data' => [],
25
+ 'edit_all' => [] }
14
26
  end
15
27
 
16
- def go_to_data_grids
28
+ def go_to_data_grids(admin: true)
29
+ log_in_as(admin ? 'marty' : 'grid_user')
17
30
  press('Applications')
18
- press('Data Grids')
19
- expect(page).to have_content 'Data Grids'
31
+ dest = 'Data Grids' + (admin ? ' Admin' : '')
32
+ press(dest)
33
+ expect(page).to have_content dest
20
34
  end
21
35
 
22
36
  # setup the info for the ext grid
@@ -24,7 +38,7 @@ feature 'data grid view', js: true do
24
38
  widths, rows = get_grid_info
25
39
  @grid = page.all(:xpath,
26
40
  ".//div[contains(@class, 'x-grid-item-container')]").
27
- reject { |e| e.text.include?('2016-12-31') }.first
41
+ reject { |e| e.text.include?('DataGrid1') }.first
28
42
  @gridx = @grid.native.rect.x
29
43
  @gridy = @grid.native.rect.y
30
44
  height = @grid.native.size.height
@@ -278,18 +292,97 @@ feature 'data grid view', js: true do
278
292
  expect(grid_meta).to eq(exp_meta)
279
293
  end
280
294
 
281
- it 'dg editor' do
295
+ it 'dg perms' do
282
296
  log_in_as('marty')
283
297
  go_to_data_grids
284
298
  dgv = netzke_find('data_grid_view')
285
299
  grids = dgv.get_col_vals('name', 5)
286
- context_test_all = ENV['DG_FEATURE_QUICK'] != 'true'
287
- [['edit_all', context_test_all],
300
+ set_one = lambda do |grid, perms|
301
+ pos = grids.index(grid) + 1
302
+ dgv.select_row(pos)
303
+ press('Edit')
304
+ wait_for_ajax
305
+ view_combo = netzke_find('Can View', 'combobox')
306
+ edit_data_combo = netzke_find('Can Edit Data', 'combobox')
307
+ edit_all_combo = netzke_find('Can Edit All', 'combobox')
308
+ view_combo.select_values(perms['view'].join(', '))
309
+ edit_data_combo.select_values(perms['edit_data'].join(', '))
310
+ edit_all_combo.select_values(perms['edit_all'].join(', '))
311
+ press('OK')
312
+ wait_for_ajax
313
+ end
314
+ dg1p = { 'view' => ['Data Grid Editor'],
315
+ 'edit_data' => ['Data Grid Editor'],
316
+ 'edit_all' => ['Admin'] }
317
+ dg2p = { 'view' => ['Data Grid Editor', 'User Manager'],
318
+ 'edit_data' => ['Data Grid Editor', 'User Manager'],
319
+ 'edit_all' => ['Data Grid Editor', 'Admin'] }
320
+ set_one.call('DataGrid1', dg1p)
321
+ set_one.call('DataGrid2', dg2p)
322
+ dg1 = Marty::DataGrid.mcfly_pt('infinity').find_by(name: 'DataGrid1')
323
+ dg2 = Marty::DataGrid.mcfly_pt('infinity').find_by(name: 'DataGrid2')
324
+ fixexp = lambda do |orig_h|
325
+ orig_h.each_with_object({}) do |(k, v), h|
326
+ h[k] = v.map do |vals|
327
+ vals.split(' ').map(&:downcase).join('_')
328
+ end.sort
329
+ end
330
+ end
331
+ exp1 = fixexp.call(dg1p)
332
+ exp2 = fixexp.call(dg2p)
333
+ fixgot = ->(orig_h) { Hash[orig_h.map { |k, v| [k, v.sort] }] }
334
+ got1 = fixgot.call(dg1.permissions)
335
+ got2 = fixgot.call(dg2.permissions)
336
+ cmp1 = struct_compare(got1, exp1)
337
+ cmp2 = struct_compare(got2, exp2)
338
+ expect(cmp1).to be_falsey
339
+ expect(cmp2).to be_falsey
340
+
341
+ set_one.call('DataGrid1', @no_perm)
342
+ set_one.call('DataGrid2', @no_perm)
343
+ dg1 = Marty::DataGrid.mcfly_pt('infinity').find_by(name: 'DataGrid1')
344
+ dg2 = Marty::DataGrid.mcfly_pt('infinity').find_by(name: 'DataGrid2')
345
+ expect(dg1.permissions).to eq(@no_perm)
346
+ expect(dg2.permissions).to eq(@no_perm)
347
+ end
348
+
349
+ it 'show grid' do
350
+ log_in_as('marty')
351
+ go_to_data_grids
352
+ dgv = netzke_find('data_grid_view')
353
+ grids = dgv.get_col_vals('name', 5)
354
+ pos = grids.index('DataGrid1') + 1
355
+ dgv.select_row(pos)
356
+ press('Show Grid')
357
+ expect(page).to have_content('Antwerp')
358
+ end
359
+
360
+ def set_perm(perm)
361
+ p = @no_perm + (perm ? { perm => ['data_grid_editor'] } : {})
362
+ Marty::DataGrid.mcfly_pt('infinity').each do |dg|
363
+ dg.permissions = p
364
+ dg.save!
365
+ end
366
+ end
367
+
368
+ it 'dg context menus' do
369
+ log_in_as('grid_user')
370
+ go_to_data_grids(admin: false)
371
+ dgv = netzke_find('data_grid_user_view')
372
+ context_test_all = ENV['DG_FEATURE_FULL'] == 'true'
373
+ [[nil, nil],
374
+ ['edit_all', context_test_all],
288
375
  ['edit_data', false],
289
376
  ['view', false]].each do |perm, all_cells|
377
+
378
+ set_perm(perm)
379
+ press('Refresh')
380
+ grids = dgv.get_col_vals('name', 5)
381
+ if perm.nil?
382
+ expect(grids).to be_nil
383
+ next
384
+ end
290
385
  grids.each do |grid|
291
- Marty::Config['grid_edit_edit_perm'] = perm
292
- Marty::Config['grid_edit_save_perm'] = perm
293
386
  pos = grids.index(grid) + 1
294
387
  dgv.select_row(pos)
295
388
  press('Edit Grid')
@@ -300,8 +393,14 @@ feature 'data grid view', js: true do
300
393
  wait_for_ajax
301
394
  end
302
395
  end
303
- Marty::Config['grid_edit_edit_perm'] = 'edit_all'
304
- Marty::Config['grid_edit_save_perm'] = 'edit_all'
396
+ end
397
+
398
+ it 'dg editor' do
399
+ set_perm('edit_all')
400
+ log_in_as('grid_user')
401
+ go_to_data_grids(admin: false)
402
+ dgv = netzke_find('data_grid_user_view')
403
+ grids = dgv.get_col_vals('name', 5)
305
404
 
306
405
  # now test some editing, saving, and cancel logic
307
406
  get_latest = lambda do
@@ -460,40 +559,5 @@ feature 'data grid view', js: true do
460
559
  press('Save')
461
560
  sleep 1
462
561
  validate_grid('1')
463
-
464
- Marty::Config['grid_edit_save_perm'] = 'view'
465
- pos = grids.index('DataGrid5') + 1
466
- dgv.select_row(pos)
467
- count = 1
468
- begin
469
- press('Edit Grid')
470
- wait_for_ajax
471
- grid_setup
472
- cell_edit(2, 3, '123.456')
473
- rescue StandardError => e
474
- if count > 0
475
- sleep 1
476
- retry
477
- end
478
- count -= 1
479
- end
480
- press('Save')
481
- errexp = 'error: save_grid: entered with view permissions'
482
- expect(page).to have_content(errexp)
483
- press('OK')
484
- wait_for_ajax
485
- context_click(2, 4, 4, click: true)
486
- Marty::Config['grid_edit_save_perm'] = 'edit_data'
487
- press('Save')
488
- errexp = 'error: save_grid: grid modification not allowed'
489
- expect(page).to have_content(errexp)
490
- exp_log = JSON.parse(File.read('spec/fixtures/misc/grid_log_errs.json'))
491
- got = Marty::Log.all.select(:message, :details).attributes.map do |l|
492
- newl = l.except('id')
493
- newl['details'].delete('rec_id')
494
- newl
495
- end
496
- cmp = struct_compare(got, exp_log)
497
- expect(cmp).to be_falsey
498
562
  end
499
563
  end
@@ -419,7 +419,7 @@ EOL
419
419
  %Q({"grid1":"DataGrid1"}),
420
420
  %Q({"grid1":"DataGrid1"})])
421
421
  press('Applications')
422
- press('Data Grids')
422
+ press('Data Grids Admin')
423
423
  dgv = netzke_find('data_grid_view')
424
424
  cvs = dgv.get_col_vals(:name, 4, 0)
425
425
  ind1 = cvs.index('DataGrid1') + 1
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- feature 'under Sytem menu, User Management worflows', js: true do
3
+ feature 'under System menu, User Management worflows', js: true do
4
4
  def go_to_user_view
5
5
  press('System')
6
6
  press('User Management')
@@ -77,6 +77,7 @@ feature 'under Sytem menu, User Management worflows', js: true do
77
77
 
78
78
  and_by 'check row got edited' do
79
79
  wait_for_ajax
80
+ r2 = user_view.get_row_vals(2)
80
81
  expect(user_view.get_row_vals(2)).to netzke_include(
81
82
  login: 'new_login',
82
83
  firstname: 'new_fname',
@@ -114,6 +115,9 @@ feature 'under Sytem menu, User Management worflows', js: true do
114
115
  by 'check buttons' do
115
116
  find(:btn, 'New User', match: :first)
116
117
  user_view.select_row(1)
118
+ expect { find(:btn, 'New User') }.not_to raise_error
119
+ expect { find(:btn, 'Edit') }.not_to raise_error
120
+ expect { find(:btn, 'Delete') }.not_to raise_error
117
121
  expect(btn_disabled?('New User')).to be_falsy
118
122
  expect(btn_disabled?('Edit')).to be_falsy
119
123
  expect(btn_disabled?('Delete')).to be_falsy
@@ -127,6 +131,9 @@ feature 'under Sytem menu, User Management worflows', js: true do
127
131
  by 'check buttons' do
128
132
  find(:btn, 'New User', match: :first)
129
133
  user_view.select_row(1)
134
+ expect { find(:btn, 'New User') }.not_to raise_error
135
+ expect { find(:btn, 'Edit') }.not_to raise_error
136
+ expect { find(:btn, 'Delete') }.not_to raise_error
130
137
  expect(btn_disabled?('New User')).to be_falsy
131
138
  expect(btn_disabled?('Edit')).to be_falsy
132
139
  expect(btn_disabled?('Delete')).to be_falsy
@@ -140,9 +147,10 @@ feature 'under Sytem menu, User Management worflows', js: true do
140
147
  user_view = netzke_find('user_view')
141
148
  by 'check buttons' do
142
149
  user_view.select_row(1)
143
- expect(page).not_to have_content('New User')
144
- expect(page).not_to have_content('Edit')
145
- expect(page).not_to have_content('Delete')
150
+ err = /Unable to find visible btn/
151
+ expect { find(:btn, 'New User') }.to raise_error(err)
152
+ expect { find(:btn, 'Edit') }.to raise_error(err)
153
+ expect { find(:btn, 'Delete') }.to raise_error(err)
146
154
  end
147
155
  end
148
156
 
@@ -153,9 +161,10 @@ feature 'under Sytem menu, User Management worflows', js: true do
153
161
  user_view = netzke_find('user_view')
154
162
  by 'check buttons' do
155
163
  user_view.select_row(1)
156
- expect(page).not_to have_content('New User')
157
- expect(page).not_to have_content('Edit')
158
- expect(page).not_to have_content('Delete')
164
+ err = /Unable to find visible btn/
165
+ expect { find(:btn, 'New User') }.to raise_error(err)
166
+ expect { find(:btn, 'Edit') }.to raise_error(err)
167
+ expect { find(:btn, 'Delete') }.to raise_error(err)
159
168
  end
160
169
  end
161
170
  end
@@ -814,7 +814,8 @@ EOS
814
814
  # rubocop:disable Style/NestedTernaryOperator
815
815
  type_str = type == 'float' ? (explicit_float ? 'float' : nil) : type
816
816
  # rubocop:enable Style/NestedTernaryOperator
817
- top = [lenient_str, type_str].compact.join(' ') + "\t" + constraint + "\n"
817
+ con_part = constraint.present? ? "\t" + constraint : ''
818
+ top = [lenient_str, type_str].compact.join(' ') + con_part + "\n"
818
819
  (top =~ /\A\s*\z/ ? '' : top) +
819
820
  <<~EOS
820
821
  b\tboolean\tv
@@ -836,8 +837,8 @@ EOS
836
837
  tests = JSON.parse(File.read('spec/fixtures/json/data_grid.json'))
837
838
  aggregate_failures do
838
839
  tests.each do |test|
839
- keys = %w[id type constraint values error]
840
- id, type, constraint, values, error = test.values_at(*keys)
840
+ keys = %w[id type constraint values error line1]
841
+ id, type, constraint, values, error, line1 = test.values_at(*keys)
841
842
  err_re = Regexp.new(error) if error
842
843
  # for float, do both ex- and implicit declaration
843
844
  exfls = type == 'float' ? [true, false] : [true]
@@ -848,7 +849,21 @@ EOS
848
849
  got = nil
849
850
  tnam = "Test #{id} lenient=#{lenient} exfl=#{exfl}"
850
851
  begin
851
- dg_from_import(tnam, grid)
852
+ dg = dg_from_import(tnam, grid)
853
+
854
+ # make sure export of line1 works correctly
855
+ # when dg is lenient and/or has constraint and/or
856
+ # not float
857
+ next unless lenient || constraint.present? ||
858
+ type != 'float'
859
+
860
+ # also skip grids where we included float explicitly
861
+ # because export will convert back to implicit
862
+ next if type == 'float' && exfl
863
+
864
+ dga = dg.export_array
865
+ line1 = dga.first.first.join("\t") + "\n"
866
+ expect(line1).to eq(grid.lines.first)
852
867
  rescue StandardError => e
853
868
  got = e.message
854
869
  end
@@ -53,7 +53,8 @@ module Marty; module RSpec; module Netzke
53
53
  wait_for_element do
54
54
  begin
55
55
  cmp = first("a[data-qtip='#{button_name}']")
56
- cmp ||= first(:xpath, './/a', text: button_name.to_s)
56
+ cmp ||= all(:xpath, './/a', text: button_name.to_s).
57
+ detect { |c| c.text == button_name.to_s }
57
58
  cmp ||= find(:btn, button_name, match: :first)
58
59
  cmp.click
59
60
  true
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: 3.0.0
4
+ version: 3.0.1
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-06-24 00:00:00.000000000 Z
17
+ date: 2019-07-05 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: pg
@@ -230,6 +230,7 @@ files:
230
230
  - ".rspec"
231
231
  - ".rubocop.yml"
232
232
  - ".rubocop_todo.yml"
233
+ - ".ssh-docker/.keep"
233
234
  - ".travis.yml"
234
235
  - Dockerfile.dummy
235
236
  - Gemfile
@@ -256,6 +257,7 @@ files:
256
257
  - app/components/marty/auth_app/client/auth_app.js
257
258
  - app/components/marty/base_rule_view.rb
258
259
  - app/components/marty/config_view.rb
260
+ - app/components/marty/data_grid_user_view.rb
259
261
  - app/components/marty/data_grid_view.rb
260
262
  - app/components/marty/data_grid_view/client/data_grid_edit.js
261
263
  - app/components/marty/delorean_rule_view.rb
@@ -394,6 +396,7 @@ files:
394
396
  - db/migrate/105_create_marty_grid_index_booleans.rb
395
397
  - db/migrate/106_make_grid_indexes_nullable.rb
396
398
  - db/migrate/107_add_data_grid_constraint.rb
399
+ - db/migrate/108_add_data_grid_perms.rb
397
400
  - db/migrate/200_create_marty_event_operation_enum.rb
398
401
  - db/migrate/201_create_marty_events.rb
399
402
  - db/migrate/202_add_completion_status_to_event.rb
@@ -462,6 +465,7 @@ files:
462
465
  - marty.gemspec
463
466
  - other/marty/api/base.rb
464
467
  - other/marty/diagnostic/aws/ec2_instance.rb
468
+ - other/marty/diagnostic/aws/error.rb
465
469
  - other/marty/diagnostic/base.rb
466
470
  - other/marty/diagnostic/collection.rb
467
471
  - other/marty/diagnostic/connections.rb