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.
- checksums.yaml +4 -4
- data/bin/etna +63 -0
- data/etna.completion +926 -0
- data/etna_app.completion +133 -0
- data/ext/completions/extconf.rb +20 -0
- data/lib/commands.rb +368 -0
- data/lib/etna.rb +6 -0
- data/lib/etna/application.rb +38 -20
- data/lib/etna/client.rb +60 -29
- data/lib/etna/clients.rb +4 -0
- data/lib/etna/clients/enum.rb +9 -0
- data/lib/etna/clients/janus.rb +2 -0
- data/lib/etna/clients/janus/client.rb +73 -0
- data/lib/etna/clients/janus/models.rb +78 -0
- data/lib/etna/clients/magma.rb +2 -0
- data/lib/etna/clients/magma/client.rb +24 -9
- data/lib/etna/clients/magma/formatting.rb +1 -0
- data/lib/etna/clients/magma/formatting/models_csv.rb +345 -0
- data/lib/etna/clients/magma/models.rb +323 -9
- data/lib/etna/clients/magma/workflows.rb +10 -0
- data/lib/etna/clients/magma/workflows/add_project_models_workflow.rb +78 -0
- data/lib/etna/clients/magma/workflows/attribute_actions_from_json_workflow.rb +62 -0
- data/lib/etna/clients/magma/workflows/create_project_workflow.rb +117 -0
- data/lib/etna/clients/magma/workflows/crud_workflow.rb +85 -0
- data/lib/etna/clients/magma/workflows/ensure_containing_record_workflow.rb +44 -0
- data/lib/etna/clients/magma/workflows/file_attributes_blank_workflow.rb +68 -0
- data/lib/etna/clients/magma/workflows/file_linking_workflow.rb +115 -0
- data/lib/etna/clients/magma/workflows/json_converters.rb +81 -0
- data/lib/etna/clients/magma/workflows/json_validators.rb +447 -0
- data/lib/etna/clients/magma/workflows/model_synchronization_workflow.rb +306 -0
- data/lib/etna/clients/magma/workflows/record_synchronization_workflow.rb +63 -0
- data/lib/etna/clients/magma/workflows/update_attributes_from_csv_workflow.rb +178 -0
- data/lib/etna/clients/metis.rb +1 -0
- data/lib/etna/clients/metis/client.rb +207 -5
- data/lib/etna/clients/metis/models.rb +174 -3
- data/lib/etna/clients/metis/workflows.rb +2 -0
- data/lib/etna/clients/metis/workflows/metis_download_workflow.rb +37 -0
- data/lib/etna/clients/metis/workflows/metis_upload_workflow.rb +137 -0
- data/lib/etna/clients/polyphemus.rb +3 -0
- data/lib/etna/clients/polyphemus/client.rb +33 -0
- data/lib/etna/clients/polyphemus/models.rb +68 -0
- data/lib/etna/clients/polyphemus/workflows.rb +1 -0
- data/lib/etna/clients/polyphemus/workflows/set_configuration_workflow.rb +47 -0
- data/lib/etna/command.rb +235 -5
- data/lib/etna/controller.rb +4 -0
- data/lib/etna/environment_scoped.rb +19 -0
- data/lib/etna/generate_autocompletion_script.rb +130 -0
- data/lib/etna/json_serializable_struct.rb +6 -3
- data/lib/etna/logger.rb +0 -3
- data/lib/etna/multipart_serializable_nested_hash.rb +6 -1
- data/lib/etna/route.rb +1 -1
- data/lib/etna/spec/vcr.rb +98 -0
- data/lib/etna/templates/attribute_actions_template.json +43 -0
- data/lib/etna/test_auth.rb +3 -1
- data/lib/etna/user.rb +4 -0
- data/lib/helpers.rb +81 -0
- metadata +47 -7
data/lib/etna/clients/metis.rb
CHANGED
@@ -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
|
-
|
8
|
+
|
7
9
|
module Etna
|
8
10
|
module Clients
|
9
11
|
class Metis
|
10
|
-
|
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(
|
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
|