geotrigger 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/README.md +4 -0
- data/geotrigger.gemspec +18 -0
- data/lib/ext/string.rb +26 -0
- data/lib/geotrigger.rb +23 -0
- data/lib/geotrigger/ago/session.rb +145 -0
- data/lib/geotrigger/application.rb +27 -0
- data/lib/geotrigger/device.rb +50 -0
- data/lib/geotrigger/model.rb +84 -0
- data/lib/geotrigger/session.rb +57 -0
- data/lib/geotrigger/tag.rb +47 -0
- data/lib/geotrigger/trigger.rb +45 -0
- data/lib/geotrigger/version.rb +3 -0
- data/spec/config.yml.example +3 -0
- data/spec/geotrigger/application_spec.rb +61 -0
- data/spec/geotrigger/device_spec.rb +81 -0
- data/spec/geotrigger/geotrigger_spec.rb +108 -0
- data/spec/geotrigger/tag_spec.rb +106 -0
- data/spec/geotrigger/trigger_spec.rb +88 -0
- data/spec/helper.rb +8 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1681ff89f1fb5fa48cee8fb34b31de586e2ca57b
|
4
|
+
data.tar.gz: c4e1f585cc211d2a40e6ddadf9043ae36a831271
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a76125f1e937a2a6dffb38e98a966034adc6dab207743c1dc23b091897ec6f7d606ac1841c678a5922dcd0823c142a704ac0a2a56197d5f34b9ddc3fd29c8d2c
|
7
|
+
data.tar.gz: cdf2862f6dce564353b9f9f363729a504a75da4f1ae15c6557b1496103d44ac728f040cfe1e304da1eaa9dd1f2b1f845cace0641d3313697f470b8e3d7654f8b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/geotrigger.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/geotrigger/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Kenichi Nakamura"]
|
6
|
+
gem.email = ["kenichi.nakamura@gmail.com"]
|
7
|
+
gem.description = gem.summary = "A small ruby client for Esri's Geotrigger service"
|
8
|
+
gem.homepage = "https://github.com/esripdx/geotrigger-ruby"
|
9
|
+
gem.files = `git ls-files | grep -Ev '^(myapp|examples)'`.split("\n")
|
10
|
+
gem.test_files = `git ls-files -- spec/*`.split("\n")
|
11
|
+
gem.name = "geotrigger"
|
12
|
+
gem.require_paths = ["lib"]
|
13
|
+
gem.version = Geotrigger::VERSION
|
14
|
+
gem.license = 'apache'
|
15
|
+
|
16
|
+
gem.add_dependency 'httpclient'
|
17
|
+
gem.add_development_dependency 'timecop'
|
18
|
+
end
|
data/lib/ext/string.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
alias_method :blank?, :empty?
|
4
|
+
|
5
|
+
# these are pretty much ripped from:
|
6
|
+
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
7
|
+
#
|
8
|
+
def camelcase
|
9
|
+
string = self.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
10
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
11
|
+
string.gsub!('/', '::')
|
12
|
+
string
|
13
|
+
end
|
14
|
+
|
15
|
+
def underscore
|
16
|
+
acronym_regex = /(?=a)b/
|
17
|
+
word = self.gsub('::', '/')
|
18
|
+
word.gsub!(/(?:([A-Za-z\d])|^)(#{acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
|
19
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
20
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
21
|
+
word.tr!("-", "_")
|
22
|
+
word.downcase!
|
23
|
+
word
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/geotrigger.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'httpclient'; class HTTPClient; def inspect; to_s; end; end
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Geotrigger
|
6
|
+
class AGOError < StandardError; end
|
7
|
+
class GeotriggerError < StandardError
|
8
|
+
attr_accessor :code, :headers, :message, :params
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
lib = File.expand_path '../..', __FILE__
|
13
|
+
$:.push lib unless $:.include? lib
|
14
|
+
require 'ext/string'
|
15
|
+
|
16
|
+
require 'geotrigger/ago/session'
|
17
|
+
require 'geotrigger/session'
|
18
|
+
require 'geotrigger/model'
|
19
|
+
|
20
|
+
require 'geotrigger/application'
|
21
|
+
require 'geotrigger/device'
|
22
|
+
require 'geotrigger/tag'
|
23
|
+
require 'geotrigger/trigger'
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
module AGO
|
3
|
+
class Session
|
4
|
+
extend ::Forwardable
|
5
|
+
def_delegators :@impl, :access_token, :access_token=, :ago_data, :device_data, :refresh_token
|
6
|
+
|
7
|
+
AGO_BASE_URL = (ENV.key?('AGO_BASE_URL') ?
|
8
|
+
(ENV['AGO_BASE_URL'] + '%s') :
|
9
|
+
'https://www.arcgis.com/sharing/%s').freeze
|
10
|
+
|
11
|
+
def initialize opts = {}
|
12
|
+
@hc = HTTPClient.new
|
13
|
+
@impl = case opts[:type] || :application
|
14
|
+
when :application
|
15
|
+
Application.new self, opts
|
16
|
+
when :device
|
17
|
+
Device.new self, opts
|
18
|
+
else
|
19
|
+
raise ArgumentError 'unknown type'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def type
|
24
|
+
case @impl
|
25
|
+
when Application
|
26
|
+
:application
|
27
|
+
when Device
|
28
|
+
:device
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# http the specified method to the specified path with the given params.
|
33
|
+
# json parse the response body or raise errors.
|
34
|
+
#
|
35
|
+
def hc meth, path, params
|
36
|
+
r = @hc.__send__ meth, AGO_BASE_URL % path, params.merge(f: 'json')
|
37
|
+
raise AGOError.new r.body unless r.status == 200
|
38
|
+
h = JSON.parse r.body
|
39
|
+
raise AGOError.new r.body if h['error']
|
40
|
+
h
|
41
|
+
end
|
42
|
+
|
43
|
+
module ExpirySet
|
44
|
+
|
45
|
+
TOKEN_EXPIRY_BUFFER = 10
|
46
|
+
|
47
|
+
def wrap_token_retrieval &block
|
48
|
+
yield
|
49
|
+
expires_at = Time.now.to_i + @ago_data['expires_in']
|
50
|
+
@ago_data[:expires_at] = Time.at expires_at - TOKEN_EXPIRY_BUFFER
|
51
|
+
@ago_data
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class Application
|
57
|
+
include ExpirySet
|
58
|
+
extend ::Forwardable
|
59
|
+
def_delegator :@session, :hc
|
60
|
+
|
61
|
+
attr_reader :ago_data
|
62
|
+
|
63
|
+
def initialize session, opts = {}
|
64
|
+
@session, @client_id, @client_secret =
|
65
|
+
session, opts[:client_id], opts[:client_secret]
|
66
|
+
end
|
67
|
+
|
68
|
+
def access_token
|
69
|
+
fetch_access_token if @ago_data.nil? or
|
70
|
+
(not @ago_data[:expires_at].nil? and
|
71
|
+
Time.now >= @ago_data[:expires_at])
|
72
|
+
@ago_data['access_token']
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def fetch_access_token
|
78
|
+
wrap_token_retrieval do
|
79
|
+
@ago_data = hc :post, 'oauth2/token',
|
80
|
+
client_id: @client_id,
|
81
|
+
client_secret: @client_secret,
|
82
|
+
grant_type: 'client_credentials'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class Device
|
89
|
+
include ExpirySet
|
90
|
+
extend Forwardable
|
91
|
+
def_delegator :@session, :hc
|
92
|
+
|
93
|
+
attr_accessor :refresh_token
|
94
|
+
attr_reader :ago_data
|
95
|
+
|
96
|
+
def initialize session, opts = {}
|
97
|
+
@session, @client_id, @refresh_token =
|
98
|
+
session, opts[:client_id], opts[:refresh_token]
|
99
|
+
end
|
100
|
+
|
101
|
+
def access_token
|
102
|
+
if @ago_data.nil?
|
103
|
+
if @refresh_token.nil?
|
104
|
+
register
|
105
|
+
else
|
106
|
+
refresh_access_token
|
107
|
+
end
|
108
|
+
elsif not @ago_data[:expires_at].nil? and Time.now >= @ago_data[:expires_at]
|
109
|
+
refresh_access_token
|
110
|
+
end
|
111
|
+
@ago_data['access_token']
|
112
|
+
end
|
113
|
+
|
114
|
+
def device_data
|
115
|
+
@device_data ||= hc(:get, 'portals/self', token: access_token)['deviceInfo']
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def register
|
121
|
+
wrap_token_retrieval do
|
122
|
+
data = hc :post, 'oauth2/registerDevice', client_id: @client_id, expiration: -1
|
123
|
+
@ago_data = {
|
124
|
+
'access_token' => data['deviceToken']['access_token'],
|
125
|
+
'expires_in' => data['deviceToken']['expires_in']
|
126
|
+
}
|
127
|
+
@device_data = data['device']
|
128
|
+
@refresh_token = data['deviceToken']['refresh_token']
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def refresh_access_token
|
133
|
+
wrap_token_retrieval do
|
134
|
+
@ago_data = hc :post, 'oauth2/token',
|
135
|
+
client_id: @client_id,
|
136
|
+
refresh_token: @refresh_token,
|
137
|
+
grant_type: 'refresh_token'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
|
3
|
+
class Application < Model
|
4
|
+
|
5
|
+
def permissions
|
6
|
+
post 'application/permissions'
|
7
|
+
end
|
8
|
+
|
9
|
+
def permissions= perms
|
10
|
+
post 'application/permissions/update', perms
|
11
|
+
end
|
12
|
+
|
13
|
+
def devices params = {}
|
14
|
+
post_list 'devices', params
|
15
|
+
end
|
16
|
+
|
17
|
+
def tags params = {}
|
18
|
+
post_list 'tags', params
|
19
|
+
end
|
20
|
+
|
21
|
+
def triggers params = {}
|
22
|
+
post_list 'triggers', params
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
|
3
|
+
class Device < Model
|
4
|
+
include Taggable
|
5
|
+
|
6
|
+
def initialize opts = {}
|
7
|
+
super opts
|
8
|
+
case session.type
|
9
|
+
when :application
|
10
|
+
if opts[:device_id] and @data.nil?
|
11
|
+
grok_self_from post('device/list', deviceIds: opts[:device_id]), opts[:device_id]
|
12
|
+
end
|
13
|
+
when :device
|
14
|
+
if @data.nil?
|
15
|
+
grok_self_from post('device/list'), opts[:device_id] || :first
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_tag
|
21
|
+
'device:%s' % deviceId
|
22
|
+
end
|
23
|
+
|
24
|
+
def post_update
|
25
|
+
post_data = @data.dup
|
26
|
+
case @session.type
|
27
|
+
when :application
|
28
|
+
post_data['deviceIds'] = post_data.delete 'deviceId'
|
29
|
+
when :device
|
30
|
+
post_data.delete 'deviceId'
|
31
|
+
end
|
32
|
+
post_data.delete 'tags'
|
33
|
+
post_data.delete 'lastSeen'
|
34
|
+
|
35
|
+
grok_self_from post 'device/update', post_data
|
36
|
+
self
|
37
|
+
end
|
38
|
+
alias_method :save, :post_update
|
39
|
+
|
40
|
+
def grok_self_from data, id = nil
|
41
|
+
if id == :first
|
42
|
+
@data = data['devices'].first
|
43
|
+
else
|
44
|
+
@data = data['devices'].select {|t| t['deviceId'] == (id || @data['deviceId'])}.first
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
|
3
|
+
class Model
|
4
|
+
|
5
|
+
class StateError < StandardError; end
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
def_delegator :@session, :post
|
9
|
+
|
10
|
+
attr_accessor :data
|
11
|
+
attr_reader :session
|
12
|
+
|
13
|
+
def self.from_api data, session
|
14
|
+
i = self.new session: session
|
15
|
+
i.data = data
|
16
|
+
return i
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize opts = {}
|
20
|
+
@session = opts[:session] || Session.new(opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def post_list models, params = {}, default_params = {}
|
24
|
+
model = models.sub /s$/, ''
|
25
|
+
params = default_params.merge params
|
26
|
+
post(model + '/list', params)[models].map do |data|
|
27
|
+
Geotrigger.const_get(model.capitalize).from_api data, @session
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing meth, *args
|
32
|
+
meth_s = meth.to_s
|
33
|
+
if meth_s =~ /=$/ and args.length == 1
|
34
|
+
key = meth_s.sub(/=$/,'').camelcase
|
35
|
+
if @data and @data.key? key
|
36
|
+
@data[key] = args[0]
|
37
|
+
else
|
38
|
+
super meth, *args
|
39
|
+
end
|
40
|
+
else
|
41
|
+
key = meth_s.camelcase
|
42
|
+
if @data and @data.key? key
|
43
|
+
@data[key]
|
44
|
+
else
|
45
|
+
super meth, *args
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def == obj
|
51
|
+
if Model === obj
|
52
|
+
self.data == obj.data
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module Taggable
|
59
|
+
|
60
|
+
def tags params = {}
|
61
|
+
post_list 'tags', params, tags: @data['tags']
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_tags *names
|
65
|
+
@data['addTags'] = names.flatten
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_tags *names
|
69
|
+
names = names.flatten
|
70
|
+
raise ArgumentError.new "default tag prohibited" if names.include? default_tag
|
71
|
+
@data['removeTags'] = names
|
72
|
+
end
|
73
|
+
|
74
|
+
def tags= *names
|
75
|
+
names = names.flatten
|
76
|
+
raise ArgumentError.new "default tag required" unless names.include? default_tag
|
77
|
+
@data['setTags'] = names
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
|
3
|
+
class Session
|
4
|
+
extend Forwardable
|
5
|
+
def_delegator :@ago, :type
|
6
|
+
|
7
|
+
BASE_URL = (ENV.key?('GT_BASE_URL') ?
|
8
|
+
(ENV['GT_BASE_URL'] + '%s') :
|
9
|
+
'https://geotrigger.arcgis.com/%s').freeze
|
10
|
+
|
11
|
+
attr_writer :access_token
|
12
|
+
|
13
|
+
def initialize opts = {}
|
14
|
+
@ago = AGO::Session.new opts
|
15
|
+
@hc = HTTPClient.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def access_token
|
19
|
+
@access_token || @ago.access_token
|
20
|
+
end
|
21
|
+
|
22
|
+
def headers
|
23
|
+
{
|
24
|
+
'Content-Type' => 'application/json',
|
25
|
+
'Authorization' => "Bearer #{access_token}"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def post path, params = {}
|
30
|
+
r = @hc.post BASE_URL % path, params.to_json, headers
|
31
|
+
raise GeotriggerError.new r.body unless r.status == 200
|
32
|
+
h = JSON.parse r.body
|
33
|
+
raise_error h['error'] if h['error']
|
34
|
+
h
|
35
|
+
end
|
36
|
+
|
37
|
+
def raise_error error
|
38
|
+
ge = GeotriggerError.new error['message']
|
39
|
+
ge.code = error['code']
|
40
|
+
ge.headers = error['headers']
|
41
|
+
ge.message = error['message']
|
42
|
+
ge.params = error['params']
|
43
|
+
jj error
|
44
|
+
raise ge
|
45
|
+
end
|
46
|
+
|
47
|
+
def device?
|
48
|
+
type == :device
|
49
|
+
end
|
50
|
+
|
51
|
+
def application?
|
52
|
+
type == :application
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
|
3
|
+
class Tag < Model
|
4
|
+
|
5
|
+
def self.create session, opts
|
6
|
+
t = ::Geotrigger::Tag.new session: session
|
7
|
+
t.data = opts
|
8
|
+
t.post_create
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize opts = {}
|
12
|
+
super opts
|
13
|
+
if opts[:name] and @data.nil?
|
14
|
+
grok_self_from post('tag/list', tags: opts[:name]), opts[:name]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def triggers params = {}
|
19
|
+
post_list 'triggers', params, tags: name
|
20
|
+
end
|
21
|
+
|
22
|
+
def devices params = {}
|
23
|
+
post_list 'devices', params, tags: name
|
24
|
+
end
|
25
|
+
|
26
|
+
def post_create
|
27
|
+
post_data = @data.dup
|
28
|
+
grok_self_from post('tag/permissions', post_data), @data[:tags]
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def post_update
|
33
|
+
raise StateError.new 'device access_token prohibited' if @session.device?
|
34
|
+
post_data = @data.dup
|
35
|
+
post_data['tags'] = post_data.delete 'name'
|
36
|
+
grok_self_from post 'tag/permissions', post_data
|
37
|
+
self
|
38
|
+
end
|
39
|
+
alias_method :save, :post_update
|
40
|
+
|
41
|
+
def grok_self_from data, name = nil
|
42
|
+
@data = data['tags'].select {|t| t['name'] == (name || @data['name'])}.first
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Geotrigger
|
2
|
+
|
3
|
+
class Trigger < Model
|
4
|
+
include Taggable
|
5
|
+
|
6
|
+
def self.create session, opts
|
7
|
+
t = Trigger.new session: session
|
8
|
+
t.data = opts
|
9
|
+
t.post_create
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize opts = {}
|
13
|
+
super opts
|
14
|
+
if opts[:trigger_id] and @data.nil?
|
15
|
+
grok_self_from post('trigger/list', triggerIds: opts[:trigger_id]), opts[:trigger_id]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_tag
|
20
|
+
'trigger:%s' % triggerId
|
21
|
+
end
|
22
|
+
|
23
|
+
def post_create
|
24
|
+
post_data = @data.dup
|
25
|
+
@data = post 'trigger/create', post_data
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def post_update opts = {}
|
30
|
+
post_data = @data.dup
|
31
|
+
post_data['triggerIds'] = post_data.delete 'triggerId'
|
32
|
+
post_data.delete 'tags'
|
33
|
+
|
34
|
+
grok_self_from post 'trigger/update', post_data.merge(opts)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
alias_method :save, :post_update
|
38
|
+
|
39
|
+
def grok_self_from data, id = nil
|
40
|
+
@data = data['triggers'].select {|t| t['triggerId'] == (id || @data['triggerId'])}.first
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Geotrigger::Application do
|
4
|
+
|
5
|
+
PERMS = 'deviceList',
|
6
|
+
'deviceLocation',
|
7
|
+
'deviceTagging',
|
8
|
+
'deviceToken',
|
9
|
+
'discoverableDevice',
|
10
|
+
'discoverableApplication',
|
11
|
+
'triggerApply',
|
12
|
+
'triggerDelete',
|
13
|
+
'triggerHistory',
|
14
|
+
'triggerList',
|
15
|
+
'triggerUpdate'
|
16
|
+
|
17
|
+
let :app do
|
18
|
+
Geotrigger::Application.new client_id: CONF[:client_id],
|
19
|
+
client_secret: CONF[:client_secret]
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'fetches permissions' do
|
23
|
+
ps = app.permissions
|
24
|
+
ps.should_not be nil
|
25
|
+
ps.should be_a Hash
|
26
|
+
ps.keys.should include *PERMS
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'sets permissions' do
|
30
|
+
PERMS.each do |p|
|
31
|
+
ps = app.permissions
|
32
|
+
_ps = ps.merge p => !ps[p]
|
33
|
+
app.permissions = _ps
|
34
|
+
__ps = app.permissions
|
35
|
+
__ps.should eq ps.merge(p => !ps[p])
|
36
|
+
app.permissions = ps
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'fetches devices' do
|
41
|
+
ds = app.devices
|
42
|
+
ds.should_not be nil
|
43
|
+
ds.should be_a Array
|
44
|
+
ds.first.should be_a Geotrigger::Device unless ds.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'fetches tags' do
|
48
|
+
ts = app.tags
|
49
|
+
ts.should_not be nil
|
50
|
+
ts.should be_a Array
|
51
|
+
ts.first.should be_a Geotrigger::Tag unless ts.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'fetches triggers' do
|
55
|
+
ts = app.triggers
|
56
|
+
ts.should_not be nil
|
57
|
+
ts.should be_a Array
|
58
|
+
ts.first.should be_a Geotrigger::Trigger unless ts.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Geotrigger::Device do
|
4
|
+
|
5
|
+
let :dev do
|
6
|
+
s = GT::AGO::Session.new client_id: CONF[:client_id],
|
7
|
+
type: :device
|
8
|
+
|
9
|
+
# can't use this because GT doesn't know it yet
|
10
|
+
#
|
11
|
+
ago_did = s.device_data['deviceId']
|
12
|
+
|
13
|
+
# make a call to GT so it learns of new device
|
14
|
+
#
|
15
|
+
at = s.access_token
|
16
|
+
r = HTTPClient.new.get Geotrigger::Session::BASE_URL % 'device/list',
|
17
|
+
nil,
|
18
|
+
'Authorization' => "Bearer #{at}"
|
19
|
+
|
20
|
+
gt_did = JSON.parse(r.body)['devices'][0]['deviceId']
|
21
|
+
|
22
|
+
# assert ids are the same
|
23
|
+
#
|
24
|
+
ago_did.should eq gt_did
|
25
|
+
|
26
|
+
GT::Device.new client_id: CONF[:client_id],
|
27
|
+
client_secret: CONF[:client_secret],
|
28
|
+
device_id: ago_did
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'fetches tags' do
|
32
|
+
ts = dev.tags
|
33
|
+
ts.should_not be nil
|
34
|
+
ts.should be_a Array
|
35
|
+
ts.first.should be_a GT::Tag
|
36
|
+
ts.first.name.should eq dev.default_tag
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'knows the default tag' do
|
40
|
+
dev.default_tag =~ /^device:\S+$/
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'updates' do
|
44
|
+
dev.properties = {'foo' => 'bar'}
|
45
|
+
dev.trackingProfile = 'adaptive'
|
46
|
+
dev.save
|
47
|
+
dev.properties.should eq({'foo' => 'bar'})
|
48
|
+
dev.trackingProfile.should eq 'adaptive'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'adds tags' do
|
52
|
+
ts = dev.tags
|
53
|
+
ts.length.should eq 1
|
54
|
+
dev.add_tags 'foo', 'bar'
|
55
|
+
dev.save
|
56
|
+
ts = dev.tags
|
57
|
+
ts.length.should eq 3
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'sets tags' do
|
61
|
+
ts = dev.tags
|
62
|
+
ts.length.should eq 1
|
63
|
+
dev.tags = dev.default_tag, 'fu', 'bat'
|
64
|
+
dev.save
|
65
|
+
ts = dev.tags
|
66
|
+
ts.length.should eq 3
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'removes tags' do
|
70
|
+
dev.tags = dev.default_tag, 'fizz', 'buzz'
|
71
|
+
dev.save
|
72
|
+
sleep 3
|
73
|
+
ts = dev.tags
|
74
|
+
ts.length.should eq 3
|
75
|
+
dev.remove_tags 'fizz', 'buzz'
|
76
|
+
dev.save
|
77
|
+
ts = dev.tags
|
78
|
+
ts.length.should eq 1
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Geotrigger::AGO::Session do
|
4
|
+
|
5
|
+
def session_should_be_ok agos
|
6
|
+
agos.access_token.should_not be nil
|
7
|
+
agos.ago_data.should_not be nil
|
8
|
+
agos.ago_data.keys.should include 'access_token', 'expires_in', :expires_at
|
9
|
+
(agos.ago_data[:expires_at] > Time.now).should be true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'fetches an access token for client credentials' do
|
13
|
+
session_should_be_ok Geotrigger::AGO::Session.new client_id: CONF[:client_id],
|
14
|
+
client_secret: CONF[:client_secret]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'fetches access and refresh tokens for client id' do
|
18
|
+
session_should_be_ok Geotrigger::AGO::Session.new client_id: CONF[:client_id],
|
19
|
+
type: :device
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'fetches access token for client id and refresh token' do
|
23
|
+
session_should_be_ok Geotrigger::AGO::Session.new client_id: CONF[:client_id],
|
24
|
+
refresh_token: CONF[:refresh_token],
|
25
|
+
type: :device
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'updates an expired access token with client credentials' do
|
29
|
+
agos = Geotrigger::AGO::Session.new client_id: CONF[:client_id],
|
30
|
+
client_secret: CONF[:client_secret]
|
31
|
+
at = agos.access_token
|
32
|
+
Timecop.travel agos.ago_data[:expires_at] do
|
33
|
+
_at = agos.access_token
|
34
|
+
session_should_be_ok agos
|
35
|
+
at.should_not eq _at
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'updates an expired access token without given refresh token' do
|
40
|
+
agos = Geotrigger::AGO::Session.new client_id: CONF[:client_id],
|
41
|
+
type: :device
|
42
|
+
at = agos.access_token
|
43
|
+
Timecop.travel agos.ago_data[:expires_at] do
|
44
|
+
_at = agos.access_token
|
45
|
+
session_should_be_ok agos
|
46
|
+
at.should_not eq _at
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'updates an expired access token given refresh token' do
|
51
|
+
agos = Geotrigger::AGO::Session.new client_id: CONF[:client_id],
|
52
|
+
refresh_token: CONF[:refresh_token],
|
53
|
+
type: :device
|
54
|
+
at = agos.access_token
|
55
|
+
Timecop.travel agos.ago_data[:expires_at] do
|
56
|
+
_at = agos.access_token
|
57
|
+
session_should_be_ok agos
|
58
|
+
at.should_not eq _at
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
describe Geotrigger::Session do
|
65
|
+
|
66
|
+
it 'fetches an access token for client credentials' do
|
67
|
+
s = Geotrigger::Session.new client_id: CONF[:client_id],
|
68
|
+
client_secret: CONF[:client_secret]
|
69
|
+
s.access_token.should_not be nil
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'fetches an access token for client id' do
|
73
|
+
s = Geotrigger::Session.new client_id: CONF[:client_id],
|
74
|
+
type: :device
|
75
|
+
s.access_token.should_not be nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'uses a given access token' do
|
79
|
+
s = Geotrigger::Session.new client_id: CONF[:client_id],
|
80
|
+
client_secret: CONF[:client_secret]
|
81
|
+
s.access_token = 'foo'
|
82
|
+
s.access_token.should eq 'foo'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'uses a given refresh token' do
|
86
|
+
s = Geotrigger::Session.new client_id: CONF[:client_id],
|
87
|
+
refresh_token: CONF[:refresh_token],
|
88
|
+
type: :device
|
89
|
+
s.access_token.should_not be nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'knows what type it is' do
|
93
|
+
s = Geotrigger::Session.new client_id: CONF[:client_id],
|
94
|
+
client_secret: CONF[:client_secret]
|
95
|
+
s.application?.should be true
|
96
|
+
s.device?.should be false
|
97
|
+
s = Geotrigger::Session.new client_id: CONF[:client_id],
|
98
|
+
type: :device
|
99
|
+
s.application?.should be false
|
100
|
+
s.device?.should be true
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'raises errors correctly' do
|
104
|
+
s = Geotrigger::Session.new
|
105
|
+
->{ s.post 'application/permissions' }.should raise_error
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Geotrigger::Tag do
|
4
|
+
|
5
|
+
TAG_PERMS = 'deviceList',
|
6
|
+
'deviceLocation',
|
7
|
+
'deviceTagging',
|
8
|
+
'deviceToken',
|
9
|
+
'discoverable',
|
10
|
+
'triggerApply',
|
11
|
+
'triggerDelete',
|
12
|
+
'triggerHistory',
|
13
|
+
'triggerList',
|
14
|
+
'triggerUpdate'
|
15
|
+
|
16
|
+
let :session do
|
17
|
+
Geotrigger::Session.new client_id: CONF[:client_id],
|
18
|
+
client_secret: CONF[:client_secret]
|
19
|
+
end
|
20
|
+
|
21
|
+
let :tag do
|
22
|
+
GT::Tag.create session, tags: "foo#{Time.now.to_i}"
|
23
|
+
end
|
24
|
+
|
25
|
+
let :dev do
|
26
|
+
s = GT::AGO::Session.new client_id: CONF[:client_id],
|
27
|
+
type: :device
|
28
|
+
|
29
|
+
# can't use this because GT doesn't know it yet
|
30
|
+
#
|
31
|
+
ago_did = s.device_data['deviceId']
|
32
|
+
|
33
|
+
# make a call to GT so it learns of new device
|
34
|
+
#
|
35
|
+
at = s.access_token
|
36
|
+
r = HTTPClient.new.get Geotrigger::Session::BASE_URL % 'device/list',
|
37
|
+
nil,
|
38
|
+
'Authorization' => "Bearer #{at}"
|
39
|
+
|
40
|
+
gt_did = JSON.parse(r.body)['devices'][0]['deviceId']
|
41
|
+
|
42
|
+
# assert ids are the same
|
43
|
+
#
|
44
|
+
ago_did.should eq gt_did
|
45
|
+
|
46
|
+
GT::Device.new client_id: CONF[:client_id],
|
47
|
+
client_secret: CONF[:client_secret],
|
48
|
+
device_id: ago_did
|
49
|
+
end
|
50
|
+
|
51
|
+
let :trigger_opts do
|
52
|
+
{
|
53
|
+
'condition' => {
|
54
|
+
'direction' => 'enter',
|
55
|
+
'geo' => {
|
56
|
+
'latitude' => 45.5165,
|
57
|
+
'longitude' => -122.6764,
|
58
|
+
'distance' => 100
|
59
|
+
}
|
60
|
+
},
|
61
|
+
'action' => {
|
62
|
+
'trackingProfile' => 'adaptive'
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
let :trigger do
|
68
|
+
GT::Trigger.create session, trigger_opts
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'creates a tag' do
|
72
|
+
t = tag
|
73
|
+
t.name.should match /^foo/
|
74
|
+
TAG_PERMS.each {|p| t.__send__(p).should_not be nil}
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'fetches triggers' do
|
78
|
+
t = tag
|
79
|
+
trig = trigger
|
80
|
+
trig.add_tags t.name
|
81
|
+
trig.save
|
82
|
+
t.triggers.first.data.should eq trig.data
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'fetches devices' do
|
86
|
+
t = tag
|
87
|
+
d = dev
|
88
|
+
d.add_tags t.name
|
89
|
+
d.save
|
90
|
+
t.devices.first.device_id.should eq d.device_id
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'updates permissions' do
|
94
|
+
t = tag
|
95
|
+
t.name.should match /^foo/
|
96
|
+
t_data = t.data.dup
|
97
|
+
TAG_PERMS.each do |p|
|
98
|
+
t.data[p] = !t_data[p]
|
99
|
+
doid = t.data.object_id
|
100
|
+
t.save
|
101
|
+
t.data.object_id.should_not eq doid
|
102
|
+
t.data[p].should eq !t_data[p]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Geotrigger::Trigger do
|
4
|
+
|
5
|
+
let :session do
|
6
|
+
Geotrigger::Session.new client_id: CONF[:client_id],
|
7
|
+
client_secret: CONF[:client_secret]
|
8
|
+
end
|
9
|
+
|
10
|
+
let :opts do
|
11
|
+
{
|
12
|
+
'condition' => {
|
13
|
+
'direction' => 'enter',
|
14
|
+
'geo' => {
|
15
|
+
'latitude' => 45.5165,
|
16
|
+
'longitude' => -122.6764,
|
17
|
+
'distance' => 100
|
18
|
+
}
|
19
|
+
},
|
20
|
+
'action' => {
|
21
|
+
'trackingProfile' => 'adaptive'
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
let :trigger do
|
27
|
+
GT::Trigger.create session, opts
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should create a trigger' do
|
31
|
+
t = trigger
|
32
|
+
t.trigger_id.should_not be nil
|
33
|
+
t.condition.should eq opts['condition']
|
34
|
+
t.action.should eq opts['action']
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'fetches tags' do
|
38
|
+
ts = trigger.tags
|
39
|
+
ts.should_not be nil
|
40
|
+
ts.should be_a Array
|
41
|
+
ts.first.should be_a GT::Tag
|
42
|
+
ts.first.name.should eq trigger.default_tag
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'knows the default tag' do
|
46
|
+
trigger.default_tag =~ /^trigger:\S+$/
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'updates' do
|
50
|
+
trigger.properties = {'foo' => 'bar'}
|
51
|
+
trigger.action['trackingProfile'] = 'adaptive'
|
52
|
+
trigger.save
|
53
|
+
trigger.properties.should eq({'foo' => 'bar'})
|
54
|
+
trigger.action['trackingProfile'].should eq 'adaptive'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'adds tags' do
|
58
|
+
ts = trigger.tags
|
59
|
+
ts.length.should eq 1
|
60
|
+
trigger.add_tags 'foo', 'bar'
|
61
|
+
trigger.save
|
62
|
+
ts = trigger.tags
|
63
|
+
ts.length.should eq 3
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'sets tags' do
|
67
|
+
ts = trigger.tags
|
68
|
+
ts.length.should eq 1
|
69
|
+
trigger.tags = trigger.default_tag, 'fu', 'bat'
|
70
|
+
trigger.save
|
71
|
+
ts = trigger.tags
|
72
|
+
ts.length.should eq 3
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'removes tags' do
|
76
|
+
trigger.tags = trigger.default_tag, 'fizz', 'buzz'
|
77
|
+
trigger.save
|
78
|
+
sleep 3
|
79
|
+
ts = trigger.tags
|
80
|
+
ts.length.should eq 3
|
81
|
+
trigger.remove_tags 'fizz', 'buzz'
|
82
|
+
trigger.save
|
83
|
+
ts = trigger.tags
|
84
|
+
ts.length.should eq 1
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
end
|
data/spec/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: geotrigger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kenichi Nakamura
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-26 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: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: timecop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A small ruby client for Esri's Geotrigger service
|
42
|
+
email:
|
43
|
+
- kenichi.nakamura@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- Gemfile
|
50
|
+
- README.md
|
51
|
+
- geotrigger.gemspec
|
52
|
+
- lib/ext/string.rb
|
53
|
+
- lib/geotrigger.rb
|
54
|
+
- lib/geotrigger/ago/session.rb
|
55
|
+
- lib/geotrigger/application.rb
|
56
|
+
- lib/geotrigger/device.rb
|
57
|
+
- lib/geotrigger/model.rb
|
58
|
+
- lib/geotrigger/session.rb
|
59
|
+
- lib/geotrigger/tag.rb
|
60
|
+
- lib/geotrigger/trigger.rb
|
61
|
+
- lib/geotrigger/version.rb
|
62
|
+
- spec/config.yml.example
|
63
|
+
- spec/geotrigger/application_spec.rb
|
64
|
+
- spec/geotrigger/device_spec.rb
|
65
|
+
- spec/geotrigger/geotrigger_spec.rb
|
66
|
+
- spec/geotrigger/tag_spec.rb
|
67
|
+
- spec/geotrigger/trigger_spec.rb
|
68
|
+
- spec/helper.rb
|
69
|
+
homepage: https://github.com/esripdx/geotrigger-ruby
|
70
|
+
licenses:
|
71
|
+
- apache
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 2.0.3
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: A small ruby client for Esri's Geotrigger service
|
93
|
+
test_files:
|
94
|
+
- spec/config.yml.example
|
95
|
+
- spec/geotrigger/application_spec.rb
|
96
|
+
- spec/geotrigger/device_spec.rb
|
97
|
+
- spec/geotrigger/geotrigger_spec.rb
|
98
|
+
- spec/geotrigger/tag_spec.rb
|
99
|
+
- spec/geotrigger/trigger_spec.rb
|
100
|
+
- spec/helper.rb
|