mirror-api 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|