iot_services 0.1.3
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 +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +104 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +126 -0
- data/Guardfile +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +129 -0
- data/Rakefile +6 -0
- data/bin/console +29 -0
- data/bin/setup +8 -0
- data/iot_services.gemspec +30 -0
- data/lib/iot_services.rb +10 -0
- data/lib/iot_services/adapters/http.rb +29 -0
- data/lib/iot_services/adapters/json_response.rb +18 -0
- data/lib/iot_services/errors.rb +5 -0
- data/lib/iot_services/evolens/client.rb +184 -0
- data/lib/iot_services/evolens/errors.rb +29 -0
- data/lib/iot_services/version.rb +3 -0
- metadata +176 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 186a0027265b63444d4be2b5c93c5930c4d4acf149342768dc86c1997fc3c3f1
|
|
4
|
+
data.tar.gz: 58c454c92902aa8dc64d2dbac02eeb470d8132482286c75e9b5f54137ab8d932
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a4b85e3587c5841ca6256bb097917041ca400ed175d0568f3ecd30d9e65799a133925ed9e65322a16fc2e1b172ee82559ae02f3bac61155e14d222f584182d34
|
|
7
|
+
data.tar.gz: df2def8a661e87d4966aa63125b2dc6ba1078d88a78a94f8f72274c60e82d9bb8facaf156d8545ebc589fa5171957f212acc1ccf4107a9de0fdc99adaa006e3a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Style/Documentation:
|
|
2
|
+
Enabled: false
|
|
3
|
+
|
|
4
|
+
Style/MethodDefParentheses:
|
|
5
|
+
Enabled: false
|
|
6
|
+
|
|
7
|
+
Style/ParallelAssignment:
|
|
8
|
+
Enabled: false
|
|
9
|
+
|
|
10
|
+
Style/FrozenStringLiteralComment:
|
|
11
|
+
Enabled: false
|
|
12
|
+
|
|
13
|
+
Style/FormatStringToken:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
16
|
+
Style/EmptyMethod:
|
|
17
|
+
Enabled: false
|
|
18
|
+
|
|
19
|
+
Style/GuardClause:
|
|
20
|
+
Enabled: false
|
|
21
|
+
|
|
22
|
+
Style/CommentAnnotation:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Style/AsciiComments:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Style/StabbyLambdaParentheses:
|
|
29
|
+
EnforcedStyle: require_no_parentheses
|
|
30
|
+
|
|
31
|
+
Style/MultilineIfModifier:
|
|
32
|
+
Enabled: false
|
|
33
|
+
|
|
34
|
+
Style/SafeNavigation:
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
Style/FormatString:
|
|
38
|
+
Enabled: false
|
|
39
|
+
|
|
40
|
+
Style/AndOr:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Style/Next:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/NumericPredicate:
|
|
47
|
+
Enabled: false
|
|
48
|
+
|
|
49
|
+
Style/SymbolArray:
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
Style/MutableConstant:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
Style/RescueModifier:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
Layout/LineLength:
|
|
59
|
+
Max: 120
|
|
60
|
+
|
|
61
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
62
|
+
Enabled: false
|
|
63
|
+
|
|
64
|
+
Layout/MultilineMethodCallIndentation:
|
|
65
|
+
Enabled: false
|
|
66
|
+
|
|
67
|
+
Layout/TrailingEmptyLines:
|
|
68
|
+
Enabled: false
|
|
69
|
+
|
|
70
|
+
Layout/FirstArrayElementIndentation:
|
|
71
|
+
EnforcedStyle: consistent
|
|
72
|
+
|
|
73
|
+
Layout/ParameterAlignment:
|
|
74
|
+
EnforcedStyle: with_fixed_indentation
|
|
75
|
+
|
|
76
|
+
Layout/SpaceInLambdaLiteral:
|
|
77
|
+
Enabled: false
|
|
78
|
+
|
|
79
|
+
Layout/EmptyLineAfterGuardClause:
|
|
80
|
+
Enabled: false
|
|
81
|
+
|
|
82
|
+
Metrics:
|
|
83
|
+
Enabled: false
|
|
84
|
+
|
|
85
|
+
Bundler:
|
|
86
|
+
Enabled: false
|
|
87
|
+
|
|
88
|
+
Lint/AssignmentInCondition:
|
|
89
|
+
Enabled: false
|
|
90
|
+
|
|
91
|
+
Lint/ShadowingOuterLocalVariable:
|
|
92
|
+
Enabled: false
|
|
93
|
+
|
|
94
|
+
Lint/AmbiguousRegexpLiteral:
|
|
95
|
+
Enabled: false
|
|
96
|
+
|
|
97
|
+
Lint/AmbiguousOperator:
|
|
98
|
+
Enabled: false
|
|
99
|
+
|
|
100
|
+
Lint/AmbiguousBlockAssociation:
|
|
101
|
+
Enabled: false
|
|
102
|
+
|
|
103
|
+
Naming/AccessorMethodName:
|
|
104
|
+
Enabled: false
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.5.0
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
iot_services (0.1.3)
|
|
5
|
+
activesupport (>= 4.0)
|
|
6
|
+
http (~> 4.3.0)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
activesupport (6.0.3.1)
|
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
13
|
+
i18n (>= 0.7, < 2)
|
|
14
|
+
minitest (~> 5.1)
|
|
15
|
+
tzinfo (~> 1.1)
|
|
16
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
|
17
|
+
addressable (2.7.0)
|
|
18
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
19
|
+
ast (2.4.0)
|
|
20
|
+
coderay (1.1.2)
|
|
21
|
+
concurrent-ruby (1.1.6)
|
|
22
|
+
diff-lcs (1.3)
|
|
23
|
+
domain_name (0.5.20190701)
|
|
24
|
+
unf (>= 0.0.5, < 1.0.0)
|
|
25
|
+
ffi (1.12.2)
|
|
26
|
+
ffi-compiler (1.0.1)
|
|
27
|
+
ffi (>= 1.0.0)
|
|
28
|
+
rake
|
|
29
|
+
formatador (0.2.5)
|
|
30
|
+
guard (2.16.1)
|
|
31
|
+
formatador (>= 0.2.4)
|
|
32
|
+
listen (>= 2.7, < 4.0)
|
|
33
|
+
lumberjack (>= 1.0.12, < 2.0)
|
|
34
|
+
nenv (~> 0.1)
|
|
35
|
+
notiffany (~> 0.0)
|
|
36
|
+
pry (>= 0.9.12)
|
|
37
|
+
shellany (~> 0.0)
|
|
38
|
+
thor (>= 0.18.1)
|
|
39
|
+
guard-compat (1.2.1)
|
|
40
|
+
guard-rspec (4.7.3)
|
|
41
|
+
guard (~> 2.1)
|
|
42
|
+
guard-compat (~> 1.1)
|
|
43
|
+
rspec (>= 2.99.0, < 4.0)
|
|
44
|
+
http (4.3.0)
|
|
45
|
+
addressable (~> 2.3)
|
|
46
|
+
http-cookie (~> 1.0)
|
|
47
|
+
http-form_data (~> 2.2)
|
|
48
|
+
http-parser (~> 1.2.0)
|
|
49
|
+
http-cookie (1.0.3)
|
|
50
|
+
domain_name (~> 0.5)
|
|
51
|
+
http-form_data (2.3.0)
|
|
52
|
+
http-parser (1.2.1)
|
|
53
|
+
ffi-compiler (>= 1.0, < 2.0)
|
|
54
|
+
i18n (1.8.5)
|
|
55
|
+
concurrent-ruby (~> 1.0)
|
|
56
|
+
jaro_winkler (1.5.4)
|
|
57
|
+
listen (3.2.1)
|
|
58
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
59
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
|
60
|
+
lumberjack (1.2.4)
|
|
61
|
+
method_source (0.9.2)
|
|
62
|
+
minitest (5.14.2)
|
|
63
|
+
nenv (0.3.0)
|
|
64
|
+
notiffany (0.1.3)
|
|
65
|
+
nenv (~> 0.1)
|
|
66
|
+
shellany (~> 0.0)
|
|
67
|
+
parallel (1.19.1)
|
|
68
|
+
parser (2.7.0.4)
|
|
69
|
+
ast (~> 2.4.0)
|
|
70
|
+
pry (0.12.2)
|
|
71
|
+
coderay (~> 1.1.0)
|
|
72
|
+
method_source (~> 0.9.0)
|
|
73
|
+
public_suffix (4.0.6)
|
|
74
|
+
rainbow (3.0.0)
|
|
75
|
+
rake (12.3.3)
|
|
76
|
+
rb-fsevent (0.10.3)
|
|
77
|
+
rb-inotify (0.10.1)
|
|
78
|
+
ffi (~> 1.0)
|
|
79
|
+
rexml (3.2.4)
|
|
80
|
+
rspec (3.9.0)
|
|
81
|
+
rspec-core (~> 3.9.0)
|
|
82
|
+
rspec-expectations (~> 3.9.0)
|
|
83
|
+
rspec-mocks (~> 3.9.0)
|
|
84
|
+
rspec-core (3.9.1)
|
|
85
|
+
rspec-support (~> 3.9.1)
|
|
86
|
+
rspec-expectations (3.9.1)
|
|
87
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
88
|
+
rspec-support (~> 3.9.0)
|
|
89
|
+
rspec-mocks (3.9.1)
|
|
90
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
91
|
+
rspec-support (~> 3.9.0)
|
|
92
|
+
rspec-support (3.9.2)
|
|
93
|
+
rubocop (0.80.1)
|
|
94
|
+
jaro_winkler (~> 1.5.1)
|
|
95
|
+
parallel (~> 1.10)
|
|
96
|
+
parser (>= 2.7.0.1)
|
|
97
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
98
|
+
rexml
|
|
99
|
+
ruby-progressbar (~> 1.7)
|
|
100
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
|
101
|
+
ruby-progressbar (1.10.1)
|
|
102
|
+
shellany (0.0.1)
|
|
103
|
+
thor (1.0.1)
|
|
104
|
+
thread_safe (0.3.6)
|
|
105
|
+
tzinfo (1.2.7)
|
|
106
|
+
thread_safe (~> 0.1)
|
|
107
|
+
unf (0.1.4)
|
|
108
|
+
unf_ext
|
|
109
|
+
unf_ext (0.0.7.7)
|
|
110
|
+
unicode-display_width (1.6.1)
|
|
111
|
+
zeitwerk (2.3.0)
|
|
112
|
+
|
|
113
|
+
PLATFORMS
|
|
114
|
+
ruby
|
|
115
|
+
|
|
116
|
+
DEPENDENCIES
|
|
117
|
+
guard
|
|
118
|
+
guard-rspec
|
|
119
|
+
iot_services!
|
|
120
|
+
pry
|
|
121
|
+
rake (~> 12.0)
|
|
122
|
+
rspec (~> 3.0)
|
|
123
|
+
rubocop (~> 0.80.0)
|
|
124
|
+
|
|
125
|
+
BUNDLED WITH
|
|
126
|
+
2.1.4
|
data/Guardfile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
|
5
|
+
# directories %w(app lib config test spec features) \
|
|
6
|
+
# .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
|
7
|
+
|
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
|
11
|
+
#
|
|
12
|
+
# $ mkdir config
|
|
13
|
+
# $ mv Guardfile config/
|
|
14
|
+
# $ ln -s config/Guardfile .
|
|
15
|
+
#
|
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
|
17
|
+
|
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
|
20
|
+
# * bundler: 'bundle exec rspec'
|
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
|
23
|
+
# installed the spring binstubs per the docs)
|
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
|
25
|
+
# * 'just' rspec: 'rspec'
|
|
26
|
+
|
|
27
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
|
28
|
+
require "guard/rspec/dsl"
|
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
|
30
|
+
|
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
|
32
|
+
|
|
33
|
+
# RSpec files
|
|
34
|
+
rspec = dsl.rspec
|
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
|
37
|
+
watch(rspec.spec_files)
|
|
38
|
+
|
|
39
|
+
# Ruby files
|
|
40
|
+
ruby = dsl.ruby
|
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
|
42
|
+
end
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Emmanuel Nicolau
|
|
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,129 @@
|
|
|
1
|
+
# IOT Services
|
|
2
|
+
|
|
3
|
+
A Ruby library that provides access to the [IOT EVOlens](https://www.iot.es/tools/) API.
|
|
4
|
+
|
|
5
|
+
[](https://travis-ci.org/eeng/iot_services)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
You know, the usual:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'iot_services'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## ECP Usage
|
|
16
|
+
|
|
17
|
+
First, you must instantiate the API client:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
evolens = IOTServices::Evolens::Client.new(
|
|
21
|
+
base_url: ENV['EVOLENS_BASE_URL'],
|
|
22
|
+
client_id: ENV['EVOLENS_ECP_CLIENT_ID'],
|
|
23
|
+
client_secret: ENV['EVOLENS_ECP_CLIENT_SECRET'],
|
|
24
|
+
# http_opts: {logger: Logger.new(STDOUT)}
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then, you can create a new wearer:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
response = evolens.new_wearer(
|
|
32
|
+
userEmail: ENV['EVOLENS_ECP_EMAIL'],
|
|
33
|
+
wearerId: 'wearer@acme.com',
|
|
34
|
+
wearerCode: 'wearer@acme.com',
|
|
35
|
+
wearerEmail: 'wearer@acme.com',
|
|
36
|
+
gender: 'MALE',
|
|
37
|
+
birthYear: '1982',
|
|
38
|
+
locationCode: '28007',
|
|
39
|
+
termsAndConditions: 'Y',
|
|
40
|
+
rightEyePrescription: {sph: '+0.00', cyl: '-0.00', axis: '00º', add: '+1.00'},
|
|
41
|
+
leftEyePrescription: {sph: '+0.00', cyl: '-0.00', axis: '00º', add: '+1.00'},
|
|
42
|
+
labCode: 'OPTICALAB',
|
|
43
|
+
languageCode: 'es'
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
And also send its lifestyle questionary:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
response = evolens.new_questionary(
|
|
51
|
+
orderId: response[:orderId],
|
|
52
|
+
activities: [
|
|
53
|
+
{name: 'DRIVE', frequency: 'DURATION_A_LITTLE', isTopOne: false},
|
|
54
|
+
{name: 'READ', frequency: 'DURATION_ZERO', isTopOne: false},
|
|
55
|
+
{name: 'COMPUTER', frequency: 'DURATION_A_LOT', isTopOne: false},
|
|
56
|
+
{name: 'OFFICE', frequency: 'DURATION_A_LOT', isTopOne: false},
|
|
57
|
+
{name: 'RETAIL', frequency: 'DURATION_A_LOT', isTopOne: true}
|
|
58
|
+
]
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Finally, in the response you may find the EVOlens ID:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
response['orderCode'] # => "9772005ZRN87"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Listing Providers and Designs
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
evolens.get_providers_and_designs
|
|
72
|
+
# => [{'ProviderName' => 'P1', 'Designs' => ['D1', 'D2', ...]}, ...]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Authentication and Authorization
|
|
76
|
+
|
|
77
|
+
These steps are done automatically the first time a request to the API is executed, and then the authorization token is cached. So it's recommended that you reuse the client instance on every request.
|
|
78
|
+
However, if you want to explicitly do this, you can call the `login` method.
|
|
79
|
+
In addition, when the authorization token expires, a new one it automatically requested on the next API call.
|
|
80
|
+
|
|
81
|
+
## ECP Registration
|
|
82
|
+
|
|
83
|
+
The API is a bit convoluted on this subject. It requires another set of secrets for an admin role, an additional login request, and finally, the ECP registration is done in two steps: First, an ECP "placeholder" must be created with the admin role, and then its credentials are associated with an ECP role.
|
|
84
|
+
|
|
85
|
+
Consequently, you'll need two client instances to make this happen:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
adm = IOTServices::Evolens::Client.new(
|
|
89
|
+
base_url: ENV['EVOLENS_BASE_URL'],
|
|
90
|
+
client_id: ENV['EVOLENS_ADMIN_CLIENT_ID'],
|
|
91
|
+
client_secret: ENV['EVOLENS_ADMIN_CLIENT_SECRET'],
|
|
92
|
+
admin_credentials: {
|
|
93
|
+
email: ENV['EVOLENS_ADMIN_EMAIL'],
|
|
94
|
+
password: ENV['EVOLENS_ADMIN_PASSWORD']
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
ecp = IOTServices::Evolens::Client.new(
|
|
99
|
+
base_url: ENV['EVOLENS_BASE_URL'],
|
|
100
|
+
client_id: ENV['EVOLENS_ECP_CLIENT_ID'],
|
|
101
|
+
client_secret: ENV['EVOLENS_ECP_CLIENT_SECRET']
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Finally, you must call these two methods to complete the process:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
adm.create_ecp(
|
|
109
|
+
store_id: 'THE_STORE_ID',
|
|
110
|
+
ecp_code: 'THE_ECP_CODE'
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
ecp.register_ecp(
|
|
114
|
+
store_code: 'THE_STORE_CODE',
|
|
115
|
+
ecp_code: 'THE_ECP_CODE',
|
|
116
|
+
email: 'SOME_EMAIL',
|
|
117
|
+
password: 'SOME_PASSWORD'
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
If they don't throw any errors it means everything went well.
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
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.
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
Bug reports and pull requests are welcome!
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'iot_services'
|
|
5
|
+
|
|
6
|
+
def new_clients
|
|
7
|
+
adm = IOTServices::Evolens::Client.new(
|
|
8
|
+
base_url: ENV['EVOLENS_BASE_URL'],
|
|
9
|
+
client_id: ENV['EVOLENS_ADMIN_CLIENT_ID'],
|
|
10
|
+
client_secret: ENV['EVOLENS_ADMIN_CLIENT_SECRET'],
|
|
11
|
+
admin_credentials: {
|
|
12
|
+
email: ENV['EVOLENS_ADMIN_EMAIL'],
|
|
13
|
+
password: ENV['EVOLENS_ADMIN_PASSWORD']
|
|
14
|
+
},
|
|
15
|
+
http_opts: {logger: Logger.new(STDOUT)}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
ecp = IOTServices::Evolens::Client.new(
|
|
19
|
+
base_url: ENV['EVOLENS_BASE_URL'],
|
|
20
|
+
client_id: ENV['EVOLENS_ECP_CLIENT_ID'],
|
|
21
|
+
client_secret: ENV['EVOLENS_ECP_CLIENT_SECRET'],
|
|
22
|
+
http_opts: {logger: Logger.new(STDOUT)}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
[adm, ecp]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
require 'pry'
|
|
29
|
+
Pry.start
|
data/bin/setup
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative 'lib/iot_services/version'
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = 'iot_services'
|
|
5
|
+
spec.version = IOTServices::VERSION
|
|
6
|
+
spec.authors = ['Emmanuel Nicolau']
|
|
7
|
+
spec.email = ['emmanicolau@gmail.com']
|
|
8
|
+
|
|
9
|
+
spec.summary = 'IOT Services'
|
|
10
|
+
spec.description = 'IOT Services'
|
|
11
|
+
spec.homepage = 'https://github.com/eeng/iot_services'
|
|
12
|
+
|
|
13
|
+
# Specify which files should be added to the gem when it is released.
|
|
14
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
15
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
16
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
+
end
|
|
18
|
+
spec.bindir = 'exe'
|
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
20
|
+
spec.require_paths = ['lib']
|
|
21
|
+
|
|
22
|
+
spec.add_dependency 'activesupport', '>= 4.0'
|
|
23
|
+
spec.add_dependency 'http', '~> 4.3.0'
|
|
24
|
+
spec.add_development_dependency 'guard'
|
|
25
|
+
spec.add_development_dependency 'guard-rspec'
|
|
26
|
+
spec.add_development_dependency 'pry'
|
|
27
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 0.80.0'
|
|
30
|
+
end
|
data/lib/iot_services.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
|
3
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
|
4
|
+
|
|
5
|
+
require 'iot_services/version'
|
|
6
|
+
require 'iot_services/errors'
|
|
7
|
+
require 'iot_services/adapters/json_response'
|
|
8
|
+
require 'iot_services/adapters/http'
|
|
9
|
+
require 'iot_services/evolens/errors'
|
|
10
|
+
require 'iot_services/evolens/client'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'http'
|
|
2
|
+
|
|
3
|
+
module IOTServices
|
|
4
|
+
module Adapters
|
|
5
|
+
class HTTP
|
|
6
|
+
def initialize base_url: '', logger: nil
|
|
7
|
+
@base_url = base_url
|
|
8
|
+
@logger = logger
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
%i[get post put].each do |method|
|
|
12
|
+
define_method method do |url, args = {}|
|
|
13
|
+
request url, method, args
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def request url, method, headers: {}, json: nil
|
|
20
|
+
http = ::HTTP.headers(headers)
|
|
21
|
+
http = http.use(logging: {logger: @logger}) if @logger
|
|
22
|
+
response = http.send(method, "#{@base_url}#{url}", json: json)
|
|
23
|
+
JsonResponse.new(response.status, response.body)
|
|
24
|
+
rescue ::HTTP::Error => e
|
|
25
|
+
raise NetworkError, e
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module IOTServices
|
|
4
|
+
module Adapters
|
|
5
|
+
class JsonResponse
|
|
6
|
+
attr_reader :status, :body
|
|
7
|
+
|
|
8
|
+
def initialize status, body
|
|
9
|
+
@status = status
|
|
10
|
+
@body = JSON.parse(body)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ok?
|
|
14
|
+
@status == 200
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
module IOTServices
|
|
2
|
+
module Evolens
|
|
3
|
+
class Client
|
|
4
|
+
attr_reader :token, :token_expires
|
|
5
|
+
|
|
6
|
+
def initialize(
|
|
7
|
+
base_url:,
|
|
8
|
+
client_id:,
|
|
9
|
+
client_secret:,
|
|
10
|
+
admin_credentials: nil, # Only needed for admin role endpoints
|
|
11
|
+
http_opts: {},
|
|
12
|
+
http: Adapters::HTTP.new(http_opts.merge(base_url: base_url)),
|
|
13
|
+
token: nil,
|
|
14
|
+
token_expires: nil
|
|
15
|
+
)
|
|
16
|
+
@base_url = base_url
|
|
17
|
+
@client_id = client_id
|
|
18
|
+
@client_secret = client_secret
|
|
19
|
+
@admin_credentials = admin_credentials
|
|
20
|
+
@http = http
|
|
21
|
+
@token = token
|
|
22
|
+
@token_expires = token_expires
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def authenticate
|
|
26
|
+
response = @http.get('/oauth/authorize', headers: {'X-Client-Id' => @client_id})
|
|
27
|
+
on_ok_response response do
|
|
28
|
+
@token = response.body['data']['authorization']
|
|
29
|
+
end
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def authorize
|
|
34
|
+
headers = {'X-Client-Id' => @client_id, 'X-Client-Secret' => @client_secret}
|
|
35
|
+
response = @http.get("/oauth/authorize/#{@token}", headers: headers)
|
|
36
|
+
on_ok_response response do
|
|
37
|
+
# I think it's safe to override the token obtained during the authentication step with this new one,
|
|
38
|
+
# as it's no longer needed on following requests.
|
|
39
|
+
@token = response.body['data']['token']
|
|
40
|
+
@token_expires = Time.parse(response.body['data']['expire'])
|
|
41
|
+
end
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# To access admin role operations, like creating an ECP, the API requires an additional login step.
|
|
46
|
+
# Should not be needed to use this explicitly as it's called from the main login method.
|
|
47
|
+
def admin_panel_login
|
|
48
|
+
headers = {'Authorization' => @token, 'Content-Type' => 'application/json'}
|
|
49
|
+
response = @http.post('/ga/user/login', headers: headers, json: @admin_credentials)
|
|
50
|
+
on_ok_response response do
|
|
51
|
+
# I think it's safe to override the token as the admin panel token appears to have a shorter span.
|
|
52
|
+
# This way with can reuse the on_private_endpoint functionality, otherwise we would have to replicate
|
|
53
|
+
# the whole relogin behaviour for this different tokens.
|
|
54
|
+
@token = response.body['data'].first['token']
|
|
55
|
+
@token_expires = Time.parse(response.body['data'].first['tokenExpire'])
|
|
56
|
+
end
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Executes the authentication and authorization procedure only if we haven't obtain the tokens yet,
|
|
61
|
+
# or if they've expired. Idempotent operation.
|
|
62
|
+
def login
|
|
63
|
+
relogin if authorization_expired?
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Executes the authentication and authorization procedure to obtain the tokens needed for the subsequent requests.
|
|
68
|
+
def relogin
|
|
69
|
+
reset_auth_data
|
|
70
|
+
authenticate
|
|
71
|
+
authorize
|
|
72
|
+
admin_panel_login if @admin_credentials
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# To create a visual profile, we first need to create the patient.
|
|
76
|
+
# Make sure you've provided ECP role credentials in the constructor.
|
|
77
|
+
def new_wearer data
|
|
78
|
+
on_private_endpoint do |headers|
|
|
79
|
+
@http.post('/ecp/store/wearer/new', headers: headers, json: data)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Uploads a patients visual profile.
|
|
84
|
+
# Requires ECP role credentials.
|
|
85
|
+
def new_questionary data
|
|
86
|
+
on_private_endpoint do |headers|
|
|
87
|
+
@http.post('/ecp/store/wearer/new/questionary', headers: headers, json: data)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns an [Array] of providers and its designs.
|
|
92
|
+
# Requires ECP role credentials.
|
|
93
|
+
def get_providers_and_designs
|
|
94
|
+
on_private_endpoint do |headers|
|
|
95
|
+
@http.get('/ecp/user/GetProvidersAndDesign', headers: headers)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Creates an ECP "placeholder" that later should be associated with its email.
|
|
100
|
+
# Requires Admin role credentials.
|
|
101
|
+
def create_ecp store_id:, ecp_code:
|
|
102
|
+
json = {'EcpCode' => ecp_code}
|
|
103
|
+
on_private_endpoint do |headers|
|
|
104
|
+
@http.post("/ga/ecps/#{store_id}", headers: headers, json: json)
|
|
105
|
+
end.first
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Links an ECP "placeholder" with its email and password.
|
|
109
|
+
# Requires ECP role credentials.
|
|
110
|
+
def register_ecp store_code:, ecp_code:, email:, password:
|
|
111
|
+
json = {
|
|
112
|
+
storeCode: store_code,
|
|
113
|
+
ecpUserCode: ecp_code,
|
|
114
|
+
email: email,
|
|
115
|
+
password: password
|
|
116
|
+
}
|
|
117
|
+
on_private_endpoint do |headers|
|
|
118
|
+
@http.post('/ecp/user/register', headers: headers, json: json)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Unlinks an ECP email, i.e. leaves it open to register another email
|
|
123
|
+
# Requires Admin role credentials.
|
|
124
|
+
def deregister_ecp ecp_id:, ecp_code:
|
|
125
|
+
on_private_endpoint do |headers|
|
|
126
|
+
@http.put("/ga/ecps/unlink?id=#{ecp_id}&EcpCode=#{ecp_code}", headers: headers)
|
|
127
|
+
end.first
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Returns the list of created ECPs.
|
|
131
|
+
# Requires Admin role credentials.
|
|
132
|
+
def get_ecps
|
|
133
|
+
on_private_endpoint do |headers|
|
|
134
|
+
@http.get('/ga/ecps', headers: headers)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def on_ok_response response
|
|
141
|
+
case response.status
|
|
142
|
+
when 200
|
|
143
|
+
yield
|
|
144
|
+
when 401
|
|
145
|
+
raise UnauthorizedError, response
|
|
146
|
+
else
|
|
147
|
+
raise ServerError, response
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def retry_auth_if_expired
|
|
152
|
+
retries ||= 0
|
|
153
|
+
login
|
|
154
|
+
yield
|
|
155
|
+
rescue UnauthorizedError
|
|
156
|
+
if (retries += 1) < 2
|
|
157
|
+
relogin
|
|
158
|
+
retry
|
|
159
|
+
else
|
|
160
|
+
raise
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def authorization_expired?
|
|
165
|
+
@token.nil? || @token_expires.nil?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def reset_auth_data
|
|
169
|
+
@token = nil
|
|
170
|
+
@token_expires = nil
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def on_private_endpoint
|
|
174
|
+
retry_auth_if_expired do
|
|
175
|
+
headers = {'Authorization' => @token, 'Content-Type' => 'application/json'}
|
|
176
|
+
response = yield headers
|
|
177
|
+
on_ok_response response do
|
|
178
|
+
response.body['data']
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module IOTServices
|
|
2
|
+
module Evolens
|
|
3
|
+
class ServerError < Error
|
|
4
|
+
attr_reader :code, :status
|
|
5
|
+
|
|
6
|
+
def initialize response
|
|
7
|
+
super response_message(response)
|
|
8
|
+
@code = response_code(response)
|
|
9
|
+
@status = response.status
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def response_message response
|
|
13
|
+
if response.body['error']
|
|
14
|
+
response.body['error']['message']
|
|
15
|
+
elsif response.body['Message']
|
|
16
|
+
[response.body['Message'], response.body['MessageDetail']].join("\n")
|
|
17
|
+
else
|
|
18
|
+
'Error response not recognized'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def response_code response
|
|
23
|
+
response.body['error']['code'] if response.body['error']
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class UnauthorizedError < ServerError; end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: iot_services
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.3
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Emmanuel Nicolau
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-11-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: http
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 4.3.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 4.3.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: guard
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: guard-rspec
|
|
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: pry
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rake
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '12.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '12.0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rspec
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '3.0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3.0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rubocop
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: 0.80.0
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 0.80.0
|
|
125
|
+
description: IOT Services
|
|
126
|
+
email:
|
|
127
|
+
- emmanicolau@gmail.com
|
|
128
|
+
executables: []
|
|
129
|
+
extensions: []
|
|
130
|
+
extra_rdoc_files: []
|
|
131
|
+
files:
|
|
132
|
+
- ".gitignore"
|
|
133
|
+
- ".rspec"
|
|
134
|
+
- ".rubocop.yml"
|
|
135
|
+
- ".ruby-version"
|
|
136
|
+
- ".travis.yml"
|
|
137
|
+
- Gemfile
|
|
138
|
+
- Gemfile.lock
|
|
139
|
+
- Guardfile
|
|
140
|
+
- LICENSE.txt
|
|
141
|
+
- README.md
|
|
142
|
+
- Rakefile
|
|
143
|
+
- bin/console
|
|
144
|
+
- bin/setup
|
|
145
|
+
- iot_services.gemspec
|
|
146
|
+
- lib/iot_services.rb
|
|
147
|
+
- lib/iot_services/adapters/http.rb
|
|
148
|
+
- lib/iot_services/adapters/json_response.rb
|
|
149
|
+
- lib/iot_services/errors.rb
|
|
150
|
+
- lib/iot_services/evolens/client.rb
|
|
151
|
+
- lib/iot_services/evolens/errors.rb
|
|
152
|
+
- lib/iot_services/version.rb
|
|
153
|
+
homepage: https://github.com/eeng/iot_services
|
|
154
|
+
licenses: []
|
|
155
|
+
metadata: {}
|
|
156
|
+
post_install_message:
|
|
157
|
+
rdoc_options: []
|
|
158
|
+
require_paths:
|
|
159
|
+
- lib
|
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
161
|
+
requirements:
|
|
162
|
+
- - ">="
|
|
163
|
+
- !ruby/object:Gem::Version
|
|
164
|
+
version: '0'
|
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
|
+
requirements:
|
|
167
|
+
- - ">="
|
|
168
|
+
- !ruby/object:Gem::Version
|
|
169
|
+
version: '0'
|
|
170
|
+
requirements: []
|
|
171
|
+
rubyforge_project:
|
|
172
|
+
rubygems_version: 2.7.3
|
|
173
|
+
signing_key:
|
|
174
|
+
specification_version: 4
|
|
175
|
+
summary: IOT Services
|
|
176
|
+
test_files: []
|