empire-client 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/empire.rb +178 -0
  3. data/lib/walkthrough.rb +101 -0
  4. metadata +104 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 528dc8d235093675e0f22b738a0f16d8e7134e09
4
+ data.tar.gz: c6b479ff28bef1e9f7ad0a9d219923b3e3a4078b
5
+ SHA512:
6
+ metadata.gz: 0534ce3f0f14d9525eecbd921a7f6e9d1b51e378d6ffc39f5f67b0f841b1c376cb130116928a0ab41843c97da7aab69b2f69e080cce88502fca147029c2ff498
7
+ data.tar.gz: 5948cc3f5ee664f930b4586a9cd21231ffc2e1806a5c0e4ea084c8c2835c0792371493b574c1f750b65794960e719c5afa012261ec818fd7485990b21f689854
data/lib/empire.rb ADDED
@@ -0,0 +1,178 @@
1
+ require 'yaml'
2
+ require 'json'
3
+ require 'date'
4
+
5
+ require 'httpclient'
6
+ require 'irb-pager'
7
+
8
+ require_relative 'walkthrough.rb'
9
+
10
+ class Empire
11
+ attr_reader :base_url
12
+
13
+ # app_key is your Empire application key, and is necessary for using the API
14
+ # opts can include any of:
15
+ # * :api_server => the server to connect to (default: api.empiredata.com)
16
+ # * :end_user => a string identifying the end user, required for any operations on views (default: nil)
17
+ # * :secrets_yaml => the path to a YAML file generated by https://login.empiredata.co (default: nil)
18
+ def initialize(app_key = nil, opts = {})
19
+ api_server = opts[:api_server] || 'api.empiredata.co'
20
+
21
+ @app_key = app_key
22
+ @end_user = opts[:end_user]
23
+ @session_key = nil
24
+
25
+ @http_client = HTTPClient.new
26
+
27
+ protocol = api_server.start_with?('localhost') ? 'http' : 'https'
28
+ @base_url = "#{protocol}://#{api_server}/empire/"
29
+
30
+ @service_secrets = nil
31
+ if opts[:secrets_yaml]
32
+ @service_secrets = YAML.load_file opts[:secrets_yaml]
33
+ end
34
+ end
35
+
36
+ # Connect to specific service
37
+ # service: service name
38
+ # secrets: hash with service secrets (optional if the Empire instance was initialized with a secrets_yaml)
39
+ def connect(service, secrets = nil)
40
+ path = "services/#{service}/connect"
41
+ unless secrets
42
+ unless @service_secrets
43
+ raise "secrets must be provided on connect command,
44
+ or secrets_yaml file must be given when constructing this instance"
45
+ end
46
+
47
+ secrets = {}
48
+ @service_secrets[service]['option'].each{|k, v| secrets[k] = v['value']}
49
+ end
50
+ request path, :post, {}, secrets
51
+ end
52
+
53
+ # Describe all services, all tables within a given service, or a given table
54
+ def describe(service = nil, table = nil)
55
+ path = 'services'
56
+ if service and table
57
+ path += "/#{service}/#{table}"
58
+ elsif service and !table
59
+ path += "/#{service}"
60
+ elsif !service and table
61
+ raise "Service must be specified if table is specified"
62
+ end
63
+
64
+ request path
65
+ end
66
+
67
+ # Paginated printing of an SQL query
68
+ def print_query(sql)
69
+ IRB::Pager.pager {
70
+ query(sql) do |l|
71
+ puts l
72
+ end
73
+ }
74
+ end
75
+
76
+ # Issue a SQL query, yielding each row
77
+ def query(sql)
78
+ path = 'query'
79
+ io = request path, :post, {}, {query: sql}, stream: true
80
+ io.each do |l|
81
+ yield l
82
+ end
83
+ end
84
+
85
+ # Insert a new row into this service table. The row should be a hash of {column: value}
86
+ def insert(service, table, row)
87
+ path = "services/#{service}/#{table}"
88
+ request path, :post, {}, row
89
+ end
90
+
91
+ # Materialize a SQL query as a view. This creates or updates a view.
92
+ def materialize_view(name, sql)
93
+ unless @end_user
94
+ raise "Cannot use materialized view within a session initiated without an enduser"
95
+ end
96
+ path = "view/#{name}"
97
+ data = {'query' => sql}
98
+ request path, :put, {}, data
99
+ end
100
+
101
+ # Delete a materialized view of SQL query
102
+ def drop_view(name)
103
+ unless @end_user
104
+ raise "Cannot use materialized view within a session initiated without an enduser"
105
+ end
106
+ path = "view/#{name}"
107
+ request path, :delete
108
+ end
109
+
110
+ # Boolean check if a materialized view is ready for querying.
111
+ # @note The user is expected to check view_ready? before querying a view with query()
112
+ def view_ready?(name)
113
+ status = view_status(name)
114
+
115
+ case status['viewStatus']
116
+ when 'ready' then true
117
+ when 'pending' then false
118
+ else
119
+ raise "Unknown view status: #{response['viewStatus']}"
120
+ end
121
+ end
122
+
123
+ # Datetime that this view was materialized at.
124
+ # nil if the materialization is currently pending.
125
+ def view_materialized_at(name)
126
+ status = view_status(name)
127
+ Date.parse(status['materializedAt']) rescue nil
128
+ end
129
+
130
+ # emulate default Object#inspect method but only display the object_id, not the properties
131
+ # to make things cleaner and more similar to Python client
132
+ def inspect
133
+ "#<Empire:#{(object_id << 1).to_s(16)}>"
134
+ end
135
+
136
+ private
137
+
138
+ def view_status(name)
139
+ unless @end_user
140
+ raise "Cannot use materialized view within a session initiated without an enduser"
141
+ end
142
+ path = "view/#{name}/status"
143
+ request path, :get
144
+ end
145
+
146
+ def request(path, method = :get, headers = {}, data = {}, opts = {})
147
+ create_session unless @sessionkey
148
+ headers_with_session = headers.merge({'Authorization' => "Empire sessionkey=\"#{@sessionkey}\""})
149
+ do_request path, method, headers_with_session, data, opts
150
+ end
151
+
152
+ def create_session
153
+ headers = {'Authorization' => "Empire appkey=\"#{@app_key}\""}
154
+ session_url = "session/create"
155
+ if @end_user
156
+ session_url = session_url + "?enduser=#{@end_user}"
157
+ end
158
+ data = do_request session_url, :post, headers
159
+ @sessionkey = data['sessionkey']
160
+ end
161
+
162
+ def do_request(path, method = :get, headers = {}, data = {}, opts = {})
163
+ url = @base_url + path
164
+ headers.merge!({'Content-Type' => 'application/json', 'Accept' => '*/*'})
165
+ if opts[:stream]
166
+ # return an IO object representing the streamed content
167
+ conn = @http_client.request_async method, url, nil, data.to_json, headers
168
+ conn.pop.content
169
+ else
170
+ # return the response body
171
+ response = @http_client.request method, url, body: data.to_json, header: headers
172
+ if response.status == HTTP::Status::INTERNAL
173
+ raise "Internal Server Error"
174
+ end
175
+ JSON.parse(response.body)
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,101 @@
1
+
2
+ class Empire
3
+
4
+ # Run automatic test of all services from YAML
5
+ def walkthrough
6
+ @last_service = nil
7
+ @last_table = nil
8
+
9
+ unless @service_secrets
10
+ puts "Please connect some services in https://login.empiredata.co, and download the new yaml file"
11
+ return
12
+ end
13
+
14
+ @service_secrets.each do |secret|
15
+ service = secret[0]
16
+ walkthrough_service(service)
17
+ end
18
+
19
+ walkthrough_materialized_view(@last_service, @last_table)
20
+
21
+ nil
22
+ end
23
+
24
+ private
25
+
26
+ def walkthrough_service(service)
27
+ puts "empire.connect '#{service}'"
28
+ begin
29
+ connect service
30
+ rescue
31
+ puts "Problem connecting to #{service}"
32
+ return
33
+ end
34
+
35
+ tables = describe service
36
+
37
+ unless tables and tables['service'] and tables['service']['tables']
38
+ puts "Can't find tables belonging to #{service}"
39
+ return
40
+ end
41
+
42
+ tables['service']['tables'].each do |table_data|
43
+ table = table_data['table']
44
+ walkthrough_table(service, table)
45
+ end
46
+
47
+ @last_service = service
48
+ end
49
+
50
+ def walkthrough_table(service, table)
51
+ if service == "mailchimp"
52
+ # These mailchimp tables can only be queried when filtering by a particular list.
53
+ if ["list_member", "campaign", "campaign_sent_to", "campaign_opened"].include? table
54
+ return
55
+ end
56
+ end
57
+
58
+ begin
59
+ sql = "SELECT * FROM #{service}.#{table} LIMIT 5"
60
+ puts "empire.query '#{sql}'"
61
+ query(sql) do |row|
62
+ print_row(row, 75)
63
+ end
64
+ rescue Exception => e
65
+ puts "Problem with #{service}.#{table}"
66
+ end
67
+
68
+ @last_table = table
69
+ end
70
+
71
+ def walkthrough_materialized_view(service, table)
72
+ unless @end_user
73
+ puts "Please specify an end_user parameter when instantiating the client, so that you can try materialized views"
74
+ return
75
+ end
76
+
77
+ puts "empire.materialize_view('view_name', 'SELECT * FROM #{service}.#{table} LIMIT 5')"
78
+ materialize_view('view_name', "SELECT * FROM #{service}.#{table} LIMIT 5")
79
+
80
+ puts "until empire.view_ready? 'view_name'\n sleep 0.1\nend"
81
+ until view_ready? 'view_name'
82
+ sleep 0.01
83
+ end
84
+
85
+ puts "empire.query 'SELECT * FROM view_name'"
86
+ query('SELECT * FROM view_name') do |row|
87
+ print_row(row, 75)
88
+ end
89
+
90
+ puts "empire.drop_view 'view_name'"
91
+ drop_view 'view_name'
92
+ end
93
+
94
+ def print_row(row, max_length)
95
+ fragment = row.slice(0, max_length)
96
+ if fragment.length == max_length
97
+ fragment = fragment + "..."
98
+ end
99
+ puts " #{fragment}"
100
+ end
101
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: empire-client
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.3'
5
+ platform: ruby
6
+ authors:
7
+ - UPSHOT Data, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httpclient
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: irb-pager
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.18'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.18'
69
+ description: Empire is an API for accessing enterprise SaaS services such as Salesforce,
70
+ Zendesk, Google Apps, etc. It provides a uniform, database-like interface to every
71
+ service that it supports. Empire makes it easy to integrate data from multiple enterprise
72
+ services into your own enterprise app.
73
+ email: hello@empiredata.co
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - lib/empire.rb
79
+ - lib/walkthrough.rb
80
+ homepage: http://empiredata.co
81
+ licenses:
82
+ - Apache License, Version 2.0
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.9.3
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.3.0
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Ruby client for the Empire API
104
+ test_files: []