etna 0.1.11 → 0.1.16
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/lib/etna/application.rb +8 -2
- data/lib/etna/auth.rb +1 -1
- data/lib/etna/client.rb +31 -28
- data/lib/etna/clients/magma.rb +2 -0
- data/lib/etna/clients/magma/client.rb +65 -0
- data/lib/etna/clients/magma/models.rb +265 -0
- data/lib/etna/clients/metis.rb +2 -0
- data/lib/etna/clients/metis/client.rb +37 -0
- data/lib/etna/clients/metis/models.rb +142 -0
- data/lib/etna/command.rb +8 -0
- data/lib/etna/directed_graph.rb +56 -0
- data/lib/etna/hmac.rb +2 -1
- data/lib/etna/json_serializable_struct.rb +34 -0
- data/lib/etna/logger.rb +18 -1
- data/lib/etna/multipart_serializable_nested_hash.rb +45 -0
- data/lib/etna/parse_body.rb +1 -1
- data/lib/etna/server.rb +3 -0
- data/lib/etna/test_auth.rb +1 -1
- data/lib/etna/user.rb +7 -1
- metadata +25 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 345d1790412dd359129f41586677ac2ac1a53c55897768d5d695e45ac828dcb6
|
4
|
+
data.tar.gz: ad2a6121a6659fa1352268b1545b434a9a7777531e6a8bdea8a74d6d779f54f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3994385c74ff519195d80cae05e87dc5bd57755efd55ef20e26fdcb5b658c0ae45da22c62a7a87e77805e8d74eed7a7fe44440f4c5cd766dcf8d4b1f9ed46598
|
7
|
+
data.tar.gz: e0b39e27d1b5d2aab03aeb5d7118ca6c7b908e94778fe37aee9f83deee1fac3191a399bca95ca5c147f10261092974d2d40fd22ee44862e50506f763d6c982d8
|
data/lib/etna/application.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
|
6
6
|
require_relative './sign_service'
|
7
7
|
require 'singleton'
|
8
|
+
require 'rollbar'
|
8
9
|
|
9
10
|
module Etna::Application
|
10
11
|
def self.included(other)
|
@@ -31,6 +32,12 @@ module Etna::Application
|
|
31
32
|
|
32
33
|
def configure(opts)
|
33
34
|
@config = opts
|
35
|
+
|
36
|
+
if (rollbar_config = config(:rollbar)) && rollbar_config[:access_token]
|
37
|
+
Rollbar.configure do |config|
|
38
|
+
config.access_token = rollbar_config[:access_token]
|
39
|
+
end
|
40
|
+
end
|
34
41
|
end
|
35
42
|
|
36
43
|
def setup_logger
|
@@ -40,10 +47,9 @@ module Etna::Application
|
|
40
47
|
# Number of old copies of the log to keep.
|
41
48
|
config(:log_copies) || 5,
|
42
49
|
# How large the log can get before overturning.
|
43
|
-
config(:log_size) || 1048576
|
50
|
+
config(:log_size) || 1048576,
|
44
51
|
)
|
45
52
|
log_level = (config(:log_level) || 'warn').upcase.to_sym
|
46
|
-
|
47
53
|
@logger.level = Logger.const_defined?(log_level) ? Logger.const_get(log_level) : Logger::WARN
|
48
54
|
end
|
49
55
|
|
data/lib/etna/auth.rb
CHANGED
@@ -93,7 +93,7 @@ module Etna
|
|
93
93
|
return false unless headers = etna_param(request, :headers)
|
94
94
|
|
95
95
|
headers = headers.split(/,/).map do |header|
|
96
|
-
[ header.to_sym, etna_param(request, header) ]
|
96
|
+
[ header.to_sym, params(request)[header.to_sym] || etna_param(request, header) ]
|
97
97
|
end.to_h
|
98
98
|
|
99
99
|
# Now expect the standard headers
|
data/lib/etna/client.rb
CHANGED
@@ -4,13 +4,15 @@ require 'singleton'
|
|
4
4
|
|
5
5
|
module Etna
|
6
6
|
class Client
|
7
|
-
def initialize(host, token)
|
7
|
+
def initialize(host, token, routes_available: true)
|
8
8
|
@host = host.sub(%r!/$!,'')
|
9
9
|
@token = token
|
10
10
|
|
11
|
-
set_routes
|
12
11
|
|
13
|
-
|
12
|
+
if routes_available
|
13
|
+
set_routes
|
14
|
+
define_route_helpers
|
15
|
+
end
|
14
16
|
end
|
15
17
|
|
16
18
|
attr_reader :routes
|
@@ -19,6 +21,29 @@ module Etna
|
|
19
21
|
Etna::Route.path(route[:route], params)
|
20
22
|
end
|
21
23
|
|
24
|
+
def multipart_post(endpoint, content, &block)
|
25
|
+
uri = request_uri(endpoint)
|
26
|
+
multipart = Net::HTTP::Post::Multipart.new uri.path, content
|
27
|
+
multipart.add_field('Authorization', "Etna #{@token}")
|
28
|
+
request(uri, multipart, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def post(endpoint, params={}, &block)
|
32
|
+
body_request(Net::HTTP::Post, endpoint, params, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(endpoint, params={}, &block)
|
36
|
+
query_request(Net::HTTP::Get, endpoint, params, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def options(endpoint, params={}, &block)
|
40
|
+
query_request(Net::HTTP::Options, endpoint, params, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(endpoint, params={}, &block)
|
44
|
+
body_request(Net::HTTP::Delete, endpoint, params, &block)
|
45
|
+
end
|
46
|
+
|
22
47
|
private
|
23
48
|
|
24
49
|
def set_routes
|
@@ -60,32 +85,10 @@ module Etna
|
|
60
85
|
end
|
61
86
|
end
|
62
87
|
|
63
|
-
def multipart_post(endpoint, content, &block)
|
64
|
-
uri = request_uri(endpoint)
|
65
|
-
multipart = Net::HTTP::Post::Multipart.new uri.path, content
|
66
|
-
multipart.add_field('Authorization', "Etna #{@token}")
|
67
|
-
request(uri, multipart, &block)
|
68
|
-
end
|
69
|
-
|
70
|
-
def post(endpoint, params={}, &block)
|
71
|
-
body_request(Net::HTTP::Post, endpoint, params, &block)
|
72
|
-
end
|
73
|
-
|
74
|
-
def get(endpoint, params={}, &block)
|
75
|
-
query_request(Net::HTTP::Get, endpoint, params, &block)
|
76
|
-
end
|
77
|
-
|
78
|
-
def options(endpoint, params={}, &block)
|
79
|
-
query_request(Net::HTTP::Options, endpoint, params, &block)
|
80
|
-
end
|
81
|
-
|
82
|
-
def delete(endpoint, params={}, &block)
|
83
|
-
body_request(Net::HTTP::Delete, endpoint, params, &block)
|
84
|
-
end
|
85
88
|
|
86
89
|
def body_request(type, endpoint, params={}, &block)
|
87
90
|
uri = request_uri(endpoint)
|
88
|
-
req = type.new(uri.
|
91
|
+
req = type.new(uri.request_uri,request_params)
|
89
92
|
req.body = params.to_json
|
90
93
|
request(uri, req, &block)
|
91
94
|
end
|
@@ -122,9 +125,9 @@ module Etna
|
|
122
125
|
def json_error(body)
|
123
126
|
msg = JSON.parse(body, symbolize_names: true)
|
124
127
|
if (msg.has_key?(:errors) && msg[:errors].is_a?(Array))
|
125
|
-
return msg[:errors]
|
128
|
+
return JSON.generate(msg[:errors])
|
126
129
|
elsif msg.has_key?(:error)
|
127
|
-
return msg[:error]
|
130
|
+
return JSON.generate(msg[:error])
|
128
131
|
end
|
129
132
|
end
|
130
133
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'net/http/persistent'
|
2
|
+
require 'net/http/post/multipart'
|
3
|
+
require 'singleton'
|
4
|
+
require_relative '../../client'
|
5
|
+
require_relative './models'
|
6
|
+
|
7
|
+
module Etna
|
8
|
+
module Clients
|
9
|
+
class Magma
|
10
|
+
def initialize(host:, token:)
|
11
|
+
raise 'Magma client configuration is missing host.' unless host
|
12
|
+
raise 'Magma client configuration is missing token.' unless token
|
13
|
+
@etna_client = ::Etna::Client.new(host, token, routes_available: false)
|
14
|
+
end
|
15
|
+
|
16
|
+
# This endpoint returns models and records by name:
|
17
|
+
# e.g. params:
|
18
|
+
# {
|
19
|
+
# model_name: "model_one", # or "all"
|
20
|
+
# record_names: [ "rn1", "rn2" ], # or "all",
|
21
|
+
# attribute_names: "all"
|
22
|
+
# }
|
23
|
+
def retrieve(retrieval_request)
|
24
|
+
json = nil
|
25
|
+
@etna_client.post('/retrieve', retrieval_request) do |res|
|
26
|
+
json = JSON.parse(res.body)
|
27
|
+
end
|
28
|
+
|
29
|
+
RetrievalResponse.new(json)
|
30
|
+
end
|
31
|
+
|
32
|
+
# This 'query' end point is used to fetch data by graph query
|
33
|
+
# See question.rb for more detail
|
34
|
+
def query(query_request)
|
35
|
+
json = nil
|
36
|
+
@etna_client.post('/query', query_request) do |res|
|
37
|
+
json = JSON.parse(res.body)
|
38
|
+
end
|
39
|
+
|
40
|
+
QueryResponse.new(json)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Post revisions to Magma records
|
44
|
+
# { model_name: { record_name: { attribute1: 1, attribute2: 2 } } } }
|
45
|
+
# data can also be a File or IO stream
|
46
|
+
def update(update_request)
|
47
|
+
json = nil
|
48
|
+
@etna_client.multipart_post('/update', update_request.encode_multipart_content) do |res|
|
49
|
+
json = JSON.parse(res.body)
|
50
|
+
end
|
51
|
+
|
52
|
+
UpdateResponse.new(json)
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_model(update_model_request)
|
56
|
+
json = nil
|
57
|
+
@etna_client.post('/update_model', update_model_request) do |res|
|
58
|
+
json = JSON.parse(res.body)
|
59
|
+
end
|
60
|
+
|
61
|
+
UpdateModelResponse.new(json)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require_relative '../../json_serializable_struct'
|
2
|
+
require_relative '../../multipart_serializable_nested_hash'
|
3
|
+
require_relative '../../directed_graph'
|
4
|
+
|
5
|
+
# TODO: In the near future, I'd like to transition to specifying apis via SWAGGER and generating model stubs from the
|
6
|
+
# common definitions. For nowe I've written them out by hand here.
|
7
|
+
module Etna
|
8
|
+
module Clients
|
9
|
+
class Magma
|
10
|
+
class RetrievalRequest < Struct.new(:model_name, :attribute_names, :record_names, :project_name, keyword_init: true)
|
11
|
+
include JsonSerializableStruct
|
12
|
+
|
13
|
+
def initialize(**params)
|
14
|
+
super({model_name: 'all', attribute_names: 'all', record_names: []}.update(params))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class QueryRequest < Struct.new(:query, :project_name, keyword_init: true)
|
19
|
+
include JsonSerializableStruct
|
20
|
+
end
|
21
|
+
|
22
|
+
class UpdateRequest < Struct.new(:revisions, :project_name, keyword_init: true)
|
23
|
+
include JsonSerializableStruct
|
24
|
+
include MultipartSerializableNestedHash
|
25
|
+
|
26
|
+
def initialize(**params)
|
27
|
+
super({revisions: {}}.update(params))
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_revision(model_name, record_name, **attrs)
|
31
|
+
revision = revisions[model_name] ||= {}
|
32
|
+
record = revision[record_name] ||= {}
|
33
|
+
record.update(attrs)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class UpdateModelRequest < Struct.new(:project_name, :actions, keyword_init: true)
|
38
|
+
include JsonSerializableStruct
|
39
|
+
|
40
|
+
def initialize(**params)
|
41
|
+
super({actions: []}.update(params))
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_action(action)
|
45
|
+
actions << action
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class AddAttributeAction < Struct.new(:action_name, :model_name, :attribute_name, :type, :description, :display_name, :format_hint, :hidden, :index, :link_model_name, :read_only, :restricted, :unique, :validation, keyword_init: true)
|
50
|
+
include JsonSerializableStruct
|
51
|
+
def initialize(**args)
|
52
|
+
super({action_name: 'add_attribute'}.update(args))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class AttributeValidation < Struct.new(:type, :value, :begin, :end, keyword_init: true)
|
57
|
+
include JsonSerializableStruct
|
58
|
+
end
|
59
|
+
|
60
|
+
class AttributeValidationType < String
|
61
|
+
REGEXP = AttributeValidationType.new("Regexp")
|
62
|
+
ARRAY = AttributeValidationType.new("Array")
|
63
|
+
RANGE = AttributeValidationType.new("Range")
|
64
|
+
end
|
65
|
+
|
66
|
+
class RetrievalResponse
|
67
|
+
attr_reader :raw
|
68
|
+
|
69
|
+
def initialize(raw = {})
|
70
|
+
@raw = raw
|
71
|
+
end
|
72
|
+
|
73
|
+
def models
|
74
|
+
Models.new(raw['models'])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class UpdateModelResponse < RetrievalResponse
|
79
|
+
end
|
80
|
+
|
81
|
+
class QueryResponse
|
82
|
+
attr_reader :raw
|
83
|
+
|
84
|
+
def initialize(raw = {})
|
85
|
+
@raw = raw
|
86
|
+
end
|
87
|
+
|
88
|
+
def answer
|
89
|
+
raw['answer']
|
90
|
+
end
|
91
|
+
|
92
|
+
def format
|
93
|
+
raw['format']
|
94
|
+
end
|
95
|
+
|
96
|
+
def type
|
97
|
+
raw['type']
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class UpdateResponse < RetrievalResponse
|
102
|
+
end
|
103
|
+
|
104
|
+
class Models
|
105
|
+
attr_reader :raw
|
106
|
+
|
107
|
+
def initialize(raw = {})
|
108
|
+
@raw = raw
|
109
|
+
end
|
110
|
+
|
111
|
+
def model_keys
|
112
|
+
raw.keys
|
113
|
+
end
|
114
|
+
|
115
|
+
def model(model_key)
|
116
|
+
Model.new(raw[model_key])
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_directed_graph(include_casual_links=false)
|
120
|
+
graph = ::DirectedGraph.new
|
121
|
+
|
122
|
+
model_keys.each do |model_name|
|
123
|
+
graph.add_connection(model(model_name).template.parent, model_name)
|
124
|
+
|
125
|
+
if include_casual_links
|
126
|
+
attributes = model(model_name).template.attributes
|
127
|
+
attributes.attribute_keys.each do |attribute_name|
|
128
|
+
attribute = attributes.attribute(attribute_name)
|
129
|
+
|
130
|
+
linked_model_name = attribute.link_model_name
|
131
|
+
if linked_model_name
|
132
|
+
if attribute.attribute_type == AttributeType::PARENT
|
133
|
+
graph.add_connection(linked_model_name, model_name)
|
134
|
+
elsif attribute.attribute_type == AttributeType::COLLECTION
|
135
|
+
graph.add_connection(model_name, linked_model_name)
|
136
|
+
elsif attribute.attribute_type == AttributeType::CHILD
|
137
|
+
graph.add_connection(model_name, linked_model_name)
|
138
|
+
elsif attribute.attribute_type == AttributeType::LINK
|
139
|
+
graph.add_connection(model_name, linked_model_name)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
graph
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Model
|
151
|
+
attr_reader :raw
|
152
|
+
|
153
|
+
def initialize(raw = {})
|
154
|
+
@raw = raw
|
155
|
+
end
|
156
|
+
|
157
|
+
def documents
|
158
|
+
Documents.new(raw['documents'])
|
159
|
+
end
|
160
|
+
|
161
|
+
def template
|
162
|
+
Template.new(raw['template'])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class Documents
|
167
|
+
attr_reader :raw
|
168
|
+
|
169
|
+
def initialize(raw = {})
|
170
|
+
@raw = raw
|
171
|
+
end
|
172
|
+
|
173
|
+
def document_keys
|
174
|
+
raw.keys
|
175
|
+
end
|
176
|
+
|
177
|
+
def document(document_key)
|
178
|
+
raw[document_key]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class Template
|
183
|
+
attr_reader :raw
|
184
|
+
|
185
|
+
def initialize(raw = {})
|
186
|
+
@raw = raw
|
187
|
+
end
|
188
|
+
|
189
|
+
def name
|
190
|
+
raw['name'] || ""
|
191
|
+
end
|
192
|
+
|
193
|
+
def identifier
|
194
|
+
raw['identifier'] || ""
|
195
|
+
end
|
196
|
+
|
197
|
+
def parent
|
198
|
+
raw['parent']
|
199
|
+
end
|
200
|
+
|
201
|
+
def attributes
|
202
|
+
Attributes.new(raw['attributes'])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class Attributes
|
207
|
+
attr_reader :raw
|
208
|
+
|
209
|
+
def initialize(raw = {})
|
210
|
+
@raw = raw
|
211
|
+
end
|
212
|
+
|
213
|
+
def attribute_keys
|
214
|
+
raw.keys
|
215
|
+
end
|
216
|
+
|
217
|
+
def attribute(attribute_key)
|
218
|
+
Attribute.new(raw[attribute_key])
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Attribute
|
223
|
+
attr_reader :raw
|
224
|
+
|
225
|
+
def initialize(raw = {})
|
226
|
+
@raw = raw
|
227
|
+
end
|
228
|
+
|
229
|
+
def name
|
230
|
+
@raw['name'] || ""
|
231
|
+
end
|
232
|
+
|
233
|
+
def attribute_name
|
234
|
+
@raw['attribute_name'] || ""
|
235
|
+
end
|
236
|
+
|
237
|
+
def attribute_type
|
238
|
+
@raw['attribute_type'] && AttributeType.new(@raw['attribute_type'])
|
239
|
+
end
|
240
|
+
|
241
|
+
def link_model_name
|
242
|
+
raw['link_model_name']
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
class AttributeType < String
|
247
|
+
STRING = AttributeType.new("string")
|
248
|
+
DATE_TIME = AttributeType.new("date_time")
|
249
|
+
BOOLEAN = AttributeType.new("boolean")
|
250
|
+
CHILD = AttributeType.new("child")
|
251
|
+
COLLECTION = AttributeType.new("collection")
|
252
|
+
FILE = AttributeType.new("file")
|
253
|
+
FLOAT = AttributeType.new("float")
|
254
|
+
IDENTIFIER = AttributeType.new("identifier")
|
255
|
+
IMAGE = AttributeType.new("image")
|
256
|
+
INTEGER = AttributeType.new("integer")
|
257
|
+
LINK = AttributeType.new("link")
|
258
|
+
MATCH = AttributeType.new("match")
|
259
|
+
MATRIX = AttributeType.new("matrix")
|
260
|
+
PARENT = AttributeType.new("parent")
|
261
|
+
TABLE = AttributeType.new("table")
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'net/http/persistent'
|
2
|
+
require 'net/http/post/multipart'
|
3
|
+
require 'singleton'
|
4
|
+
require_relative '../../client'
|
5
|
+
require_relative './models'
|
6
|
+
require 'pry'
|
7
|
+
module Etna
|
8
|
+
module Clients
|
9
|
+
class Metis
|
10
|
+
def initialize(host:, token:)
|
11
|
+
raise 'Metis client configuration is missing host.' unless host
|
12
|
+
raise 'Metis client configuration is missing token.' unless token
|
13
|
+
@etna_client = ::Etna::Client.new(host, token)
|
14
|
+
end
|
15
|
+
|
16
|
+
def list_all_folders(list_all_folders_request)
|
17
|
+
FoldersResponse.new(
|
18
|
+
@etna_client.folder_list_all_folders(list_all_folders_request.to_h))
|
19
|
+
end
|
20
|
+
|
21
|
+
def list_folder(list_folder_request)
|
22
|
+
FoldersAndFilesResponse.new(
|
23
|
+
@etna_client.folder_list(list_folder_request.to_h))
|
24
|
+
end
|
25
|
+
|
26
|
+
def rename_folder(rename_folder_request)
|
27
|
+
FoldersResponse.new(
|
28
|
+
@etna_client.folder_rename(rename_folder_request.to_h))
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_folder(create_folder_request)
|
32
|
+
FoldersResponse.new(
|
33
|
+
@etna_client.folder_create(create_folder_request.to_h))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require_relative '../../json_serializable_struct'
|
2
|
+
|
3
|
+
|
4
|
+
module Etna
|
5
|
+
module Clients
|
6
|
+
class Metis
|
7
|
+
class ListFoldersRequest < Struct.new(:project_name, :bucket_name, :offset, :limit, keyword_init: true)
|
8
|
+
include JsonSerializableStruct
|
9
|
+
|
10
|
+
def initialize(**params)
|
11
|
+
super({}.update(params))
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
# The :project_name comes in from Polyphemus as a symbol value,
|
16
|
+
# we need to make sure it's a string because it's going
|
17
|
+
# in the URL.
|
18
|
+
super().compact.transform_values(&:to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class RenameFolderRequest < Struct.new(:project_name, :bucket_name, :folder_path, :new_bucket_name, :new_folder_path, keyword_init: true)
|
23
|
+
include JsonSerializableStruct
|
24
|
+
|
25
|
+
def initialize(**params)
|
26
|
+
super({}.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 ListFolderRequest < Struct.new(:project_name, :bucket_name, :folder_path, keyword_init: true)
|
38
|
+
include JsonSerializableStruct
|
39
|
+
|
40
|
+
def initialize(**params)
|
41
|
+
super({}.update(params))
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_h
|
45
|
+
# The :project_name comes in from Polyphemus as a symbol value,
|
46
|
+
# we need to make sure it's a string because it's going
|
47
|
+
# in the URL.
|
48
|
+
super().compact.transform_values(&:to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class CreateFolderRequest < Struct.new(:project_name, :bucket_name, :folder_path, keyword_init: true)
|
53
|
+
include JsonSerializableStruct
|
54
|
+
|
55
|
+
def initialize(**params)
|
56
|
+
super({}.update(params))
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_h
|
60
|
+
# The :project_name comes in from Polyphemus as a symbol value,
|
61
|
+
# we need to make sure it's a string because it's going
|
62
|
+
# in the URL.
|
63
|
+
super().compact.transform_values(&:to_s)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class FoldersResponse
|
68
|
+
attr_reader :raw
|
69
|
+
|
70
|
+
def initialize(raw = {})
|
71
|
+
@raw = raw
|
72
|
+
end
|
73
|
+
|
74
|
+
def folders
|
75
|
+
Folders.new(raw[:folders])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class FoldersAndFilesResponse < FoldersResponse
|
80
|
+
def files
|
81
|
+
Files.new(raw[:files])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Files
|
86
|
+
attr_reader :raw
|
87
|
+
|
88
|
+
def initialize(raw = {})
|
89
|
+
@raw = raw
|
90
|
+
end
|
91
|
+
|
92
|
+
def all
|
93
|
+
raw.map { |file| File.new(file) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Folders
|
98
|
+
attr_reader :raw
|
99
|
+
|
100
|
+
def initialize(raw = {})
|
101
|
+
@raw = raw
|
102
|
+
end
|
103
|
+
|
104
|
+
def all
|
105
|
+
raw.map { |folder| Folder.new(folder) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class File
|
110
|
+
attr_reader :raw
|
111
|
+
|
112
|
+
def initialize(raw = {})
|
113
|
+
@raw = raw
|
114
|
+
end
|
115
|
+
|
116
|
+
def file_path
|
117
|
+
raw[:file_path]
|
118
|
+
end
|
119
|
+
|
120
|
+
def file_name
|
121
|
+
raw[:file_name]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Folder
|
126
|
+
attr_reader :raw
|
127
|
+
|
128
|
+
def initialize(raw = {})
|
129
|
+
@raw = raw
|
130
|
+
end
|
131
|
+
|
132
|
+
def folder_path
|
133
|
+
raw[:folder_path]
|
134
|
+
end
|
135
|
+
|
136
|
+
def bucket_name
|
137
|
+
raw[:bucket_name]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/etna/command.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rollbar'
|
2
|
+
|
1
3
|
module Etna
|
2
4
|
class Command
|
3
5
|
class << self
|
@@ -15,12 +17,18 @@ module Etna
|
|
15
17
|
# To be overridden during inheritance.
|
16
18
|
def execute
|
17
19
|
raise 'Command is not implemented'
|
20
|
+
rescue => e
|
21
|
+
Rollbar.error(e)
|
22
|
+
raise
|
18
23
|
end
|
19
24
|
|
20
25
|
# To be overridden during inheritance, to e.g. connect to a database.
|
21
26
|
# Should be called with super by inheriting method.
|
22
27
|
def setup(config)
|
23
28
|
Etna::Application.find(self.class).configure(config)
|
29
|
+
rescue => e
|
30
|
+
Rollbar.error(e)
|
31
|
+
raise
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class DirectedGraph
|
2
|
+
def initialize
|
3
|
+
@children = {}
|
4
|
+
@parents = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :children
|
8
|
+
attr_reader :parents
|
9
|
+
|
10
|
+
def add_connection(parent, child)
|
11
|
+
children = @children[parent] ||= {}
|
12
|
+
child_children = @children[child] ||= {}
|
13
|
+
|
14
|
+
children[child] = child_children
|
15
|
+
|
16
|
+
parents = @parents[child] ||= {}
|
17
|
+
parent_parents = @parents[parent] ||= {}
|
18
|
+
parents[parent] = parent_parents
|
19
|
+
end
|
20
|
+
|
21
|
+
def descendants(parent)
|
22
|
+
seen = Set.new
|
23
|
+
|
24
|
+
seen.add(parent)
|
25
|
+
queue = @children[parent].keys.dup
|
26
|
+
parent_queue = @parents[parent].keys.dup
|
27
|
+
|
28
|
+
# Because this is not an acyclical graph, the definition of descendants needs to be stronger;
|
29
|
+
# here we believe that any path that would move through --any-- parent to this child would not be considered
|
30
|
+
# descendant, so we first find all those parents and mark them as 'seen' so that they are not traveled.
|
31
|
+
while next_parent = parent_queue.pop
|
32
|
+
next if seen.include?(next_parent)
|
33
|
+
seen.add(next_parent)
|
34
|
+
parent_queue.push(*@parents[next_parent].keys)
|
35
|
+
end
|
36
|
+
|
37
|
+
queue = queue.nil? ? [] : queue.dup
|
38
|
+
paths = {}
|
39
|
+
|
40
|
+
while child = queue.pop
|
41
|
+
next if seen.include? child
|
42
|
+
seen.add(child)
|
43
|
+
path = (paths[child] ||= [parent])
|
44
|
+
|
45
|
+
@children[child].keys.each do |child_child|
|
46
|
+
queue.push child_child
|
47
|
+
|
48
|
+
unless paths.include? child_child
|
49
|
+
paths[child_child] = path + [child]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
paths
|
55
|
+
end
|
56
|
+
end
|
data/lib/etna/hmac.rb
CHANGED
@@ -60,6 +60,7 @@ module Etna
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def valid_id?
|
63
|
+
return false if !@application.config(:hmac_keys)
|
63
64
|
@application.config(:hmac_keys).key?(@id)
|
64
65
|
end
|
65
66
|
|
@@ -78,7 +79,7 @@ module Etna
|
|
78
79
|
# these are set as headers or params
|
79
80
|
@nonce,
|
80
81
|
@id,
|
81
|
-
@headers.
|
82
|
+
@headers.to_json,
|
82
83
|
@expiration,
|
83
84
|
].join("\n")
|
84
85
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Etna
|
2
|
+
module JsonSerializableStruct
|
3
|
+
def self.included(cls)
|
4
|
+
cls.instance_eval do
|
5
|
+
def self.as_json(v)
|
6
|
+
if v.respond_to? :as_json
|
7
|
+
return v.as_json
|
8
|
+
end
|
9
|
+
|
10
|
+
if v.is_a? Hash
|
11
|
+
return v.map { |k, v| [k, as_json(v)] }.to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
if v.class.include? Enumerable
|
15
|
+
return v.map { |v| as_json(v) }
|
16
|
+
end
|
17
|
+
|
18
|
+
v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def as_json
|
24
|
+
members.map do |k|
|
25
|
+
v = self.class.as_json(send(k))
|
26
|
+
[k, v]
|
27
|
+
end.to_h.delete_if { |k, v| v.nil? }
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_json
|
31
|
+
as_json.to_json
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/etna/logger.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'rollbar'
|
2
3
|
|
3
4
|
module Etna
|
4
5
|
class Logger < ::Logger
|
5
6
|
def initialize(log_dev, age, size)
|
6
7
|
super
|
7
|
-
|
8
8
|
self.formatter = proc do |severity, datetime, progname, msg|
|
9
9
|
format(severity, datetime, progname, msg)
|
10
10
|
end
|
@@ -14,11 +14,28 @@ module Etna
|
|
14
14
|
"#{severity}:#{datetime.iso8601} #{msg}\n"
|
15
15
|
end
|
16
16
|
|
17
|
+
def warn(msg, &block)
|
18
|
+
super
|
19
|
+
Rollbar.warn(msg)
|
20
|
+
end
|
21
|
+
|
22
|
+
def error(msg, &block)
|
23
|
+
super
|
24
|
+
Rollbar.error(msg)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fatal(msg, &block)
|
28
|
+
super
|
29
|
+
Rollbar.error(msg)
|
30
|
+
end
|
31
|
+
|
17
32
|
def log_error(e)
|
18
33
|
error(e.message)
|
19
34
|
e.backtrace.each do |trace|
|
20
35
|
error(trace)
|
21
36
|
end
|
37
|
+
|
38
|
+
Rollbar.error(e)
|
22
39
|
end
|
23
40
|
|
24
41
|
def log_request(request)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Etna
|
2
|
+
module MultipartSerializableNestedHash
|
3
|
+
def self.included(cls)
|
4
|
+
cls.instance_eval do
|
5
|
+
def self.encode_multipart_pairs(value, base_key, is_root, &block)
|
6
|
+
if value.is_a? Hash
|
7
|
+
value.each do |k, v|
|
8
|
+
encode_multipart_pairs(v, is_root ? k : "#{base_key}[#{k}]", false, &block)
|
9
|
+
end
|
10
|
+
elsif value.is_a? Array
|
11
|
+
value.each_with_index do |v, i|
|
12
|
+
# This is necessary to ensure that arrays of hashes that have hetergenous keys still get parsed correctly
|
13
|
+
# Since the only way to indicate a new entry in the array of hashes is by re-using a key that existed in
|
14
|
+
# the previous hash.
|
15
|
+
if v.is_a? Hash
|
16
|
+
encode_multipart_pairs(i, "#{base_key}[][_idx]", false, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
encode_multipart_pairs(v, "#{base_key}[]", false, &block)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
raise "base_key cannot be empty for a scalar value!" if base_key.length == 0
|
23
|
+
yield [base_key, value.respond_to?(:read) ? UploadIO.new(value, 'application/octet-stream') : value.to_s]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def self.encode_multipart_content(value, base_key = '', is_root = true)
|
29
|
+
result = []
|
30
|
+
self.encode_multipart_pairs(value, base_key, is_root) { |pair| result << pair }
|
31
|
+
result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def encode_multipart_content(base_key = '')
|
37
|
+
value = self
|
38
|
+
if value.respond_to? :as_json
|
39
|
+
value = value.as_json
|
40
|
+
end
|
41
|
+
|
42
|
+
self.class.encode_multipart_content(value, base_key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/etna/parse_body.rb
CHANGED
data/lib/etna/server.rb
CHANGED
data/lib/etna/test_auth.rb
CHANGED
data/lib/etna/user.rb
CHANGED
@@ -7,7 +7,9 @@ module Etna
|
|
7
7
|
}
|
8
8
|
|
9
9
|
def initialize params, token=nil
|
10
|
-
@first, @last, @email, @encoded_permissions = params.values_at(:first, :last, :email, :perm)
|
10
|
+
@first, @last, @email, @encoded_permissions, encoded_flags = params.values_at(:first, :last, :email, :perm, :flags)
|
11
|
+
|
12
|
+
@flags = encoded_flags&.split(/;/) || []
|
11
13
|
@token = token unless !token
|
12
14
|
raise ArgumentError, "No email given!" unless @email
|
13
15
|
end
|
@@ -30,6 +32,10 @@ module Etna
|
|
30
32
|
end.inject([],:+).to_h
|
31
33
|
end
|
32
34
|
|
35
|
+
def has_flag?(flag)
|
36
|
+
@flags.include?(flag)
|
37
|
+
end
|
38
|
+
|
33
39
|
def name
|
34
40
|
"#{first} #{last}"
|
35
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etna
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Saurabh Asthana
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rollbar
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: See summary
|
70
84
|
email: Saurabh.Asthana@ucsf.edu
|
71
85
|
executables: []
|
@@ -76,14 +90,23 @@ files:
|
|
76
90
|
- lib/etna/application.rb
|
77
91
|
- lib/etna/auth.rb
|
78
92
|
- lib/etna/client.rb
|
93
|
+
- lib/etna/clients/magma.rb
|
94
|
+
- lib/etna/clients/magma/client.rb
|
95
|
+
- lib/etna/clients/magma/models.rb
|
96
|
+
- lib/etna/clients/metis.rb
|
97
|
+
- lib/etna/clients/metis/client.rb
|
98
|
+
- lib/etna/clients/metis/models.rb
|
79
99
|
- lib/etna/command.rb
|
80
100
|
- lib/etna/controller.rb
|
81
101
|
- lib/etna/cross_origin.rb
|
82
102
|
- lib/etna/describe_routes.rb
|
103
|
+
- lib/etna/directed_graph.rb
|
83
104
|
- lib/etna/errors.rb
|
84
105
|
- lib/etna/ext.rb
|
85
106
|
- lib/etna/hmac.rb
|
107
|
+
- lib/etna/json_serializable_struct.rb
|
86
108
|
- lib/etna/logger.rb
|
109
|
+
- lib/etna/multipart_serializable_nested_hash.rb
|
87
110
|
- lib/etna/parse_body.rb
|
88
111
|
- lib/etna/route.rb
|
89
112
|
- lib/etna/server.rb
|