mirror-api 0.0.9 → 0.1.0
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.
- data/.coveralls.yml +1 -0
- data/.rspec +1 -1
- data/README.md +130 -13
- data/lib/mirror-api.rb +0 -1
- data/lib/mirror-api/client.rb +18 -12
- data/lib/mirror-api/errors.rb +10 -0
- data/lib/mirror-api/oauth.rb +8 -6
- data/lib/mirror-api/request.rb +157 -12
- data/lib/mirror-api/request_data.rb +11 -0
- data/lib/mirror-api/resource.rb +15 -4
- data/lib/mirror-api/response_data.rb +15 -0
- data/lib/mirror-api/version.rb +1 -1
- data/spec/client_spec.rb +16 -465
- data/spec/contacts_spec.rb +218 -0
- data/spec/fixtures/subscriptions_item.json +25 -0
- data/spec/fixtures/subscriptions_list.json +30 -0
- data/spec/fixtures/timeline_item.json +7 -7
- data/spec/fixtures/timeline_list.json +15 -0
- data/spec/locations_spec.rb +68 -0
- data/spec/oauth_spec.rb +17 -0
- data/spec/request_data_spec.rb +38 -0
- data/spec/request_spec.rb +66 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/subscriptions_spec.rb +170 -0
- data/spec/timeline_attachments_spec.rb +141 -0
- data/spec/timeline_spec.rb +239 -0
- metadata +26 -3
- data/lib/mirror-api/base.rb +0 -146
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-pro
|
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--colour
|
1
|
+
--colour --format d
|
data/README.md
CHANGED
@@ -4,7 +4,12 @@
|
|
4
4
|
|
5
5
|
# Mirror::Api
|
6
6
|
|
7
|
-
Simple Mirror Api client library
|
7
|
+
Simple Mirror Api client library.
|
8
|
+
|
9
|
+
## Benefits
|
10
|
+
|
11
|
+
- Robust error handling. You can choose whether to bubble errors or not.
|
12
|
+
- Snake case(ruby friendly) notation for requests and responses
|
8
13
|
|
9
14
|
## Installation
|
10
15
|
|
@@ -22,31 +27,143 @@ Or install it yourself as:
|
|
22
27
|
|
23
28
|
## Usage
|
24
29
|
|
25
|
-
|
26
|
-
|
30
|
+
##Getting Started
|
31
|
+
###Require the mirror-api gem.
|
32
|
+
```ruby
|
27
33
|
require "mirror-api"
|
34
|
+
```
|
35
|
+
### Authenticating your client
|
36
|
+
```ruby
|
37
|
+
token = Mirror::OAuth.new(client_id, client_secret, refresh_token)
|
28
38
|
|
29
39
|
api = Mirror::Api::Client.new(token)
|
40
|
+
```
|
41
|
+
##[Timeline](https://developers.google.com/glass/v1/reference/timeline)
|
30
42
|
|
31
|
-
|
43
|
+
### [Listing Timeline items](https://developers.google.com/glass/v1/reference/timeline/list)
|
44
|
+
```ruby
|
32
45
|
items = api.timeline.list
|
46
|
+
```
|
47
|
+
|
48
|
+
### [Inserting a Timeline item](https://developers.google.com/glass/v1/reference/timeline/insert)
|
49
|
+
```ruby
|
50
|
+
item_1 = api.timeline.insert({text: "What up WORLD?!?!"})
|
51
|
+
```
|
52
|
+
|
53
|
+
### [Inserting a Timeline item with reply actions](https://developers.google.com/glass/timeline#user_interaction_with_menu_items)
|
54
|
+
```ruby
|
55
|
+
item_2 = api.timeline.insert({text: "Do you like tacos?", menu_items:[{action: "REPLY"}]})
|
56
|
+
```
|
57
|
+
|
58
|
+
### [Updating a Timeline item](https://developers.google.com/glass/v1/reference/timeline/update)
|
59
|
+
```ruby
|
60
|
+
txt = "Seriously, do you like tacos? This is the second time I had to ask you."
|
61
|
+
item_2 = api.timeline.update(item_2.id, {text: txt, menu_items:[{action: "REPLY"}]})
|
62
|
+
```
|
63
|
+
|
64
|
+
### [Patching a Timeline item](https://developers.google.com/glass/v1/reference/timeline/patch)
|
65
|
+
```ruby
|
66
|
+
item_2 = api.timeline.patch(item_2.id, {text: "You realize you are a bad friend right?", menu_items:[{action: "REPLY"}]})
|
67
|
+
```
|
68
|
+
|
69
|
+
### [Getting a Timeline item](https://developers.google.com/glass/v1/reference/timeline/get)
|
70
|
+
```ruby
|
71
|
+
api.timeline.get(item_2.id)
|
72
|
+
```
|
73
|
+
|
74
|
+
### [Deleting a Timeline item](https://developers.google.com/glass/v1/reference/timeline/delete)
|
75
|
+
```ruby
|
76
|
+
api.timeline.delete(item_2.id)
|
77
|
+
```
|
78
|
+
|
79
|
+
##[Timeline Attachments](https://developers.google.com/glass/v1/reference/timeline/attachments)
|
80
|
+
|
81
|
+
### [Listing Timeline Attachments](https://developers.google.com/glass/v1/reference/timeline/attachments/list)
|
82
|
+
```ruby
|
83
|
+
attachments = api.timeline.list(item_1.id, {attachments:{}})
|
84
|
+
```
|
85
|
+
|
86
|
+
### [Getting a Timeline Attachment](https://developers.google.com/glass/v1/reference/timeline/attachments/get)
|
87
|
+
```ruby
|
88
|
+
#for the sake of getting an id...
|
89
|
+
attachment = attachments.items.first
|
90
|
+
api.timeline.get(item_2.id, {attachments:{id: attachment.id}})
|
91
|
+
```
|
33
92
|
|
34
|
-
|
35
|
-
|
93
|
+
### [Deleting a Timeline Attachment](https://developers.google.com/glass/v1/reference/timeline/attachments/delete)
|
94
|
+
```ruby
|
95
|
+
api.timeline.delete(item_2.id, {attachments:{id: attachment.id}})
|
96
|
+
```
|
97
|
+
|
98
|
+
##[Subscriptions](https://developers.google.com/glass/v1/reference/subscriptions)
|
99
|
+
|
100
|
+
### [Listing Subscriptions](https://developers.google.com/glass/v1/reference/subscriptions/list)
|
101
|
+
```ruby
|
102
|
+
subscriptions = api.subscriptions.list
|
103
|
+
```
|
104
|
+
|
105
|
+
### [Inserting a Subscription](https://developers.google.com/glass/v1/reference/subscriptions/insert)
|
106
|
+
```ruby
|
107
|
+
subscription = api.subscriptions.insert({collection: "timeline", userToken:"user_1", operation: ["UPDATE"], callbackUrl: "https://yourawesomewebsite.com/callback"})
|
108
|
+
```
|
36
109
|
|
37
|
-
|
38
|
-
|
110
|
+
### [Updating a Subscription](https://developers.google.com/glass/v1/reference/subscriptions/update)
|
111
|
+
```ruby
|
112
|
+
item_2 = api.subscriptions.update(subscription.id, {collection: "timeline", operation: ["UPDATE", "INSERT", "DELETE"], callbackUrl: "https://yourawesomewebsite.com/callback"})
|
39
113
|
|
40
|
-
|
114
|
+
```
|
115
|
+
|
116
|
+
### [Deleting a Subscription](https://developers.google.com/glass/v1/reference/subscriptions/delete)
|
117
|
+
```ruby
|
118
|
+
api.subscriptions.delete(item_2.id)
|
119
|
+
```
|
120
|
+
|
121
|
+
##[Locations](https://developers.google.com/glass/v1/reference/locations)
|
122
|
+
|
123
|
+
### [Listing Locations](https://developers.google.com/glass/v1/reference/locations/list)
|
124
|
+
```ruby
|
125
|
+
locations = api.locations.list
|
126
|
+
```
|
41
127
|
|
42
|
-
|
128
|
+
### [Getting a Location](https://developers.google.com/glass/v1/reference/locations/get)
|
129
|
+
```ruby
|
130
|
+
#for the sake of getting an id...
|
131
|
+
location = locations.items.first
|
132
|
+
api.locations.get(location.id)
|
43
133
|
```
|
44
134
|
|
45
|
-
##
|
135
|
+
##[Contacts](https://developers.google.com/glass/v1/reference/contacts)
|
46
136
|
|
47
|
-
|
137
|
+
### [Listing Contacts](https://developers.google.com/glass/v1/reference/contacts/list)
|
138
|
+
```ruby
|
139
|
+
items = api.contacts.list
|
140
|
+
```
|
141
|
+
|
142
|
+
### [Inserting a Contact](https://developers.google.com/glass/v1/reference/contacts/insert)
|
143
|
+
```ruby
|
144
|
+
contact = api.contacts.insert({id: '1234', displayName: 'Troll', imageUrls: ["http://pixelr3ap3r.com/wp-content/uploads/2012/08/357c6328ee4b11e1bfbf22000a1c91a7_7.jpg"]})
|
145
|
+
```
|
146
|
+
|
147
|
+
### [Updating a Contact](https://developers.google.com/glass/v1/reference/contacts/update)
|
148
|
+
```ruby
|
149
|
+
contact = api.contacts.update(contact.id, {displayName: 'Unicorn'})
|
150
|
+
```
|
151
|
+
|
152
|
+
### [Patching a Contact](https://developers.google.com/glass/v1/reference/contacts/patch)
|
153
|
+
```ruby
|
154
|
+
contact = api.contacts.patch(contact.id, {displayName: 'Grumpy Cat', imageUrls: ["http://blog.catmoji.com/wp-content/uploads/2012/09/grumpy-cat.jpeg"]})
|
155
|
+
```
|
156
|
+
|
157
|
+
### [Getting a Contact](https://developers.google.com/glass/v1/reference/contacts/get)
|
158
|
+
```ruby
|
159
|
+
api.contacts.get(contact.id)
|
160
|
+
```
|
161
|
+
|
162
|
+
### [Deleting a Contact](https://developers.google.com/glass/v1/reference/contacts/delete)
|
163
|
+
```ruby
|
164
|
+
api.contacts.delete(contact.id)
|
165
|
+
```
|
48
166
|
|
49
|
-
https://github.com/google/google-api-ruby-client
|
50
167
|
|
51
168
|
## Contributing
|
52
169
|
|
data/lib/mirror-api.rb
CHANGED
data/lib/mirror-api/client.rb
CHANGED
@@ -4,30 +4,36 @@ module Mirror
|
|
4
4
|
module Api
|
5
5
|
class Client
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
attr_accessor :credentials, :options
|
8
|
+
|
9
|
+
def initialize(credentials, options={raise_errors: false})
|
10
|
+
self.credentials = credentials
|
11
|
+
self.options = options
|
12
|
+
raise "Invalid credentials. Missing token" unless (self.credentials && self.credentials[:token])
|
13
|
+
end
|
14
|
+
|
15
|
+
def credentials=(value)
|
16
|
+
if value.is_a?(String)
|
17
|
+
@credentials = {:token => value}
|
18
|
+
elsif value.is_a?(Hash)
|
19
|
+
@credentials = value
|
20
|
+
end
|
15
21
|
end
|
16
22
|
|
17
23
|
def timeline
|
18
|
-
@timeline ||= Resource.new(
|
24
|
+
@timeline ||= Resource.new(credentials, Request::TIMELINE, options)
|
19
25
|
end
|
20
26
|
|
21
27
|
def subscriptions
|
22
|
-
@subscriptions ||= Resource.new(
|
28
|
+
@subscriptions ||= Resource.new(credentials, Request::SUBSCRIPTIONS, options)
|
23
29
|
end
|
24
30
|
|
25
31
|
def locations
|
26
|
-
@locations ||= Resource.new(
|
32
|
+
@locations ||= Resource.new(credentials, Request::LOCATIONS, options)
|
27
33
|
end
|
28
34
|
|
29
35
|
def contacts
|
30
|
-
@contacts ||= Resource.new(
|
36
|
+
@contacts ||= Resource.new(credentials, Request::CONTACTS, options)
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Mirror
|
2
|
+
module Api
|
3
|
+
class Errors
|
4
|
+
ERROR_400 = {code: 1, :message => "Mirror API returned a status code 400 for this call"}
|
5
|
+
ERROR_404 = {code: 2, :message => "Mirror API returned a status code 404 for this call"}
|
6
|
+
INTERNAL_ERROR = {:code => 100, :message => "We are experiencing problems requesting your name. Our team has been notified."}
|
7
|
+
TOKEN_REFRESH_ERROR = {:code => 200, :message => "Could not refresh your API access token"}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/mirror-api/oauth.rb
CHANGED
@@ -12,12 +12,14 @@ module Mirror
|
|
12
12
|
req = Net::HTTP::Post.new("/o/oauth2/token")
|
13
13
|
req.set_form_data(client_id: self.client_id, client_secret: self.client_secret, refresh_token: self.refresh_token, grant_type: "refresh_token")
|
14
14
|
res = Net::HTTP.start("accounts.google.com", use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) { |http| http.request(req) }
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
result["access_token"]
|
19
|
-
|
20
|
-
|
15
|
+
result = JSON.parse(res.body)
|
16
|
+
|
17
|
+
if result
|
18
|
+
if result["access_token"]
|
19
|
+
result["access_token"]
|
20
|
+
elsif result["error"]
|
21
|
+
raise "Error in get_access_token #{result["error"]}"
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
data/lib/mirror-api/request.rb
CHANGED
@@ -1,22 +1,163 @@
|
|
1
|
-
|
1
|
+
require "rest-client"
|
2
|
+
require "json"
|
3
|
+
require "logger"
|
4
|
+
require_relative "request_data"
|
5
|
+
require_relative "response_data"
|
6
|
+
require_relative "errors"
|
2
7
|
|
3
8
|
module Mirror
|
4
9
|
module Api
|
5
10
|
HOST = "https://www.googleapis.com"
|
6
11
|
|
7
|
-
class Request
|
12
|
+
class Request
|
8
13
|
TIMELINE = "timeline"
|
9
14
|
SUBSCRIPTIONS = "subscriptions"
|
10
15
|
LOCATIONS = "locations"
|
11
16
|
CONTACTS = "contacts"
|
12
17
|
|
18
|
+
VALID_RESOURCES = [TIMELINE, SUBSCRIPTIONS, LOCATIONS, CONTACTS]
|
19
|
+
|
20
|
+
attr_accessor :last_error, :logger, :host, :last_exception, :throw_on_fail, :response, :data, :creds, :resource, :params
|
21
|
+
|
22
|
+
|
13
23
|
def initialize(creds, options={})
|
14
|
-
|
24
|
+
self.resource = options[:resource]
|
25
|
+
self.params = options[:params]
|
26
|
+
self.logger = options[:logger]
|
15
27
|
@id = options[:id]
|
16
|
-
|
28
|
+
|
17
29
|
@expected_response = options[:expected_response]
|
18
|
-
|
19
|
-
|
30
|
+
@creds = creds
|
31
|
+
@last_exception = nil
|
32
|
+
@throw_on_fail = options[:raise_errors] || false
|
33
|
+
@host = options[:host] || HOST
|
34
|
+
|
35
|
+
@last_error = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def logger=(value)
|
39
|
+
if value
|
40
|
+
if value.is_a?(Logger)
|
41
|
+
@logger = value
|
42
|
+
else
|
43
|
+
raise "Invalid object given as logger #{value.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def resource=(value)
|
49
|
+
if value
|
50
|
+
if VALID_RESOURCES.include?(value)
|
51
|
+
@resource = value
|
52
|
+
else
|
53
|
+
raise "Invalid resource name #{value}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@resource = TIMELINE unless @resource
|
57
|
+
end
|
58
|
+
|
59
|
+
def params=(value)
|
60
|
+
if value
|
61
|
+
if value.is_a?(RequestData)
|
62
|
+
@params = value
|
63
|
+
elsif value.is_a?(Hash) && value.keys.count > 0
|
64
|
+
@params = RequestData.new(value)
|
65
|
+
else
|
66
|
+
raise "Parameter #{value.inspect} is not compatible"
|
67
|
+
end
|
68
|
+
else
|
69
|
+
@params = nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
public
|
74
|
+
def post
|
75
|
+
do_verb(:post)
|
76
|
+
end
|
77
|
+
|
78
|
+
def put
|
79
|
+
do_verb(:put)
|
80
|
+
end
|
81
|
+
|
82
|
+
def patch
|
83
|
+
do_verb(:patch)
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete
|
87
|
+
get_verb(:delete)
|
88
|
+
end
|
89
|
+
|
90
|
+
def get
|
91
|
+
get_verb
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def handle_http_response(response, request, result, &block)
|
97
|
+
@request = request
|
98
|
+
case response.code
|
99
|
+
when 404
|
100
|
+
when 400
|
101
|
+
if @logger
|
102
|
+
msg = "ERROR - #{response.code} #{request.inspect} to #{self.invoke_url} with params #{self.params}. Response is #{response.body}"
|
103
|
+
@logger.error(msg)
|
104
|
+
end
|
105
|
+
response
|
106
|
+
else
|
107
|
+
response.return!(request, result, &block)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def successful_response?
|
112
|
+
@response and @response.code == expected_response
|
113
|
+
end
|
114
|
+
|
115
|
+
def headers
|
116
|
+
{
|
117
|
+
"Accept" => "application/json",
|
118
|
+
"Content-type" => "application/json",
|
119
|
+
"Authorization" => "Bearer #{@creds[:token]}"
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
def handle_response
|
124
|
+
if successful_response?
|
125
|
+
ret_val
|
126
|
+
else
|
127
|
+
send_error
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def handle_error(error_desc, msg, errors, validation_error=nil, params={})
|
132
|
+
@last_error = error_desc.dup
|
133
|
+
@last_error[:errors] = errors
|
134
|
+
@last_error[:validation_error] = validation_error if validation_error
|
135
|
+
msg += " with params #{params}"
|
136
|
+
@logger.warn(msg) if @logger
|
137
|
+
raise error_desc[:message] if throw_on_fail
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_data
|
141
|
+
if @response and @response.body
|
142
|
+
@data = JSON.parse(@response.body) if @response.body.is_a?(String) && !@response.body.empty?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def do_verb(verb=:post)
|
147
|
+
data = self.params.to_json
|
148
|
+
@response = RestClient.send(verb, self.invoke_url, data, self.headers) do |response, request, result, &block|
|
149
|
+
handle_http_response(response, request, result, &block)
|
150
|
+
end
|
151
|
+
set_data
|
152
|
+
handle_response
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_verb(verb=:get)
|
156
|
+
@response = RestClient.send(verb, self.invoke_url, self.headers) do |response, request, result, &block|
|
157
|
+
handle_http_response(response, request, result, &block)
|
158
|
+
end
|
159
|
+
set_data
|
160
|
+
handle_response
|
20
161
|
end
|
21
162
|
|
22
163
|
def invoke_url
|
@@ -31,20 +172,24 @@ module Mirror
|
|
31
172
|
end
|
32
173
|
|
33
174
|
def attachments
|
34
|
-
params
|
35
|
-
end
|
36
|
-
|
37
|
-
def params
|
38
|
-
@params ||={}
|
175
|
+
params.attachments if params
|
39
176
|
end
|
40
177
|
|
41
178
|
def ret_val
|
42
|
-
|
179
|
+
ResponseData.new(@data)
|
43
180
|
end
|
44
181
|
|
45
182
|
def expected_response
|
46
183
|
@expected_response
|
47
184
|
end
|
185
|
+
|
186
|
+
def send_error
|
187
|
+
return handle_error(
|
188
|
+
Mirror::Api::Errors::ERROR_400,
|
189
|
+
"Error making a request for #{@resource}",
|
190
|
+
@data
|
191
|
+
)
|
192
|
+
end
|
48
193
|
end
|
49
194
|
end
|
50
195
|
end
|