silver_mother 0.0.1
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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +29 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +357 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/silver_mother/api.rb +64 -0
- data/lib/silver_mother/application.rb +104 -0
- data/lib/silver_mother/event.rb +78 -0
- data/lib/silver_mother/feed.rb +60 -0
- data/lib/silver_mother/node.rb +71 -0
- data/lib/silver_mother/person.rb +44 -0
- data/lib/silver_mother/subscription.rb +44 -0
- data/lib/silver_mother/user.rb +15 -0
- data/lib/silver_mother/utils.rb +23 -0
- data/lib/silver_mother/version.rb +3 -0
- data/lib/silver_mother.rb +14 -0
- data/silver_mother.gemspec +34 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea61ac5c02969e7b8258501fbe2a54ff25185993
|
4
|
+
data.tar.gz: a6331dc774f99fb5274164ffeb592f54d0b83c22
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16fc121ae5ae3ce7dfdb43639b7363d2426de9f68500e4645c13805616f170cae3c72c589b3881810007caf6a036da939ba05c3996e95b40a1ac014f52ba4972
|
7
|
+
data.tar.gz: 669369ebf8a8c52cee0e4829ec2689379ba44902a23005ce7f710f8599d157718f9503aa7fe2e340ff6f13a394aaa565da99439db4b505e637ad3602a6a0a82b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-12-14 15:40:09 -0700 using RuboCop version 0.46.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 9
|
10
|
+
Style/Documentation:
|
11
|
+
Exclude:
|
12
|
+
- 'spec/**/*'
|
13
|
+
- 'test/**/*'
|
14
|
+
- 'lib/silver_mother/api.rb'
|
15
|
+
- 'lib/silver_mother/application.rb'
|
16
|
+
- 'lib/silver_mother/event.rb'
|
17
|
+
- 'lib/silver_mother/feed.rb'
|
18
|
+
- 'lib/silver_mother/node.rb'
|
19
|
+
- 'lib/silver_mother/person.rb'
|
20
|
+
- 'lib/silver_mother/subscription.rb'
|
21
|
+
- 'lib/silver_mother/user.rb'
|
22
|
+
- 'lib/silver_mother/utils.rb'
|
23
|
+
|
24
|
+
# Offense count: 23
|
25
|
+
# Cop supports --auto-correct.
|
26
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
27
|
+
# SupportedStyles: when_needed, always
|
28
|
+
Style/FrozenStringLiteralComment:
|
29
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Mark Kreyman
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
# SilverMother API
|
2
|
+
|
3
|
+
A ruby library for communicating with the SilverMother REST API (https://sen.se/developers/).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'silver_mother'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install silver_mother
|
20
|
+
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Authorizing your app
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
app_params = { gateway_url: 'http://your_app_domain/notification/',
|
28
|
+
redirect_url: 'http://your_app_domain/oauth/',
|
29
|
+
oauth2_client_id: 'your-client-id',
|
30
|
+
oauth2_client_secret: 'your-client-secret',
|
31
|
+
scope: 'profile+feeds.read' }
|
32
|
+
|
33
|
+
# see https://sen.se/developers/documentation/ for a list of available scopes;
|
34
|
+
# it appears that you could only use two scopes at a time.
|
35
|
+
|
36
|
+
app = SilverMother::Application.new(app_params)
|
37
|
+
```
|
38
|
+
|
39
|
+
Then construct authorization url for your user
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
app.authorization_url
|
43
|
+
```
|
44
|
+
|
45
|
+
After following the URL the user would be directed to a Sen.se auth page, and then redirected to your `redirect_url`, with an auth code as `code` parameter, i.e.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
http://your_app_domain/oauth/?code=2zykyywQ5bcGAVzMbLUjW4hJSqm4rO
|
49
|
+
|
50
|
+
app.get_token('2zykyywQ5bcGAVzMbLUjW4hJSqm4rO')
|
51
|
+
```
|
52
|
+
|
53
|
+
**NOTE: You only have 60 secs to retrieve your access token with that code. If you’re getting an “invalid grant” error, it means that the given authorization code has expired already.**
|
54
|
+
|
55
|
+
The `app.token` object would now have the following attributes:
|
56
|
+
|
57
|
+
* access_token
|
58
|
+
* token_type
|
59
|
+
* expires_in
|
60
|
+
* refresh_token
|
61
|
+
* scope
|
62
|
+
* expires_on (gets calculated based on `expires_in` and the current time)
|
63
|
+
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
token = app.token.access_token
|
67
|
+
```
|
68
|
+
|
69
|
+
It appears that the access token is valid for 1 year. Use the `refresh_token` method to retrieve a new access token:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
app.refresh_token(app.token.refresh_token)
|
73
|
+
```
|
74
|
+
|
75
|
+
And now you have an updated `app.token` object
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
token = app.token.access_token
|
79
|
+
```
|
80
|
+
|
81
|
+
### Events
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
events_api = SilverMother::Event.instance
|
85
|
+
events_api.call(token)
|
86
|
+
feeds = events_api.feeds
|
87
|
+
nodes = events_api.nodes
|
88
|
+
node_uids = events_api.node_uids
|
89
|
+
feed_uids = events_api.feed_uids
|
90
|
+
|
91
|
+
params = {
|
92
|
+
uid: 'n3TQUtzAp3c67BYOUsIuMAwgWe7i0r3A',
|
93
|
+
type: 'notification',
|
94
|
+
limit: 5, # limit the number of results, default is 10
|
95
|
+
secs: 3600 # ttl for returned results, default is 300
|
96
|
+
}
|
97
|
+
```
|
98
|
+
|
99
|
+
Possible types appear to be:
|
100
|
+
|
101
|
+
* 'alert'
|
102
|
+
* 'battery'
|
103
|
+
* 'medication'
|
104
|
+
* 'motion'
|
105
|
+
* 'notification'
|
106
|
+
* 'presence'
|
107
|
+
* 'status'
|
108
|
+
* 'temperature'
|
109
|
+
* 'tile'
|
110
|
+
|
111
|
+
**NOTE:** you can only specify 'type' for node UIDs. An example of a params hash for a feed uid:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
params = {
|
115
|
+
uid: 'GS5vq3D3MvqUZmBqRQoQY9Av4KRgHPvs',
|
116
|
+
limit: 5, # limit the number of results, default is 10
|
117
|
+
secs: 3600 # ttl for returned results, default is 300
|
118
|
+
}
|
119
|
+
```
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
events = events_api.events(params)
|
123
|
+
```
|
124
|
+
|
125
|
+
If you need to work with a particular event:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
event = events[0]
|
129
|
+
```
|
130
|
+
|
131
|
+
Attributes/methods now available for the `event` object:
|
132
|
+
|
133
|
+
* data
|
134
|
+
* dateEvent
|
135
|
+
* dateServer
|
136
|
+
* expiresAt
|
137
|
+
* feedUid
|
138
|
+
* gatewayNodeUid
|
139
|
+
* geometry
|
140
|
+
* nodeUid
|
141
|
+
* payload
|
142
|
+
* profile
|
143
|
+
* signal
|
144
|
+
* type
|
145
|
+
* version
|
146
|
+
|
147
|
+
Some attributes might, in turn, be objects or arrays of objects that
|
148
|
+
you could further explore, i.e.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
events.first.data.body
|
152
|
+
events.first.geometry.coordinates
|
153
|
+
```
|
154
|
+
|
155
|
+
### Feeds
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
feeds_api = SilverMother::Feed.instance
|
159
|
+
feeds_api.call(token)
|
160
|
+
feeds = feeds_api.feeds
|
161
|
+
uids = feeds_api.uids
|
162
|
+
```
|
163
|
+
|
164
|
+
To get a feed (or feeds) for a particular uid:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
feed = feeds_api.feed(uid)
|
168
|
+
```
|
169
|
+
|
170
|
+
Attributes/methods now available for the `feed` object:
|
171
|
+
|
172
|
+
* uid
|
173
|
+
* eventsModel
|
174
|
+
* eventsUrl
|
175
|
+
* label
|
176
|
+
* node
|
177
|
+
* object
|
178
|
+
* type
|
179
|
+
* url
|
180
|
+
* used
|
181
|
+
|
182
|
+
Some attributes are objects or arrays of objects that you could further explore, i.e.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
feed.eventsModel.geometry
|
186
|
+
```
|
187
|
+
|
188
|
+
### Nodes
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
nodes_api = SilverMother::Node.instance
|
192
|
+
nodes_api.call(token)
|
193
|
+
nodes = nodes_api.nodes
|
194
|
+
uids = nodes_api.uids
|
195
|
+
```
|
196
|
+
|
197
|
+
`nodes` is an array of node objects that you could iterate over. i.e.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
node = nodes.first
|
201
|
+
```
|
202
|
+
|
203
|
+
Attributes/methods now available for the `node` object:
|
204
|
+
|
205
|
+
* uid
|
206
|
+
* label
|
207
|
+
* url
|
208
|
+
* resource
|
209
|
+
* token
|
210
|
+
* object
|
211
|
+
* geometry
|
212
|
+
* createdAt
|
213
|
+
* updatedAt
|
214
|
+
* paused
|
215
|
+
* subscribes
|
216
|
+
* publishes
|
217
|
+
|
218
|
+
Some attributes are, in turn, objects or arrays of objects that you could further explore, i.e.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
node.resource.type
|
222
|
+
node.publishes[0].url
|
223
|
+
```
|
224
|
+
|
225
|
+
There's another way to get a node, assuming you've run `nodes_api.uids`, in order to get a list of uids:
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
node = nodes_api.node(uid)
|
229
|
+
```
|
230
|
+
|
231
|
+
To get feed(s) for a uid:
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
feed = nodes_api.feed(uid)
|
235
|
+
```
|
236
|
+
|
237
|
+
### Persons
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
persons_api = SilverMother::Person.instance
|
241
|
+
persons_api.call(token)
|
242
|
+
persons = persons_api.persons
|
243
|
+
person = persons.first
|
244
|
+
```
|
245
|
+
|
246
|
+
Attributes/methods now available for the `person` object:
|
247
|
+
|
248
|
+
* uid
|
249
|
+
* avatarUrl
|
250
|
+
* email
|
251
|
+
* firstName
|
252
|
+
* lastName
|
253
|
+
* gender
|
254
|
+
* object
|
255
|
+
* phoneNumber
|
256
|
+
|
257
|
+
### Subscriptions
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
subscriptions_api = SilverMother::Subscription.instance
|
261
|
+
subscriptions_api.call(token)
|
262
|
+
subscriptions = subscriptions_api.subscriptions
|
263
|
+
subscription = subscriptions.first
|
264
|
+
```
|
265
|
+
|
266
|
+
Attributes/methods now available for the `subscription` object:
|
267
|
+
|
268
|
+
* uid
|
269
|
+
* createdAt
|
270
|
+
* updatedAt
|
271
|
+
* gatewayUrl
|
272
|
+
* geometry
|
273
|
+
* label
|
274
|
+
* object
|
275
|
+
* paused
|
276
|
+
* publishes
|
277
|
+
* resource
|
278
|
+
* subscribes
|
279
|
+
* url
|
280
|
+
|
281
|
+
Some attributes might, in turn, be objects or arrays of objects that you could explore further, i.e.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
subscription.subscribes[0].type
|
285
|
+
```
|
286
|
+
|
287
|
+
### User
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
users_api = SilverMother::User.instance
|
291
|
+
users_api.call(token)
|
292
|
+
user = users_api.user
|
293
|
+
```
|
294
|
+
|
295
|
+
Attributes/methods now available for the `user` object:
|
296
|
+
|
297
|
+
* uid
|
298
|
+
* email
|
299
|
+
* language
|
300
|
+
* object
|
301
|
+
* timezone
|
302
|
+
* country
|
303
|
+
* username
|
304
|
+
* subscriptions
|
305
|
+
* applications
|
306
|
+
* createdAt
|
307
|
+
* updatedAt
|
308
|
+
* is_developer
|
309
|
+
* firstName
|
310
|
+
* lastName
|
311
|
+
* devices
|
312
|
+
* persons
|
313
|
+
|
314
|
+
Some attributes might be objects or arrays of objects that you could explore further, i.e.
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
user.subscriptions[0].uid
|
318
|
+
user.applications.first.label
|
319
|
+
user.devices.last.url
|
320
|
+
user.persons[1].firstName
|
321
|
+
```
|
322
|
+
|
323
|
+
### Making API requests not covered by the current version of the gem
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
silver_mother_api = SilverMother::Api.instance
|
327
|
+
|
328
|
+
silver_mother_api.get(token, path, params = {})
|
329
|
+
silver_mother_api.post(token, path, params = {})
|
330
|
+
silver_mother_api.put(token, path, params = {})
|
331
|
+
silver_mother_api.delete(token, path, params = {})
|
332
|
+
```
|
333
|
+
|
334
|
+
## Development
|
335
|
+
|
336
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
337
|
+
|
338
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
339
|
+
|
340
|
+
## Contributing
|
341
|
+
|
342
|
+
* Fork the project.
|
343
|
+
* Run `bundle`
|
344
|
+
* Run `bundle exec rake`
|
345
|
+
* Create your feature branch (`git checkout -b my-new-feature`)
|
346
|
+
* Make your feature addition or bug fix.
|
347
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
348
|
+
* Run `bundle exec rake` again.
|
349
|
+
* Run `rubocop .`
|
350
|
+
* Commit your changes. (`git commit -am 'Add some feature'`). Please do not mess with rakefile, version, or history.
|
351
|
+
* Push to the branch. (`git push origin my-new-feature`)
|
352
|
+
* Create a new Pull Request.
|
353
|
+
|
354
|
+
## License
|
355
|
+
|
356
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
357
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'silver_mother'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require 'pry'
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module SilverMother
|
5
|
+
class Api
|
6
|
+
include Singleton
|
7
|
+
include HTTParty
|
8
|
+
|
9
|
+
# Uncomment for debugging
|
10
|
+
# debug_output
|
11
|
+
|
12
|
+
def get(path, token, params = {})
|
13
|
+
result = HTTParty.get(SENSE_API_URL + path,
|
14
|
+
add_auth_header(params, token))
|
15
|
+
|
16
|
+
log_request(path, params, result)
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(path, token, params = {})
|
21
|
+
result = HTTParty.post(SENSE_API_URL + path,
|
22
|
+
add_auth_header(params, token))
|
23
|
+
|
24
|
+
log_request(path, params, result)
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def put(path, token, params = {})
|
29
|
+
result = HTTParty.put(SENSE_API_URL + path,
|
30
|
+
add_auth_header(params, token))
|
31
|
+
|
32
|
+
log_request(path, params, result)
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete(path, token, params = {})
|
37
|
+
result = HTTParty.delete(SENSE_API_URL + path,
|
38
|
+
add_auth_header(params, token))
|
39
|
+
|
40
|
+
log_request(path, params, result)
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def add_auth_header(params, token = false)
|
47
|
+
params[:headers] ||= {}
|
48
|
+
params[:headers]['Authorization'] = "Bearer #{token}" if token
|
49
|
+
params
|
50
|
+
end
|
51
|
+
|
52
|
+
def log_request(path, params, result)
|
53
|
+
Utils.instance.log_request(path, params, result)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
HTTParty::Response.class_eval do
|
59
|
+
def to_ostruct
|
60
|
+
JSON.parse(to_json, object_class: OpenStruct)
|
61
|
+
rescue JSON::ParserError
|
62
|
+
JSON.parse(self, object_class: OpenStruct)
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module SilverMother
|
2
|
+
class Application
|
3
|
+
SENSE_URL = 'https://sen.se/api/v2/'.freeze
|
4
|
+
RESPONSE_TYPE = 'code'.freeze
|
5
|
+
GRANT_TYPES = { access: 'authorization_code',
|
6
|
+
refresh: 'refresh_token' }.freeze
|
7
|
+
|
8
|
+
DEFAULTS_PATHS = { authorization_path: 'oauth2/authorize/',
|
9
|
+
token_path: 'oauth2/token/',
|
10
|
+
refresh_path: 'oauth2/refresh/' }.freeze
|
11
|
+
|
12
|
+
attr_accessor :gateway_url,
|
13
|
+
:redirect_url,
|
14
|
+
:oauth2_client_id,
|
15
|
+
:oauth2_client_secret,
|
16
|
+
:scope,
|
17
|
+
:token
|
18
|
+
|
19
|
+
def initialize(params = {})
|
20
|
+
@gateway_url = params.fetch(:gateway_url)
|
21
|
+
@redirect_url = params.fetch(:redirect_url)
|
22
|
+
@oauth2_client_id = params.fetch(:oauth2_client_id)
|
23
|
+
@oauth2_client_secret = params.fetch(:oauth2_client_secret)
|
24
|
+
@scope = params.fetch(:scope)
|
25
|
+
end
|
26
|
+
|
27
|
+
def authorization_url
|
28
|
+
url_params = { client_id: @oauth2_client_id,
|
29
|
+
scope: @scope,
|
30
|
+
redirect_uri: html_encode(@redirect_url),
|
31
|
+
response_type: RESPONSE_TYPE }
|
32
|
+
.map { |k, v| "#{k}=#{v}" }.join('&')
|
33
|
+
|
34
|
+
SENSE_URL + DEFAULTS_PATHS[:authorization_path] + '?' + url_params
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_token(auth_code)
|
38
|
+
@token = Api.instance
|
39
|
+
.post(DEFAULTS_PATHS[:token_path],
|
40
|
+
nil,
|
41
|
+
url_encoded_params(auth_code, :access))
|
42
|
+
.to_ostruct
|
43
|
+
|
44
|
+
@token.expires_on = ttl(@token.expires_in)
|
45
|
+
@token
|
46
|
+
end
|
47
|
+
|
48
|
+
def refresh_token(refresh_token)
|
49
|
+
@token = Api.instance
|
50
|
+
.post(DEFAULTS_PATHS[:refresh_path],
|
51
|
+
nil,
|
52
|
+
url_encoded_params(refresh_token, :refresh))
|
53
|
+
.to_ostruct
|
54
|
+
|
55
|
+
@token.expires_on = ttl(@token.expires_in)
|
56
|
+
@token
|
57
|
+
end
|
58
|
+
|
59
|
+
def expired?
|
60
|
+
Time.now.to_i > @token.expires_on.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def url_encoded_params(code, grant_type)
|
66
|
+
case grant_type
|
67
|
+
when :access
|
68
|
+
params = params_for_access(code)
|
69
|
+
when :refresh
|
70
|
+
params = params_for_refresh(code)
|
71
|
+
else
|
72
|
+
raise 'Unsupported grant type'
|
73
|
+
end
|
74
|
+
|
75
|
+
{ body: url_encode(params),
|
76
|
+
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' } }
|
77
|
+
end
|
78
|
+
|
79
|
+
def ttl(secs)
|
80
|
+
Time.now.to_i + secs.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
def html_encode(url)
|
84
|
+
CGI.escape(url)
|
85
|
+
end
|
86
|
+
|
87
|
+
def url_encode(params)
|
88
|
+
params.map { |k, v| "#{k}=#{v}" }.join('&')
|
89
|
+
end
|
90
|
+
|
91
|
+
def params_for_access(code)
|
92
|
+
{ client_id: @oauth2_client_id,
|
93
|
+
client_secret: @oauth2_client_secret,
|
94
|
+
code: code,
|
95
|
+
redirect_uri: html_encode(@redirect_url),
|
96
|
+
grant_type: GRANT_TYPES[:access] }
|
97
|
+
end
|
98
|
+
|
99
|
+
def params_for_refresh(code)
|
100
|
+
{ refresh_token: code,
|
101
|
+
grant_type: GRANT_TYPES[:refresh] }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module SilverMother
|
4
|
+
class Event
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
attr_accessor :token
|
8
|
+
attr_reader :feeds,
|
9
|
+
:feed_uids,
|
10
|
+
:nodes,
|
11
|
+
:node_uids,
|
12
|
+
:event_cache,
|
13
|
+
:ttls
|
14
|
+
|
15
|
+
def call(token)
|
16
|
+
@token = token
|
17
|
+
@feeds_api = SilverMother::Feed.instance
|
18
|
+
@feeds_api.call(@token)
|
19
|
+
@nodes_api = SilverMother::Node.instance
|
20
|
+
@nodes_api.call(@token)
|
21
|
+
end
|
22
|
+
|
23
|
+
def feeds
|
24
|
+
@feeds ||= @feeds_api.feeds
|
25
|
+
end
|
26
|
+
|
27
|
+
def feed_uids
|
28
|
+
@feed_uids ||= @feeds_api.uids
|
29
|
+
end
|
30
|
+
|
31
|
+
def nodes
|
32
|
+
@nodes ||= @nodes_api.nodes
|
33
|
+
end
|
34
|
+
|
35
|
+
def node_uids
|
36
|
+
@node_uids ||= @nodes_api.uids
|
37
|
+
end
|
38
|
+
|
39
|
+
def events(uid, type = nil)
|
40
|
+
@event_cache ||= {}
|
41
|
+
@ttls ||= {}
|
42
|
+
clear_cache(uid) if expired?(@ttls[uid])
|
43
|
+
@ttls[uid] ||= ttl(TTL)
|
44
|
+
@event_cache[uid] ||= Api.instance
|
45
|
+
.get(path(uid, type), @token)
|
46
|
+
.to_ostruct
|
47
|
+
.objects
|
48
|
+
end
|
49
|
+
|
50
|
+
def clear_cache!
|
51
|
+
@event_cache = {}
|
52
|
+
@ttls = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def path(uid, type = nil)
|
58
|
+
if type
|
59
|
+
"nodes/#{uid}/feeds/#{type}/events/?limit=#{NUM_OF_RESULTS}"
|
60
|
+
else
|
61
|
+
"feeds/#{uid}/events/?limit=#{NUM_OF_RESULTS}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def ttl(secs)
|
66
|
+
Time.now.to_i + secs.to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def expired?(ttl)
|
70
|
+
Time.now.to_i > ttl.to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
def clear_cache(uid)
|
74
|
+
@event_cache.delete(uid)
|
75
|
+
@ttls.delete(uid)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module SilverMother
|
4
|
+
class Feed
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
FEEDS_PATH = 'feeds/'.freeze
|
8
|
+
|
9
|
+
attr_reader :feeds_raw, :feeds, :uids, :feed_cache
|
10
|
+
|
11
|
+
def call(token)
|
12
|
+
@token = token
|
13
|
+
@feeds_raw = []
|
14
|
+
@response = Api.instance.get(FEEDS_PATH, @token)
|
15
|
+
@feeds_raw << @response
|
16
|
+
while next_page
|
17
|
+
new_path = FEEDS_PATH + next_page_number
|
18
|
+
@response = Api.instance.get(new_path, @token)
|
19
|
+
@feeds_raw << @response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def feeds
|
24
|
+
@feeds ||= @feeds_raw.each_with_object([]) do |feed, array|
|
25
|
+
array << feed.to_ostruct.objects
|
26
|
+
end.flatten
|
27
|
+
end
|
28
|
+
|
29
|
+
def uids
|
30
|
+
@uids ||= @feeds_raw.each_with_object([]) do |feed, array|
|
31
|
+
feed.to_ostruct.objects.each do |object|
|
32
|
+
array << { uid: object.uid, type: object.type }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def feed(uid)
|
38
|
+
feed_path = FEEDS_PATH + uid + '/'
|
39
|
+
@feed_cache ||= {}
|
40
|
+
@feed_cache[uid] ||= Api.instance.get(feed_path, @token).to_ostruct
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear_cache!
|
44
|
+
@feeds_raw = []
|
45
|
+
@feeds = []
|
46
|
+
@uids = []
|
47
|
+
@feed_cache = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def next_page
|
53
|
+
@response.parsed_response['links']['next'] if @response
|
54
|
+
end
|
55
|
+
|
56
|
+
def next_page_number
|
57
|
+
@response.parsed_response['links']['next'].split('/').last if next_page
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module SilverMother
|
4
|
+
class Node
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
NODES_PATH = 'nodes/'.freeze
|
8
|
+
|
9
|
+
attr_reader :nodes_raw, :nodes, :uids, :node_cache, :uid_cache
|
10
|
+
|
11
|
+
def call(token)
|
12
|
+
@token = token
|
13
|
+
@nodes_raw = []
|
14
|
+
@response = Api.instance.get(NODES_PATH, @token)
|
15
|
+
@nodes_raw << @response
|
16
|
+
while next_page
|
17
|
+
new_path = NODES_PATH + next_page_number
|
18
|
+
@response = Api.instance.get(new_path, @token)
|
19
|
+
@nodes_raw << @response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def nodes
|
24
|
+
@nodes ||= @nodes_raw.each_with_object([]) do |node, array|
|
25
|
+
array << node.to_ostruct.objects
|
26
|
+
end.flatten
|
27
|
+
end
|
28
|
+
|
29
|
+
def uids
|
30
|
+
@uids ||= @nodes_raw.each_with_object([]) do |node, array|
|
31
|
+
node.to_ostruct.objects.each do |object|
|
32
|
+
array << { uid: object.uid,
|
33
|
+
object: object.object,
|
34
|
+
label: object.label,
|
35
|
+
type: object.type,
|
36
|
+
slug: object.slug }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def node(uid)
|
42
|
+
uid_path = NODES_PATH + uid + '/'
|
43
|
+
@node_cache ||= {}
|
44
|
+
@node_cache[uid] ||= Api.instance.get(uid_path, @token).to_ostruct
|
45
|
+
end
|
46
|
+
|
47
|
+
def feed(uid)
|
48
|
+
feed_path = NODES_PATH + uid + '/feeds/'
|
49
|
+
@feed_cache ||= {}
|
50
|
+
@feed_cache[uid] ||= Api.instance.get(feed_path, @token).to_ostruct
|
51
|
+
end
|
52
|
+
|
53
|
+
def clear_cache!
|
54
|
+
@nodes_raw = []
|
55
|
+
@nodes = []
|
56
|
+
@uids = []
|
57
|
+
@node_cache = {}
|
58
|
+
@feed_cache = {}
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def next_page
|
64
|
+
@response.parsed_response['links']['next'] if @response
|
65
|
+
end
|
66
|
+
|
67
|
+
def next_page_number
|
68
|
+
@response.parsed_response['links']['next'].split('/').last if next_page
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module SilverMother
|
4
|
+
class Person
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
PERSONS_PATH = 'persons/'.freeze
|
8
|
+
|
9
|
+
attr_reader :persons_raw, :persons
|
10
|
+
|
11
|
+
def call(token)
|
12
|
+
@token = token
|
13
|
+
@persons_raw = []
|
14
|
+
@response = Api.instance.get(PERSONS_PATH, @token)
|
15
|
+
@persons_raw << @response
|
16
|
+
while next_page
|
17
|
+
new_path = PERSONS_PATH + next_page_number
|
18
|
+
@response = Api.instance.get(new_path, @token)
|
19
|
+
@persons_raw << @response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def persons
|
24
|
+
@persons ||= @persons_raw.each_with_object([]) do |person, array|
|
25
|
+
array << person.to_ostruct.objects
|
26
|
+
end.flatten
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear_cache!
|
30
|
+
@persons_raw = []
|
31
|
+
@persons = []
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def next_page
|
37
|
+
@response.parsed_response['links']['next'] if @response
|
38
|
+
end
|
39
|
+
|
40
|
+
def next_page_number
|
41
|
+
@response.parsed_response['links']['next'].split('/').last if next_page
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module SilverMother
|
4
|
+
class Subscription
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
SUBS_PATH = 'subscriptions/'.freeze
|
8
|
+
|
9
|
+
attr_reader :subscriptions_raw, :subscriptions
|
10
|
+
|
11
|
+
def call(token)
|
12
|
+
@token = token
|
13
|
+
@subscriptions_raw = []
|
14
|
+
@response = Api.instance.get(SUBS_PATH, @token)
|
15
|
+
@subscriptions_raw << @response
|
16
|
+
while next_page
|
17
|
+
new_path = SUBS_PATH + next_page_number
|
18
|
+
@response = Api.instance.get(new_path, @token)
|
19
|
+
@subscriptions_raw << @response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def subscriptions
|
24
|
+
@subscriptions ||= @subscriptions_raw.each_with_object([]) do |sub, arr|
|
25
|
+
arr << sub.to_ostruct.objects
|
26
|
+
end.flatten
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear_cache!
|
30
|
+
@subscriptions_raw = []
|
31
|
+
@subscriptions = []
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def next_page
|
37
|
+
@response.parsed_response['links']['next'] if @response
|
38
|
+
end
|
39
|
+
|
40
|
+
def next_page_number
|
41
|
+
@response.parsed_response['links']['next'].split('/').last if next_page
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module SilverMother
|
5
|
+
class Utils
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def logger
|
9
|
+
@logger ||= (defined?(Rails) ? Rails.logger : ::Logger.new(STDOUT))
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_request(path, params, result)
|
13
|
+
log_message = <<-LOG_MESSAGE
|
14
|
+
\n
|
15
|
+
SENSE PATH: #{SENSE_API_URL}#{path}
|
16
|
+
SENSE PARAMS: #{params.inspect}
|
17
|
+
SENSE RESPONSE: #{result.success? && result.parsed_response.inspect}
|
18
|
+
LOG_MESSAGE
|
19
|
+
|
20
|
+
logger.debug(log_message) unless ENV['RACK_ENV'] == 'test'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'silver_mother/version'
|
2
|
+
require 'silver_mother/utils'
|
3
|
+
require 'silver_mother/api'
|
4
|
+
require 'silver_mother/user'
|
5
|
+
require 'silver_mother/node'
|
6
|
+
require 'silver_mother/feed'
|
7
|
+
require 'silver_mother/subscription'
|
8
|
+
require 'silver_mother/person'
|
9
|
+
require 'silver_mother/event'
|
10
|
+
require 'silver_mother/application'
|
11
|
+
|
12
|
+
SENSE_API_URL = 'https://apis.sen.se/v2/'.freeze
|
13
|
+
NUM_OF_RESULTS = 10
|
14
|
+
TTL = 300
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'silver_mother/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'silver_mother'
|
8
|
+
spec.version = SilverMother::VERSION
|
9
|
+
spec.authors = ['Mark Kreyman']
|
10
|
+
spec.email = ['mark@kreyman.com']
|
11
|
+
|
12
|
+
spec.summary = 'A library for communicating with the SilverMother API.'
|
13
|
+
spec.description = 'A ruby library for communicating with the SilverMother \
|
14
|
+
REST API. Register your application at \
|
15
|
+
https://sen.se/developers/'
|
16
|
+
|
17
|
+
spec.homepage = 'https://github.com/mkreyman/silver_mother'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`
|
21
|
+
.split("\x0")
|
22
|
+
.reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
|
24
|
+
spec.bindir = 'exe'
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ['lib']
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.13.6'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'webmock'
|
32
|
+
|
33
|
+
spec.add_dependency 'httparty', '~> 0.14.0'
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: silver_mother
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark Kreyman
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.13.6
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.13.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.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.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: httparty
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.14.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.14.0
|
83
|
+
description: |-
|
84
|
+
A ruby library for communicating with the SilverMother \
|
85
|
+
REST API. Register your application at \
|
86
|
+
https://sen.se/developers/
|
87
|
+
email:
|
88
|
+
- mark@kreyman.com
|
89
|
+
executables: []
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files: []
|
92
|
+
files:
|
93
|
+
- ".gitignore"
|
94
|
+
- ".rspec"
|
95
|
+
- ".rubocop.yml"
|
96
|
+
- ".ruby-version"
|
97
|
+
- ".travis.yml"
|
98
|
+
- Gemfile
|
99
|
+
- LICENSE.txt
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- bin/console
|
103
|
+
- bin/setup
|
104
|
+
- lib/silver_mother.rb
|
105
|
+
- lib/silver_mother/api.rb
|
106
|
+
- lib/silver_mother/application.rb
|
107
|
+
- lib/silver_mother/event.rb
|
108
|
+
- lib/silver_mother/feed.rb
|
109
|
+
- lib/silver_mother/node.rb
|
110
|
+
- lib/silver_mother/person.rb
|
111
|
+
- lib/silver_mother/subscription.rb
|
112
|
+
- lib/silver_mother/user.rb
|
113
|
+
- lib/silver_mother/utils.rb
|
114
|
+
- lib/silver_mother/version.rb
|
115
|
+
- silver_mother.gemspec
|
116
|
+
homepage: https://github.com/mkreyman/silver_mother
|
117
|
+
licenses:
|
118
|
+
- MIT
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.5.1
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: A library for communicating with the SilverMother API.
|
140
|
+
test_files: []
|