etna 0.1.16 → 0.1.18

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/bin/etna +63 -0
  3. data/etna.completion +926 -0
  4. data/etna_app.completion +133 -0
  5. data/ext/completions/extconf.rb +20 -0
  6. data/lib/commands.rb +368 -0
  7. data/lib/etna.rb +6 -0
  8. data/lib/etna/application.rb +38 -20
  9. data/lib/etna/client.rb +60 -29
  10. data/lib/etna/clients.rb +4 -0
  11. data/lib/etna/clients/enum.rb +9 -0
  12. data/lib/etna/clients/janus.rb +2 -0
  13. data/lib/etna/clients/janus/client.rb +73 -0
  14. data/lib/etna/clients/janus/models.rb +78 -0
  15. data/lib/etna/clients/magma.rb +2 -0
  16. data/lib/etna/clients/magma/client.rb +24 -9
  17. data/lib/etna/clients/magma/formatting.rb +1 -0
  18. data/lib/etna/clients/magma/formatting/models_csv.rb +345 -0
  19. data/lib/etna/clients/magma/models.rb +323 -9
  20. data/lib/etna/clients/magma/workflows.rb +10 -0
  21. data/lib/etna/clients/magma/workflows/add_project_models_workflow.rb +78 -0
  22. data/lib/etna/clients/magma/workflows/attribute_actions_from_json_workflow.rb +62 -0
  23. data/lib/etna/clients/magma/workflows/create_project_workflow.rb +117 -0
  24. data/lib/etna/clients/magma/workflows/crud_workflow.rb +85 -0
  25. data/lib/etna/clients/magma/workflows/ensure_containing_record_workflow.rb +44 -0
  26. data/lib/etna/clients/magma/workflows/file_attributes_blank_workflow.rb +68 -0
  27. data/lib/etna/clients/magma/workflows/file_linking_workflow.rb +115 -0
  28. data/lib/etna/clients/magma/workflows/json_converters.rb +81 -0
  29. data/lib/etna/clients/magma/workflows/json_validators.rb +447 -0
  30. data/lib/etna/clients/magma/workflows/model_synchronization_workflow.rb +306 -0
  31. data/lib/etna/clients/magma/workflows/record_synchronization_workflow.rb +63 -0
  32. data/lib/etna/clients/magma/workflows/update_attributes_from_csv_workflow.rb +178 -0
  33. data/lib/etna/clients/metis.rb +1 -0
  34. data/lib/etna/clients/metis/client.rb +207 -5
  35. data/lib/etna/clients/metis/models.rb +174 -3
  36. data/lib/etna/clients/metis/workflows.rb +2 -0
  37. data/lib/etna/clients/metis/workflows/metis_download_workflow.rb +37 -0
  38. data/lib/etna/clients/metis/workflows/metis_upload_workflow.rb +137 -0
  39. data/lib/etna/clients/polyphemus.rb +3 -0
  40. data/lib/etna/clients/polyphemus/client.rb +33 -0
  41. data/lib/etna/clients/polyphemus/models.rb +68 -0
  42. data/lib/etna/clients/polyphemus/workflows.rb +1 -0
  43. data/lib/etna/clients/polyphemus/workflows/set_configuration_workflow.rb +47 -0
  44. data/lib/etna/command.rb +235 -5
  45. data/lib/etna/controller.rb +4 -0
  46. data/lib/etna/environment_scoped.rb +19 -0
  47. data/lib/etna/generate_autocompletion_script.rb +130 -0
  48. data/lib/etna/json_serializable_struct.rb +6 -3
  49. data/lib/etna/logger.rb +0 -3
  50. data/lib/etna/multipart_serializable_nested_hash.rb +6 -1
  51. data/lib/etna/route.rb +1 -1
  52. data/lib/etna/spec/vcr.rb +98 -0
  53. data/lib/etna/templates/attribute_actions_template.json +43 -0
  54. data/lib/etna/test_auth.rb +3 -1
  55. data/lib/etna/user.rb +4 -0
  56. data/lib/helpers.rb +81 -0
  57. metadata +47 -7
@@ -1,2 +1,3 @@
1
1
  require_relative './metis/client'
2
2
  require_relative './metis/models'
3
+ require_relative './metis/workflows'
@@ -1,37 +1,239 @@
1
1
  require 'net/http/persistent'
2
2
  require 'net/http/post/multipart'
3
3
  require 'singleton'
4
+ require 'cgi'
5
+ require 'json'
4
6
  require_relative '../../client'
5
7
  require_relative './models'
6
- require 'pry'
8
+
7
9
  module Etna
8
10
  module Clients
9
11
  class Metis
10
- def initialize(host:, token:)
12
+ attr_reader :token
13
+ def initialize(host:, token:, persistent: true, ignore_ssl: false)
11
14
  raise 'Metis client configuration is missing host.' unless host
12
15
  raise 'Metis client configuration is missing token.' unless token
13
- @etna_client = ::Etna::Client.new(host, token)
16
+ @etna_client = ::Etna::Client.new(
17
+ host,
18
+ token,
19
+ persistent: persistent,
20
+ ignore_ssl: ignore_ssl)
21
+
22
+ @token = token
14
23
  end
15
24
 
16
- def list_all_folders(list_all_folders_request)
25
+ def list_all_folders(list_all_folders_request = ListFoldersRequest.new)
17
26
  FoldersResponse.new(
18
27
  @etna_client.folder_list_all_folders(list_all_folders_request.to_h))
19
28
  end
20
29
 
21
- def list_folder(list_folder_request)
30
+ def list_folder(list_folder_request = ListFolderRequest.new)
22
31
  FoldersAndFilesResponse.new(
23
32
  @etna_client.folder_list(list_folder_request.to_h))
24
33
  end
25
34
 
35
+ def ensure_parent_folder_exists(project_name:, bucket_name:, path:)
36
+ create_folder_request = CreateFolderRequest.new(
37
+ project_name: project_name,
38
+ bucket_name: bucket_name,
39
+ folder_path: parent_folder_path(path)
40
+ )
41
+ create_folder(create_folder_request) if !folder_exists?(create_folder_request)
42
+ end
43
+
26
44
  def rename_folder(rename_folder_request)
45
+ ensure_parent_folder_exists(
46
+ project_name: rename_folder_request.project_name,
47
+ bucket_name: rename_folder_request.new_bucket_name,
48
+ path: rename_folder_request.new_folder_path
49
+ ) if rename_folder_request.create_parent
50
+
27
51
  FoldersResponse.new(
28
52
  @etna_client.folder_rename(rename_folder_request.to_h))
29
53
  end
30
54
 
55
+ def rename_file(rename_file_request)
56
+ ensure_parent_folder_exists(
57
+ project_name: rename_file_request.project_name,
58
+ bucket_name: rename_file_request.new_bucket_name,
59
+ path: rename_file_request.new_file_path # ensure_parent_folder_exists() parses this for the parent path
60
+ ) if rename_file_request.create_parent
61
+
62
+ FilesResponse.new(
63
+ @etna_client.file_rename(rename_file_request.to_h))
64
+ end
65
+
31
66
  def create_folder(create_folder_request)
32
67
  FoldersResponse.new(
33
68
  @etna_client.folder_create(create_folder_request.to_h))
34
69
  end
70
+
71
+ def delete_folder(delete_folder_request)
72
+ FoldersResponse.new(
73
+ @etna_client.folder_remove(delete_folder_request.to_h))
74
+ end
75
+
76
+ def find(find_request)
77
+ FoldersAndFilesResponse.new(
78
+ @etna_client.bucket_find(find_request.to_h))
79
+ end
80
+
81
+ def download_file(file_or_url = File.new, &block)
82
+ if file_or_url.instance_of?(File)
83
+ download_path = file_or_url.download_path
84
+ else
85
+ download_path = file_or_url.sub(%r!^https://[^/]*?/!, '/')
86
+ end
87
+
88
+ @etna_client.get(download_path) do |response|
89
+ response.read_body(&block)
90
+ end
91
+ end
92
+
93
+ def upload_start(upload_start_request = UploadStartRequest.new)
94
+ json = nil
95
+ @etna_client.post(upload_start_request.upload_path, upload_start_request) do |res|
96
+ json = JSON.parse(res.body)
97
+ end
98
+
99
+ UploadResponse.new(json)
100
+ end
101
+
102
+ def authorize_upload(authorize_upload_request = AuthorizeUploadRequest.new)
103
+ json = nil
104
+ @etna_client.post("/authorize/upload", authorize_upload_request) do |res|
105
+ json = JSON.parse(res.body)
106
+ end
107
+
108
+ UploadResponse.new(json)
109
+ end
110
+
111
+ def upload_blob(upload_blob_request = UploadBlobRequest.new)
112
+ json = nil
113
+ @etna_client.multipart_post(upload_blob_request.upload_path, upload_blob_request.encode_multipart_content) do |res|
114
+ json = JSON.parse(res.body)
115
+ end
116
+
117
+ UploadResponse.new(json)
118
+ end
119
+
120
+ def copy_files(copy_files_request)
121
+ FilesResponse.new(
122
+ @etna_client.file_bulk_copy(copy_files_request.to_h))
123
+ end
124
+
125
+ def folder_exists?(create_folder_request)
126
+ # NOTE: this doesn't test if the folder_path itself exists
127
+ # This can be confusing for root folders, because
128
+ # they have no parents, so you don't need
129
+ # to create anything.
130
+ return true if create_folder_request.folder_path.empty? # root folder
131
+
132
+ # returns 422 if the folder_path does not exist
133
+ begin
134
+ list_folder(
135
+ Etna::Clients::Metis::ListFolderRequest.new(
136
+ project_name: create_folder_request.project_name,
137
+ bucket_name: create_folder_request.bucket_name,
138
+ folder_path: create_folder_request.folder_path
139
+ ))
140
+ rescue Etna::Error => e
141
+ return false if e.status == 422
142
+ raise
143
+ end
144
+ return true
145
+ end
146
+
147
+ def folders(project_name:, bucket_name:)
148
+ @folders ||= Hash.new { |h, key|
149
+ h[key] = list_all_folders(
150
+ Etna::Clients::Metis::ListFoldersRequest.new(
151
+ project_name: project_name,
152
+ bucket_name: key
153
+ )).folders.all
154
+ }
155
+
156
+ @folders[bucket_name]
157
+ end
158
+
159
+ def rename_folders_by_regex(project_name:, source_bucket:, source_folders:, dest_bucket:, regex:)
160
+ found_folders = source_folders.select { |folder|
161
+ folder.folder_path =~ regex
162
+ }
163
+
164
+ return if found_folders.length == 0
165
+
166
+ found_folders.each { |folder|
167
+ # If the destination folder already exists, we need to copy the files
168
+ # over to it and delete the source folder.
169
+ create_folder_request = CreateFolderRequest.new(
170
+ project_name: project_name,
171
+ bucket_name: dest_bucket,
172
+ folder_path: folder.folder_path
173
+ )
174
+
175
+ if folder_exists?(create_folder_request)
176
+ recursively_rename_folder(
177
+ project_name: project_name,
178
+ source_bucket: source_bucket,
179
+ dest_bucket: dest_bucket,
180
+ folder: folder
181
+ )
182
+ else
183
+ rename_folder(Etna::Clients::Metis::RenameFolderRequest.new(
184
+ bucket_name: source_bucket,
185
+ project_name: project_name,
186
+ folder_path: folder.folder_path,
187
+ new_bucket_name: dest_bucket,
188
+ new_folder_path: folder.folder_path,
189
+ create_parent: true)
190
+ )
191
+ end
192
+ }
193
+ end
194
+
195
+ def recursively_rename_folder(project_name:, source_bucket:, dest_bucket:, folder:)
196
+ folder_contents = list_folder(
197
+ Etna::Clients::Metis::ListFolderRequest.new(
198
+ project_name: project_name,
199
+ bucket_name: source_bucket,
200
+ folder_path: folder.folder_path
201
+ ))
202
+
203
+ folder_contents.folders.all.each do |sub_folder|
204
+ recursively_rename_folder(
205
+ project_name: project_name,
206
+ source_bucket: source_bucket,
207
+ dest_bucket: dest_bucket,
208
+ folder: sub_folder
209
+ )
210
+ end
211
+
212
+ folder_contents.files.all.each do |file|
213
+ rename_file(Etna::Clients::Metis::RenameFileRequest.new(
214
+ bucket_name: source_bucket,
215
+ project_name: project_name,
216
+ file_path: file.file_path,
217
+ new_bucket_name: dest_bucket,
218
+ new_file_path: file.file_path,
219
+ create_parent: true)
220
+ )
221
+ end
222
+
223
+ # Now delete the source folder
224
+ delete_folder(
225
+ Etna::Clients::Metis::DeleteFolderRequest.new(
226
+ project_name: project_name,
227
+ bucket_name: source_bucket,
228
+ folder_path: folder.folder_path
229
+ ))
230
+ end
231
+
232
+ private
233
+
234
+ def parent_folder_path(folder_path)
235
+ folder_path.split('/')[0..-2].join('/')
236
+ end
35
237
  end
36
238
  end
37
239
  end
@@ -1,5 +1,5 @@
1
1
  require_relative '../../json_serializable_struct'
2
-
2
+ require 'ostruct'
3
3
 
4
4
  module Etna
5
5
  module Clients
@@ -19,11 +19,26 @@ module Etna
19
19
  end
20
20
  end
21
21
 
22
- class RenameFolderRequest < Struct.new(:project_name, :bucket_name, :folder_path, :new_bucket_name, :new_folder_path, keyword_init: true)
22
+ class RenameFolderRequest < Struct.new(:project_name, :bucket_name, :folder_path, :new_bucket_name, :new_folder_path, :create_parent, keyword_init: true)
23
23
  include JsonSerializableStruct
24
24
 
25
25
  def initialize(**params)
26
- super({}.update(params))
26
+ super({create_parent: false}.update(params))
27
+ end
28
+
29
+ def to_h
30
+ # The :project_name comes in from Polyphemus as a symbol value,
31
+ # we need to make sure it's a string because it's going
32
+ # in the URL.
33
+ super().compact.transform_values(&:to_s)
34
+ end
35
+ end
36
+
37
+ class RenameFileRequest < Struct.new(:project_name, :bucket_name, :file_path, :new_bucket_name, :new_file_path, :create_parent, keyword_init: true)
38
+ include JsonSerializableStruct
39
+
40
+ def initialize(**params)
41
+ super({create_parent: false}.update(params))
27
42
  end
28
43
 
29
44
  def to_h
@@ -64,6 +79,71 @@ module Etna
64
79
  end
65
80
  end
66
81
 
82
+ class DeleteFolderRequest < Struct.new(:project_name, :bucket_name, :folder_path, keyword_init: true)
83
+ include JsonSerializableStruct
84
+
85
+ def initialize(**params)
86
+ super({}.update(params))
87
+ end
88
+
89
+ def to_h
90
+ # The :project_name comes in from Polyphemus as a symbol value,
91
+ # we need to make sure it's a string because it's going
92
+ # in the URL.
93
+ super().compact.transform_values(&:to_s)
94
+ end
95
+ end
96
+
97
+ class FindRequest < Struct.new(:project_name, :bucket_name, :limit, :offset, :params, keyword_init: true)
98
+ include JsonSerializableStruct
99
+
100
+ def initialize(**args)
101
+ super({params: []}.update(args))
102
+ end
103
+
104
+ def add_param(param)
105
+ params << param
106
+ end
107
+
108
+ def to_h
109
+ # The nested :params values don't get converted correctly with transform_values, so it's
110
+ # easier to do from a JSON string
111
+ JSON.parse(to_json, :symbolize_names => true)
112
+ end
113
+ end
114
+
115
+ class FindParam < Struct.new(:attribute, :predicate, :value, :type, keyword_init: true)
116
+ include JsonSerializableStruct
117
+ def initialize(**args)
118
+ super({}.update(args))
119
+ end
120
+ end
121
+
122
+ class CopyFilesRequest < Struct.new(:project_name, :revisions, keyword_init: true)
123
+ include JsonSerializableStruct
124
+
125
+ def initialize(**args)
126
+ super({revisions: []}.update(args))
127
+ end
128
+
129
+ def add_revision(revision)
130
+ revisions << revision
131
+ end
132
+
133
+ def to_h
134
+ # The nested :revisions values don't get converted correctly with transform_values, so it's
135
+ # easier to do from a JSON string
136
+ JSON.parse(to_json, :symbolize_names => true)
137
+ end
138
+ end
139
+
140
+ class CopyRevision < Struct.new(:source, :dest, keyword_init: true)
141
+ include JsonSerializableStruct
142
+ def initialize(**args)
143
+ super({}.update(args))
144
+ end
145
+ end
146
+
67
147
  class FoldersResponse
68
148
  attr_reader :raw
69
149
 
@@ -76,6 +156,18 @@ module Etna
76
156
  end
77
157
  end
78
158
 
159
+ class FilesResponse
160
+ attr_reader :raw
161
+
162
+ def initialize(raw = {})
163
+ @raw = raw
164
+ end
165
+
166
+ def files
167
+ Files.new(raw[:files])
168
+ end
169
+ end
170
+
79
171
  class FoldersAndFilesResponse < FoldersResponse
80
172
  def files
81
173
  Files.new(raw[:files])
@@ -117,9 +209,36 @@ module Etna
117
209
  raw[:file_path]
118
210
  end
119
211
 
212
+ def project_name
213
+ raw[:project_name]
214
+ end
215
+
216
+ def bucket_name
217
+ raw[:bucket_name]
218
+ end
219
+
220
+ def download_path
221
+ raw[:download_url].nil? ?
222
+ "/#{project_name}/download/#{bucket_name}/#{file_path}" :
223
+ raw[:download_url].sub(%r!^https://[^/]*?/!, '/')
224
+ end
225
+
226
+ def download_url
227
+ raw[:download_url] || ''
228
+ end
229
+
120
230
  def file_name
121
231
  raw[:file_name]
122
232
  end
233
+
234
+ def updated_at
235
+ time = raw[:updated_at]
236
+ time.nil? ? nil : Time.parse(time)
237
+ end
238
+
239
+ def size
240
+ raw[:size]
241
+ end
123
242
  end
124
243
 
125
244
  class Folder
@@ -137,6 +256,58 @@ module Etna
137
256
  raw[:bucket_name]
138
257
  end
139
258
  end
259
+
260
+ class AuthorizeUploadRequest < Struct.new(:project_name, :bucket_name, :file_path, keyword_init: true)
261
+ include JsonSerializableStruct
262
+ end
263
+
264
+ class UploadStartRequest < Struct.new(:file_size, :action, :metis_uid, :next_blob_size, :upload_path, :next_blob_hash, :reset, keyword_init: true)
265
+ include JsonSerializableStruct
266
+
267
+ def initialize(args)
268
+ super({ action: UploadAction::START }.update(args))
269
+ end
270
+ end
271
+
272
+ class UploadBlobRequest < Struct.new(:file_size, :action, :metis_uid, :blob_data, :upload_path, :next_blob_size, :next_blob_hash, :current_byte_position, keyword_init: true)
273
+ include MultipartSerializableNestedHash
274
+
275
+ def initialize(args)
276
+ super({ action: UploadAction::BLOB }.update(args))
277
+ end
278
+
279
+ def encode_multipart_content(base_key = '')
280
+ self.class.encode_multipart_content(to_h, base_key)
281
+ end
282
+ end
283
+
284
+ class UploadResponse
285
+ attr_reader :raw
286
+ def initialize(raw = {})
287
+ @raw = raw
288
+ end
289
+
290
+ def current_byte_position
291
+ raw['current_byte_position'].to_i
292
+ end
293
+
294
+ def url
295
+ raw['url'] || ''
296
+ end
297
+
298
+ def next_blob_size
299
+ raw['next_blob_size'].to_i
300
+ end
301
+
302
+ def upload_path
303
+ url.sub(%r!^https://[^/]*?/!, '/')
304
+ end
305
+ end
306
+
307
+ class UploadAction < String
308
+ START = UploadAction.new("start")
309
+ BLOB = UploadAction.new("blob")
310
+ end
140
311
  end
141
312
  end
142
313
  end