marty 3.0.0 → 3.0.1

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: 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