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
@@ -15,3 +15,9 @@ require_relative './etna/describe_routes'
15
15
  require_relative './etna/client'
16
16
  require_relative './etna/symbolize_params'
17
17
  require_relative './etna/spec'
18
+ require_relative './etna/clients'
19
+ require_relative './etna/environment_scoped'
20
+
21
+ class EtnaApp
22
+ include Etna::Application
23
+ end
@@ -4,18 +4,31 @@
4
4
  # whenever we want to use it as a container
5
5
 
6
6
  require_relative './sign_service'
7
+ require_relative './command'
8
+ require_relative './generate_autocompletion_script'
7
9
  require 'singleton'
8
10
  require 'rollbar'
9
11
 
10
12
  module Etna::Application
11
13
  def self.included(other)
12
14
  other.include Singleton
15
+ other.include Etna::CommandExecutor
16
+ @@application = other
17
+
18
+ other.const_set(:GenerateCompletionScript, Class.new(Etna::GenerateCompletionScript))
13
19
  end
14
20
 
15
21
  def self.find(klass)
16
- Kernel.const_get(
17
- klass.name.split('::').first
18
- ).instance
22
+ namespace = klass.name.split('::').first
23
+ if (namespace_klass = Kernel.const_get(namespace)) && (namespace_klass.respond_to? :instance)
24
+ return namespace_klass.instance
25
+ end
26
+
27
+ if @@application
28
+ return @@application.instance
29
+ end
30
+
31
+ raise "Could not find application instance from #{namespace}, and not subclass of Application found."
19
32
  end
20
33
 
21
34
  def self.register(app)
@@ -56,8 +69,18 @@ module Etna::Application
56
69
  # the application logger is available globally
57
70
  attr_reader :logger
58
71
 
59
- def config(type)
60
- @config[environment][type]
72
+ def config(type, env = environment)
73
+ return nil if @config.nil?
74
+ return nil if @config[env].nil?
75
+ return nil unless @config[env].is_a?(Hash)
76
+ @config[env][type]
77
+ end
78
+
79
+ def env_config(env = environment)
80
+ return nil if @config.nil?
81
+ return nil if @config[env].nil?
82
+ return nil unless @config[env].is_a?(Hash)
83
+ @config[env]
61
84
  end
62
85
 
63
86
  def sign
@@ -74,23 +97,18 @@ module Etna::Application
74
97
  end
75
98
  end
76
99
 
77
- def run_command(config, cmd = :help, *args)
78
- cmd = cmd.to_sym
79
- if commands.key?(cmd)
80
- commands[cmd].setup(config)
81
- commands[cmd].execute(*args)
82
- else
83
- commands[:help].execute
100
+ def run_command(config, *args, &block)
101
+ cmd, cmd_args, cmd_kwds = find_command(*args)
102
+ cmd.setup(config)
103
+
104
+ if block_given?
105
+ return unless yield [cmd, cmd_args]
84
106
  end
85
- end
86
107
 
87
- def commands
88
- @commands ||= Hash[
89
- find_descendents(Etna::Command).map do |c|
90
- cmd = c.new
91
- [ cmd.name, cmd ]
92
- end
93
- ]
108
+ cmd.execute(*cmd.fill_in_missing_params(cmd_args), **cmd_kwds)
109
+ rescue => e
110
+ Rollbar.error(e)
111
+ raise
94
112
  end
95
113
  end
96
114
 
@@ -1,13 +1,15 @@
1
1
  require 'net/http/persistent'
2
2
  require 'net/http/post/multipart'
3
3
  require 'singleton'
4
+ require 'rack/utils'
4
5
 
5
6
  module Etna
6
7
  class Client
7
- def initialize(host, token, routes_available: true)
8
- @host = host.sub(%r!/$!,'')
8
+ def initialize(host, token, routes_available: true, persistent: true, ignore_ssl: false)
9
+ @host = host.sub(%r!/$!, '')
9
10
  @token = token
10
-
11
+ @persistent = persistent
12
+ @ignore_ssl = ignore_ssl
11
13
 
12
14
  if routes_available
13
15
  set_routes
@@ -23,24 +25,24 @@ module Etna
23
25
 
24
26
  def multipart_post(endpoint, content, &block)
25
27
  uri = request_uri(endpoint)
26
- multipart = Net::HTTP::Post::Multipart.new uri.path, content
28
+ multipart = Net::HTTP::Post::Multipart.new uri.request_uri, content
27
29
  multipart.add_field('Authorization', "Etna #{@token}")
28
30
  request(uri, multipart, &block)
29
31
  end
30
32
 
31
- def post(endpoint, params={}, &block)
33
+ def post(endpoint, params = {}, &block)
32
34
  body_request(Net::HTTP::Post, endpoint, params, &block)
33
35
  end
34
36
 
35
- def get(endpoint, params={}, &block)
37
+ def get(endpoint, params = {}, &block)
36
38
  query_request(Net::HTTP::Get, endpoint, params, &block)
37
39
  end
38
40
 
39
- def options(endpoint, params={}, &block)
41
+ def options(endpoint, params = {}, &block)
40
42
  query_request(Net::HTTP::Options, endpoint, params, &block)
41
43
  end
42
44
 
43
- def delete(endpoint, params={}, &block)
45
+ def delete(endpoint, params = {}, &block)
44
46
  body_request(Net::HTTP::Delete, endpoint, params, &block)
45
47
  end
46
48
 
@@ -55,12 +57,12 @@ module Etna
55
57
  def define_route_helpers
56
58
  @routes.each do |route|
57
59
  next unless route[:name]
58
- self.define_singleton_method(route[:name]) do |params={}|
60
+ self.define_singleton_method(route[:name]) do |params = {}|
59
61
 
60
62
  missing_params = (route[:params] - params.keys.map(&:to_s))
61
63
  unless missing_params.empty?
62
64
  raise ArgumentError, "Missing required #{missing_params.size > 1 ?
63
- 'params' : 'param'} #{missing_params.join(', ')}"
65
+ 'params' : 'param'} #{missing_params.join(', ')}"
64
66
  end
65
67
 
66
68
  response = send(route[:method].downcase, route_path(route, params), params)
@@ -79,23 +81,29 @@ module Etna
79
81
 
80
82
  def persistent_connection
81
83
  @http ||= begin
82
- http = Net::HTTP::Persistent.new
83
- http.read_timeout = 3600
84
- http
85
- end
84
+ http = Net::HTTP::Persistent.new
85
+ http.read_timeout = 3600
86
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @ignore_ssl
87
+ http
88
+ end
86
89
  end
87
90
 
88
91
 
89
- def body_request(type, endpoint, params={}, &block)
92
+ def body_request(type, endpoint, params = {}, &block)
90
93
  uri = request_uri(endpoint)
91
- req = type.new(uri.request_uri,request_params)
94
+ req = type.new(uri.request_uri, request_params)
92
95
  req.body = params.to_json
93
96
  request(uri, req, &block)
94
97
  end
95
98
 
96
- def query_request(type, endpoint, params={}, &block)
99
+ def query_request(type, endpoint, params = {}, &block)
97
100
  uri = request_uri(endpoint)
98
- uri.query = URI.encode_www_form(params)
101
+
102
+ if uri.query
103
+ uri.query += "&" + URI.encode_www_form(params)
104
+ else
105
+ uri.query = URI.encode_www_form(params)
106
+ end
99
107
  req = type.new(uri.request_uri, request_params)
100
108
  request(uri, req, &block)
101
109
  end
@@ -106,9 +114,9 @@ module Etna
106
114
 
107
115
  def request_params
108
116
  {
109
- 'Content-Type' => 'application/json',
110
- 'Accept'=> 'application/json, text/*',
111
- 'Authorization'=>"Etna #{@token}"
117
+ 'Content-Type' => 'application/json',
118
+ 'Accept' => 'application/json, text/*',
119
+ 'Authorization' => "Etna #{@token}"
112
120
  }
113
121
  end
114
122
 
@@ -116,8 +124,8 @@ module Etna
116
124
  status = response.code.to_i
117
125
  if status >= 400
118
126
  msg = response.content_type == 'application/json' ?
119
- json_error(response.body) :
120
- response.body
127
+ json_error(response.body) :
128
+ response.body
121
129
  raise Etna::Error.new(msg, status)
122
130
  end
123
131
  end
@@ -133,14 +141,37 @@ module Etna
133
141
 
134
142
  def request(uri, data)
135
143
  if block_given?
136
- persistent_connection.request(uri, data) do |response|
137
- status_check!(response)
138
- yield response
144
+ if @persistent
145
+ persistent_connection.request(uri, data) do |response|
146
+ status_check!(response)
147
+ yield response
148
+ end
149
+ else
150
+ verify_mode = @ignore_ssl ?
151
+ OpenSSL::SSL::VERIFY_NONE :
152
+ OpenSSL::SSL::VERIFY_PEER
153
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: verify_mode) do |http|
154
+ http.request(data) do |response|
155
+ status_check!(response)
156
+ yield response
157
+ end
158
+ end
139
159
  end
140
160
  else
141
- response = persistent_connection.request(uri, data)
142
- status_check!(response)
143
- return response
161
+ if @persistent
162
+ response = persistent_connection.request(uri, data)
163
+ status_check!(response)
164
+ return response
165
+ else
166
+ verify_mode = @ignore_ssl ?
167
+ OpenSSL::SSL::VERIFY_NONE :
168
+ OpenSSL::SSL::VERIFY_PEER
169
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: verify_mode) do |http|
170
+ response = http.request(data)
171
+ status_check!(response)
172
+ return response
173
+ end
174
+ end
144
175
  end
145
176
  end
146
177
  end
@@ -0,0 +1,4 @@
1
+ require_relative 'clients/magma'
2
+ require_relative 'clients/metis'
3
+ require_relative 'clients/janus'
4
+ require_relative 'clients/polyphemus'
@@ -0,0 +1,9 @@
1
+ module Enum
2
+ def self.included(cls)
3
+ cls.instance_eval do
4
+ def entries
5
+ self.constants.map { |c| self.const_get(c) }.select { |v| v.instance_of?(self) }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,2 @@
1
+ require_relative './janus/client'
2
+ require_relative './janus/models'
@@ -0,0 +1,73 @@
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 Janus
10
+ def initialize(host:, token:, persistent: true, ignore_ssl: false)
11
+ raise 'Janus client configuration is missing host.' unless host
12
+ raise 'Janus client configuration is missing token.' unless token
13
+ @etna_client = ::Etna::Client.new(
14
+ host,
15
+ token,
16
+ routes_available: false,
17
+ persistent: persistent,
18
+ ignore_ssl: ignore_ssl)
19
+ end
20
+
21
+ def get_project(get_project_request = GetProjectRequest.new)
22
+ html = nil
23
+ @etna_client.get(
24
+ "/project/#{get_project_request.project_name}",
25
+ get_project_request) do |res|
26
+ html = res.body
27
+ end
28
+
29
+ HtmlResponse.new(html)
30
+ end
31
+
32
+ def add_project(add_project_request = AddProjectRequest.new)
33
+ @etna_client.post('/add_project', add_project_request) do |res|
34
+ # Redirect, no response data
35
+ end
36
+ end
37
+
38
+ def add_user(add_user_request = AddUserRequest.new)
39
+ @etna_client.post(
40
+ "/add_user/#{add_user_request.project_name}",
41
+ add_user_request) do |res|
42
+ # Redirect, no response data
43
+ end
44
+ end
45
+
46
+ def update_permission(update_permission_request = UpdatePermissionRequest.new)
47
+ @etna_client.post(
48
+ "/update_permission/#{update_permission_request.project_name}",
49
+ update_permission_request) do |res|
50
+ # Redirect, no response data
51
+ end
52
+ end
53
+
54
+ def refresh_token(refresh_token_request = RefreshTokenRequest.new)
55
+ token = nil
56
+ @etna_client.get('/refresh_token', refresh_token_request) do |res|
57
+ token = res.body
58
+ end
59
+
60
+ TokenResponse.new(token)
61
+ end
62
+
63
+ def viewer_token(viewer_token_request = ViewerTokenRequest.new)
64
+ token = nil
65
+ @etna_client.get('/viewer_token', viewer_token_request) do |res|
66
+ token = res.body
67
+ end
68
+
69
+ TokenResponse.new(token)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,78 @@
1
+ require 'ostruct'
2
+ require_relative '../../json_serializable_struct'
3
+
4
+ # TODO: In the near future, I'd like to transition to specifying apis via SWAGGER and generating model stubs from the
5
+ # common definitions. For nowe I've written them out by hand here.
6
+ module Etna
7
+ module Clients
8
+ class Janus
9
+ class GetProjectRequest < Struct.new(:project_name, keyword_init: true)
10
+ include JsonSerializableStruct
11
+
12
+ def initialize(**params)
13
+ super({}.update(params))
14
+ end
15
+ end
16
+
17
+ class AddProjectRequest < Struct.new(:project_name, :project_name_full, keyword_init: true)
18
+ include JsonSerializableStruct
19
+
20
+ def initialize(**params)
21
+ super({}.update(params))
22
+ end
23
+ end
24
+
25
+ class AddUserRequest < Struct.new(:project_name, :email, :name, :role, :privileged, :affiliation, keyword_init: true)
26
+ include JsonSerializableStruct
27
+
28
+ def initialize(**params)
29
+ super({privileged: false, affiliation: ''}.update(params))
30
+ end
31
+ end
32
+
33
+ class UpdatePermissionRequest < Struct.new(:project_name, :email, :role, :privileged, :affiliation, keyword_init: true)
34
+ include JsonSerializableStruct
35
+
36
+ def initialize(**params)
37
+ super({privileged: false, affiliation: ''}.update(params))
38
+ end
39
+ end
40
+
41
+ class RefreshTokenRequest
42
+ def map
43
+ []
44
+ end
45
+ end
46
+
47
+ class ViewerTokenRequest
48
+ def map
49
+ []
50
+ end
51
+ end
52
+
53
+ class HtmlResponse
54
+ attr_reader :raw
55
+
56
+ def initialize(raw = '')
57
+ @raw = raw
58
+ end
59
+
60
+ def html
61
+ @raw
62
+ end
63
+ end
64
+
65
+ class TokenResponse
66
+ attr_reader :raw
67
+
68
+ def initialize(raw = '')
69
+ @raw = raw
70
+ end
71
+
72
+ def token
73
+ @raw
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,2 +1,4 @@
1
1
  require_relative './magma/client'
2
2
  require_relative './magma/models'
3
+ require_relative './magma/workflows'
4
+ require_relative './magma/formatting'
@@ -7,10 +7,19 @@ require_relative './models'
7
7
  module Etna
8
8
  module Clients
9
9
  class Magma
10
- def initialize(host:, token:)
10
+ attr_reader :host, :token, :ignore_ssl
11
+ def initialize(host:, token:, persistent: true, ignore_ssl: false)
11
12
  raise 'Magma client configuration is missing host.' unless host
12
13
  raise 'Magma client configuration is missing token.' unless token
13
- @etna_client = ::Etna::Client.new(host, token, routes_available: false)
14
+ @etna_client = ::Etna::Client.new(
15
+ host,
16
+ token,
17
+ routes_available: false,
18
+ persistent: persistent,
19
+ ignore_ssl: ignore_ssl)
20
+ @host = host
21
+ @token = token
22
+ @ignore_ssl = ignore_ssl
14
23
  end
15
24
 
16
25
  # This endpoint returns models and records by name:
@@ -20,7 +29,7 @@ module Etna
20
29
  # record_names: [ "rn1", "rn2" ], # or "all",
21
30
  # attribute_names: "all"
22
31
  # }
23
- def retrieve(retrieval_request)
32
+ def retrieve(retrieval_request = RetrievalRequest.new)
24
33
  json = nil
25
34
  @etna_client.post('/retrieve', retrieval_request) do |res|
26
35
  json = JSON.parse(res.body)
@@ -31,7 +40,7 @@ module Etna
31
40
 
32
41
  # This 'query' end point is used to fetch data by graph query
33
42
  # See question.rb for more detail
34
- def query(query_request)
43
+ def query(query_request = QueryRequest.new)
35
44
  json = nil
36
45
  @etna_client.post('/query', query_request) do |res|
37
46
  json = JSON.parse(res.body)
@@ -40,10 +49,7 @@ module Etna
40
49
  QueryResponse.new(json)
41
50
  end
42
51
 
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)
52
+ def update(update_request = UpdateRequest.new)
47
53
  json = nil
48
54
  @etna_client.multipart_post('/update', update_request.encode_multipart_content) do |res|
49
55
  json = JSON.parse(res.body)
@@ -52,7 +58,16 @@ module Etna
52
58
  UpdateResponse.new(json)
53
59
  end
54
60
 
55
- def update_model(update_model_request)
61
+ def update_json(update_request = UpdateRequest.new)
62
+ json = nil
63
+ @etna_client.post('/update', update_request) do |res|
64
+ json = JSON.parse(res.body)
65
+ end
66
+
67
+ UpdateResponse.new(json)
68
+ end
69
+
70
+ def update_model(update_model_request = UpdateModelRequest.new)
56
71
  json = nil
57
72
  @etna_client.post('/update_model', update_model_request) do |res|
58
73
  json = JSON.parse(res.body)