etna 0.1.51 → 0.2.0

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/bin/etna +2 -2
  3. data/etna.completion +75 -50
  4. data/lib/commands.rb +10 -8
  5. data/lib/etna/application.rb +33 -0
  6. data/lib/etna/auth.rb +5 -2
  7. data/lib/etna/client.rb +7 -12
  8. data/lib/etna/clients/base_client.rb +10 -2
  9. data/lib/etna/clients/gnomon/client.rb +20 -0
  10. data/lib/etna/clients/gnomon/models.rb +33 -0
  11. data/lib/etna/clients/gnomon.rb +2 -0
  12. data/lib/etna/clients/janus/client.rb +26 -0
  13. data/lib/etna/clients/janus/models.rb +16 -0
  14. data/lib/etna/clients/janus/workflows/generate_token_workflow.rb +1 -1
  15. data/lib/etna/clients/magma/client.rb +126 -6
  16. data/lib/etna/clients/magma/models.rb +64 -22
  17. data/lib/etna/clients/magma/workflows/model_synchronization_workflow.rb +2 -4
  18. data/lib/etna/clients/metis/client.rb +66 -2
  19. data/lib/etna/clients/metis/models.rb +138 -85
  20. data/lib/etna/clients/metis/workflows/ingest_metis_data_workflow.rb +15 -5
  21. data/lib/etna/clients/metis/workflows/metis_upload_workflow.rb +1 -0
  22. data/lib/etna/clients/models.rb +19 -0
  23. data/lib/etna/clients/polyphemus/client.rb +96 -0
  24. data/lib/etna/clients.rb +2 -0
  25. data/lib/etna/controller.rb +31 -6
  26. data/lib/etna/cross_origin.rb +1 -1
  27. data/lib/etna/cwl.rb +1 -1
  28. data/lib/etna/errors.rb +8 -0
  29. data/lib/etna/filesystem.rb +5 -170
  30. data/lib/etna/hmac.rb +4 -4
  31. data/lib/etna/instrumentation.rb +6 -6
  32. data/lib/etna/redirect.rb +1 -1
  33. data/lib/etna/remote.rb +18 -2
  34. data/lib/etna/route.rb +3 -0
  35. data/lib/etna/spec/event_log.rb +16 -0
  36. data/lib/etna/spec/vcr.rb +3 -3
  37. data/lib/etna/test_auth.rb +1 -1
  38. data/lib/etna/user.rb +1 -5
  39. data/lib/helpers.rb +8 -0
  40. metadata +22 -5
@@ -0,0 +1,33 @@
1
+ require_relative '../base_client'
2
+
3
+ # TODO: In the near future, I'd like to transition to specifying apis via SWAGGER and generating model stubs from the
4
+ # common definitions. For nowe I've written them out by hand here.
5
+ module Etna
6
+ module Clients
7
+ class Gnomon < Etna::Clients::BaseClient
8
+ class ProjectRulesResponse
9
+ attr_reader :raw
10
+
11
+ def initialize(raw = {})
12
+ @raw = raw
13
+ end
14
+
15
+ def rules
16
+ Rules.new(raw['rules'])
17
+ end
18
+ end
19
+
20
+ class Rules
21
+ attr_reader :raw
22
+
23
+ def initialize(raw = {})
24
+ @raw = raw
25
+ end
26
+
27
+ def [](model_name)
28
+ raw[model_name.to_s]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,2 @@
1
+ require_relative './gnomon/client'
2
+ require_relative './gnomon/models'
@@ -40,6 +40,14 @@ module Etna
40
40
  end
41
41
  end
42
42
 
43
+ def update_project(update_project_request = UpdateProjectRequest.new)
44
+ @etna_client.post(
45
+ "/api/admin/#{update_project_request.project_name}/update",
46
+ update_project_request) do |res|
47
+ # Redirect, no response data
48
+ end
49
+ end
50
+
43
51
  def update_permission(update_permission_request = UpdatePermissionRequest.new)
44
52
  @etna_client.post(
45
53
  "/api/admin/#{update_permission_request.project_name}/update_permission",
@@ -74,6 +82,24 @@ module Etna
74
82
 
75
83
  response.body
76
84
  end
85
+
86
+ def get_project_stats(get_stats_request = GetStatsRequest.new)
87
+ query = ""
88
+ unless get_stats_request.project_names.nil?
89
+ query = "?"
90
+ query += get_stats_request.project_names.map do |name|
91
+ "projects[]=#{name}"
92
+ end.join('&')
93
+ end
94
+
95
+ json = nil
96
+
97
+ @etna_client.get("/api/stats/projects#{query}") do |res|
98
+ json = JSON.parse(res.body, symbolize_names: true)
99
+ end
100
+
101
+ json
102
+ end
77
103
  end
78
104
  end
79
105
  end
@@ -31,6 +31,14 @@ module Etna
31
31
  end
32
32
  end
33
33
 
34
+ class UpdateProjectRequest < Struct.new(:project_name, :resource, :requires_agreement, :cc_text, :contact_email, :project_name_full, keyword_init: true)
35
+ include JsonSerializableStruct
36
+
37
+ def initialize(**params)
38
+ super({}.update(params))
39
+ end
40
+ end
41
+
34
42
  class UpdatePermissionRequest < Struct.new(:project_name, :email, :role, :privileged, :affiliation, keyword_init: true)
35
43
  include JsonSerializableStruct
36
44
 
@@ -132,6 +140,14 @@ module Etna
132
140
  @raw
133
141
  end
134
142
  end
143
+
144
+ class GetStatsRequest < Struct.new(:project_names, keyword_init: true)
145
+ include JsonSerializableStruct
146
+
147
+ def initialize(**params)
148
+ super({}.update(params))
149
+ end
150
+ end
135
151
  end
136
152
  end
137
153
  end
@@ -25,7 +25,7 @@ module Etna
25
25
  until private_key_file
26
26
  puts "Location of private key file?"
27
27
  private_key_file = ::File.expand_path(STDIN.gets.chomp)
28
- unless File.exists?(private_key_file)
28
+ unless ::File.exist?(private_key_file)
29
29
  puts "No such file."
30
30
  private_key_file = nil
31
31
  end
@@ -56,9 +56,13 @@ module Etna
56
56
  # attribute_names: "all"
57
57
  # }
58
58
  def retrieve(retrieval_request = RetrievalRequest.new)
59
+ if retrieval_request.is_a?(Hash)
60
+ retrieval_request = RetrievalRequest.new(retrieval_request)
61
+ end
62
+
59
63
  json = nil
60
64
  @etna_client.post('/retrieve', retrieval_request) do |res|
61
- json = JSON.parse(res.body)
65
+ json = safe_parse(res)
62
66
  end
63
67
 
64
68
  RetrievalResponse.new(json)
@@ -69,7 +73,7 @@ module Etna
69
73
  def query(query_request = QueryRequest.new)
70
74
  json = nil
71
75
  @etna_client.post('/query', query_request) do |res|
72
- json = JSON.parse(res.body)
76
+ json = safe_parse(res)
73
77
  end
74
78
 
75
79
  QueryResponse.new(json)
@@ -77,17 +81,124 @@ module Etna
77
81
 
78
82
  def update(update_request = UpdateRequest.new)
79
83
  json = nil
84
+
80
85
  @etna_client.multipart_post('/update', update_request.encode_multipart_content) do |res|
81
- json = JSON.parse(res.body)
86
+ json = safe_parse(res)
82
87
  end
83
88
 
84
89
  UpdateResponse.new(json)
85
90
  end
86
91
 
87
- def update_json(update_request = UpdateRequest.new)
92
+ class Pager
93
+ def initialize(client, update_request, page_size)
94
+ @client = client
95
+
96
+ @update_request = update_request
97
+
98
+ @page_size = [ page_size || 2, 2 ].max
99
+ end
100
+
101
+ def response
102
+ page_count = 0
103
+ page = {}
104
+
105
+ results = UpdateResponse.new({"models" => {}})
106
+
107
+ flat_records.each do |flat_record|
108
+ page_count += 1 + flat_record[:tables].length
109
+
110
+ add_to_page( page, flat_record )
111
+
112
+ flat_record[:tables].each do |table_record|
113
+ add_to_page(page, table_record)
114
+ end
115
+
116
+ if page_count >= @page_size
117
+ results.update(update_part(page))
118
+ page = {}
119
+ page_count = 0
120
+ end
121
+ end
122
+
123
+ results.update(update_part(page)) if page_count > 0
124
+
125
+ return results
126
+ end
127
+
128
+ def revisions
129
+ @update_request.revisions
130
+ end
131
+
132
+ def flat_records
133
+ project = @client.retrieve(
134
+ project_name: @update_request.project_name,
135
+ hide_templates: false
136
+ )
137
+
138
+ flat_records = {}
139
+ revisions.each do |model_name, records|
140
+ records.each do |record_name, record|
141
+ flat = {
142
+ model_name: model_name,
143
+ record_name: record_name,
144
+ record: record
145
+ }
146
+ flat_name = "#{model_name}.#{record_name}"
147
+
148
+ # if the update sets a table attribute, attach table records to their parent so they are submitted together
149
+ flat_tables = []
150
+ record.each do |attribute_name, value|
151
+ att = project.models.model(model_name).template.attributes.attribute(attribute_name)
152
+ if att.attribute_type == 'table'
153
+ value.each do |table_entry_name|
154
+ table_record = revisions[ att.link_model_name ][table_entry_name]
155
+ flat_tables.push(
156
+ {
157
+ model_name: att.link_model_name,
158
+ record_name: table_entry_name,
159
+ record: table_record
160
+ }
161
+ )
162
+
163
+ # exclude the attached table record from the flattened list of records
164
+ flat_records[ "#{ att.link_model_name }.#{table_entry_name}" ] = nil
165
+ end
166
+ end
167
+ end
168
+ flat[:tables] = flat_tables
169
+ flat_records[ flat_name ] = flat unless flat_records.has_key?(flat_name)
170
+ end
171
+ end
172
+
173
+ return flat_records.values.compact.shuffle
174
+ end
175
+
176
+ def update_part(page)
177
+ @client.update_json(
178
+ UpdateRequest.new(
179
+ project_name: @update_request.project_name,
180
+ revisions: page,
181
+ dry_run: @update_request.dry_run,
182
+ autolink: @update_request.autolink
183
+ )
184
+ )
185
+ end
186
+
187
+ def add_to_page(page, entry)
188
+ page[ entry[:model_name] ] ||= {}
189
+ page[ entry[:model_name] ][ entry[:record_name] ] = entry[:record]
190
+ end
191
+ end
192
+
193
+ def update_json(update_request = UpdateRequest.new, page_size=nil)
88
194
  json = nil
195
+
196
+ if page_size
197
+ return Etna::Clients::Magma::Pager.new(self, update_request, page_size).response
198
+ end
199
+
89
200
  @etna_client.post('/update', update_request) do |res|
90
- json = JSON.parse(res.body)
201
+ json = safe_parse(res)
91
202
  end
92
203
 
93
204
  UpdateResponse.new(json)
@@ -96,11 +207,20 @@ module Etna
96
207
  def update_model(update_model_request = UpdateModelRequest.new)
97
208
  json = nil
98
209
  @etna_client.post('/update_model', update_model_request) do |res|
99
- json = JSON.parse(res.body)
210
+ json = safe_parse(res)
100
211
  end
101
212
 
102
213
  UpdateModelResponse.new(json)
103
214
  end
215
+
216
+ def set_flags(project_name:, flags:)
217
+ json = nil
218
+ @etna_client.post("/flags/#{project_name}", { flags: flags }) do |res|
219
+ json = safe_parse(res)
220
+ end
221
+
222
+ json
223
+ end
104
224
  end
105
225
  end
106
226
  end
@@ -13,8 +13,8 @@ module Etna
13
13
  class RetrievalRequest < Struct.new(:model_name, :attribute_names, :record_names, :project_name, :page, :page_size, :order, :filter, :hide_templates, keyword_init: true)
14
14
  include JsonSerializableStruct
15
15
 
16
- def initialize(**params)
17
- super({model_name: 'all', attribute_names: 'all', record_names: []}.update(params))
16
+ def initialize(params = {}, **kwds)
17
+ super(**{model_name: 'all', attribute_names: 'all', record_names: []}.update(params).update(kwds))
18
18
  end
19
19
  end
20
20
 
@@ -22,12 +22,12 @@ module Etna
22
22
  include JsonSerializableStruct
23
23
  end
24
24
 
25
- class UpdateRequest < Struct.new(:revisions, :project_name, :dry_run, keyword_init: true)
25
+ class UpdateRequest < Struct.new(:revisions, :project_name, :dry_run, :autolink, keyword_init: true)
26
26
  include JsonSerializableStruct
27
27
  include MultipartSerializableNestedHash
28
28
 
29
- def initialize(**params)
30
- super({revisions: {}, dry_run: false}.update(params))
29
+ def initialize(params = {}, **kwds)
30
+ super(**{revisions: {}, dry_run: false}.update(params).update(kwds))
31
31
  end
32
32
 
33
33
  def update_revision(model_name, record_name, attrs)
@@ -49,8 +49,8 @@ module Etna
49
49
  class UpdateModelRequest < Struct.new(:project_name, :actions, keyword_init: true)
50
50
  include JsonSerializableStruct
51
51
 
52
- def initialize(**params)
53
- super({actions: []}.update(params))
52
+ def initialize(params = {}, **kwds)
53
+ super(**{actions: []}.update(params).update(kwds))
54
54
  end
55
55
 
56
56
  def add_action(action)
@@ -58,27 +58,35 @@ module Etna
58
58
  end
59
59
  end
60
60
 
61
- class AddModelAction < Struct.new(:action_name, :model_name, :parent_model_name, :parent_link_type, :identifier, :date_shift_root, keyword_init: true)
61
+ class AddModelAction < Struct.new(:action_name, :model_name, :parent_model_name, :parent_link_type, :identifier, :date_shift_root, :template_project_name, :template_model_name, keyword_init: true)
62
62
  include JsonSerializableStruct
63
63
 
64
- def initialize(**args)
65
- super({action_name: 'add_model', date_shift_root: false}.update(args))
64
+ def initialize(args = {}, **kwds)
65
+ super(**{action_name: 'add_model', date_shift_root: false}.update(args).update(kwds))
66
66
  end
67
67
  end
68
68
 
69
69
  class SetDateShiftRootAction < Struct.new(:action_name, :model_name, :date_shift_root, keyword_init: true)
70
70
  include JsonSerializableStruct
71
71
 
72
+ def initialize(args = {}, **kwds)
73
+ super(**{action_name: 'set_date_shift_root'}.update(args).update(kwds))
74
+ end
75
+ end
76
+
77
+ class SetModelTemplateAction < Struct.new(:action_name, :model_name, :template_project_name, :template_model_name, :clear_template, keyword_init: true)
78
+ include JsonSerializableStruct
79
+
72
80
  def initialize(**args)
73
- super({action_name: 'set_date_shift_root'}.update(args))
81
+ super({action_name: 'set_model_template', clear_template: false}.update(args))
74
82
  end
75
83
  end
76
84
 
77
85
  class AddAttributeAction < Struct.new(:action_name, :model_name, :attribute_name, :type, :description, :display_name, :format_hint, :hidden, :index, :link_model_name, :read_only, :attribute_group, :restricted, :unique, :validation, keyword_init: true)
78
86
  include JsonSerializableStruct
79
87
 
80
- def initialize(**args)
81
- super({action_name: 'add_attribute'}.update(args))
88
+ def initialize(args = {}, **kwds)
89
+ super(**{action_name: 'add_attribute'}.update(args).update(kwds))
82
90
  end
83
91
 
84
92
  def attribute_type=(val)
@@ -101,8 +109,8 @@ module Etna
101
109
  class AddLinkAction < Struct.new(:action_name, :links, keyword_init: true)
102
110
  include JsonSerializableStruct
103
111
 
104
- def initialize(**args)
105
- super({action_name: 'add_link', links: []}.update(args))
112
+ def initialize(args = {}, **kwds)
113
+ super(**{action_name: 'add_link', links: []}.update(args).update(kwds))
106
114
  end
107
115
  end
108
116
 
@@ -113,16 +121,16 @@ module Etna
113
121
  class AddProjectAction < Struct.new(:action_name, :no_metis_bucket, keyword_init: true)
114
122
  include JsonSerializableStruct
115
123
 
116
- def initialize(**args)
117
- super({action_name: 'add_project'}.update(args))
124
+ def initialize(args = {}, **kwds)
125
+ super(**{action_name: 'add_project'}.update(args).update(kwds))
118
126
  end
119
127
  end
120
128
 
121
129
  class UpdateAttributeAction < Struct.new(:action_name, :model_name, :attribute_name, :description, :display_name, :format_hint, :hidden, :index, :link_model_name, :read_only, :attribute_group, :restricted, :unique, :validation, keyword_init: true)
122
130
  include JsonSerializableStruct
123
131
 
124
- def initialize(**args)
125
- super({action_name: 'update_attribute'}.update(args))
132
+ def initialize(args = {}, **kwds)
133
+ super(**{action_name: 'update_attribute'}.update(args).update(kwds))
126
134
  end
127
135
 
128
136
  def as_json
@@ -133,8 +141,8 @@ module Etna
133
141
  class RenameAttributeAction < Struct.new(:action_name, :model_name, :attribute_name, :new_attribute_name, keyword_init: true)
134
142
  include JsonSerializableStruct
135
143
 
136
- def initialize(**args)
137
- super({action_name: 'rename_attribute'}.update(args))
144
+ def initialize(args = {}, **kwds)
145
+ super(**{action_name: 'rename_attribute'}.update(args).update(kwds))
138
146
  end
139
147
  end
140
148
 
@@ -159,6 +167,12 @@ module Etna
159
167
  def models
160
168
  Models.new(raw['models'])
161
169
  end
170
+
171
+ def update(response)
172
+ response.models.each do |model_name, model|
173
+ models.update( model_name, model )
174
+ end
175
+ end
162
176
  end
163
177
 
164
178
  class UpdateModelResponse < RetrievalResponse
@@ -219,6 +233,12 @@ module Etna
219
233
  Model.new(raw[model_key.to_s])
220
234
  end
221
235
 
236
+ def each
237
+ model_keys.each do |model_key|
238
+ yield model_key, model(model_key)
239
+ end
240
+ end
241
+
222
242
  def all
223
243
  raw.values.map { |r| Model.new(r) }
224
244
  end
@@ -229,6 +249,20 @@ module Etna
229
249
  Models.new({}.update(raw).update(raw_update))
230
250
  end
231
251
 
252
+ def update(model_name, model)
253
+ raw[ model_name.to_s ] ||= {}
254
+ raw[ model_name.to_s ]["template"] = model.template.raw
255
+ raw[ model_name.to_s ]["documents"] ||= {}
256
+ raw[ model_name.to_s ]["documents"].update(model.documents.raw)
257
+ end
258
+
259
+ def is_table?(model_name)
260
+ parent_model_name = self.model(model_name)&.template&.parent
261
+ parent_model = self.model(parent_model_name)
262
+ parent_attribute = parent_model&.template&.attributes&.attribute(model_name)
263
+ parent_attribute&.attribute_type == 'table'
264
+ end
265
+
232
266
  # Can look up reciprocal links by many means. At minimum, a source model or model name must be provided,
233
267
  # and either a link_attribute_name, attribute, or link_model.
234
268
  def find_reciprocal(
@@ -238,7 +272,7 @@ module Etna
238
272
  attribute: model&.template&.attributes&.attribute(link_attribute_name),
239
273
  link_model: self.model(attribute&.link_model_name)
240
274
  )
241
- return nil if model.nil? || model.name.nil?
275
+ return nil if model.nil?
242
276
 
243
277
  return link_model&.template&.attributes&.all&.find { |a| a.name == link_attribute_name } if link_attribute_name
244
278
 
@@ -371,6 +405,14 @@ module Etna
371
405
  Attributes.new(raw['attributes'] ||= {})
372
406
  end
373
407
 
408
+ def template_project_name
409
+ raw['template_project_name']
410
+ end
411
+
412
+ def template_model_name
413
+ raw['template_model_name']
414
+ end
415
+
374
416
  def build_attributes
375
417
  Attributes.new(raw['attributes'] ||= {})
376
418
  end
@@ -249,10 +249,8 @@ module Etna
249
249
  template = source_model.template
250
250
 
251
251
  add_model_action = AddModelAction.new(
252
- {
253
- model_name: target_model_name,
254
- identifier: template.identifier,
255
- }
252
+ model_name: target_model_name,
253
+ identifier: template.identifier,
256
254
  )
257
255
 
258
256
  parents = template.attributes.all.select { |a| a.attribute_type == AttributeType::PARENT }
@@ -23,6 +23,16 @@ module Etna
23
23
  @etna_client.folder_list_all_folders(list_all_folders_request.to_h))
24
24
  end
25
25
 
26
+ def tail_bucket(tail_bucket_request = TailBucketRequest.new)
27
+ TailResponse.new(
28
+ tail_bucket_request.project_name,
29
+ tail_bucket_request.bucket_name,
30
+ @etna_client.bucket_tail(tail_bucket_request.to_h).split("\n").map do |line|
31
+ JSON.parse(line, symbolize_names: true)
32
+ end
33
+ )
34
+ end
35
+
26
36
  def list_folder(list_folder_request = ListFolderRequest.new)
27
37
  if list_folder_request.folder_path != ''
28
38
  FoldersAndFilesResponse.new(
@@ -101,16 +111,36 @@ module Etna
101
111
 
102
112
  def download_file(file_or_url = File.new, &block)
103
113
  if file_or_url.instance_of?(File)
104
- download_path = file_or_url.download_path
114
+ download_url = file_or_url.download_url
115
+
116
+ if !download_url.present?
117
+ download = authorize_download(AuthorizeDownloadRequest.new(
118
+ project_name: file_or_url.project_name,
119
+ bucket_name: file_or_url.bucket_name,
120
+ file_path: file_or_url.file_path
121
+ ))
122
+ download_url = download.download_url
123
+ end
105
124
  else
106
- download_path = file_or_url.sub(%r!^https://[^/]*?/!, '/')
125
+ download_url = file_or_url
107
126
  end
108
127
 
128
+ download_path = download_url.sub(%r!^https://[^/]*?/!, '/')
129
+
109
130
  @etna_client.get(download_path) do |response|
110
131
  response.read_body(&block)
111
132
  end
112
133
  end
113
134
 
135
+ def authorize_download(authorize_download_request = AuthorizeDownloadRequest.new)
136
+ json = nil
137
+ @etna_client.post("/authorize/download", authorize_download_request) do |res|
138
+ json = JSON.parse(res.body)
139
+ end
140
+
141
+ DownloadResponse.new(json)
142
+ end
143
+
114
144
  def file_metadata(file_or_url = File.new)
115
145
  if file_or_url.instance_of?(File)
116
146
  download_path = file_or_url.download_path
@@ -329,11 +359,45 @@ module Etna
329
359
  ))
330
360
  end
331
361
 
362
+ def get_file_count_by_project(get_file_count_by_project_request = GetFileCountByProjectRequest.new)
363
+ query = ""
364
+ unless get_file_count_by_project_request.project_names.nil?
365
+ query = "?#{create_query_list_str('projects', get_file_count_by_project_request.project_names)}"
366
+ end
367
+
368
+ json = nil
369
+ @etna_client.get("/api/stats/files#{query}") do |res|
370
+ json = JSON.parse(res.body, symbolize_names: true)
371
+ end
372
+
373
+ json
374
+ end
375
+
376
+ def get_byte_count_by_project(get_byte_count_by_project_request = GetByteCountByProjectRequest.new)
377
+ query = ""
378
+ unless get_byte_count_by_project_request.project_names.nil?
379
+ query = "?#{create_query_list_str('projects', get_byte_count_by_project_request.project_names)}"
380
+ end
381
+
382
+ json = nil
383
+ @etna_client.get("/api/stats/bytes#{query}") do |res|
384
+ json = JSON.parse(res.body, symbolize_names: true)
385
+ end
386
+
387
+ json
388
+ end
389
+
332
390
  private
333
391
 
334
392
  def parent_folder_path(folder_path)
335
393
  folder_path.split('/')[0..-2].join('/')
336
394
  end
395
+
396
+ def create_query_list_str(name, values)
397
+ values.map do |val|
398
+ "#{name}[]=#{val}"
399
+ end.join('&')
400
+ end
337
401
  end
338
402
  end
339
403
  end