alula-ruby 0.50.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +14 -0
- data/.env.example +8 -0
- data/.github/workflows/gem-push.yml +45 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Dockerfile +6 -0
- data/Gemfile +12 -0
- data/Guardfile +42 -0
- data/README.md +423 -0
- data/Rakefile +6 -0
- data/VERSION.md +84 -0
- data/alula-docker-compose.yml +80 -0
- data/alula.gemspec +38 -0
- data/bin/console +15 -0
- data/bin/docparse +36 -0
- data/bin/genresource +79 -0
- data/bin/setup +8 -0
- data/bin/testauth +24 -0
- data/bin/testprep +9 -0
- data/data/docs/Alula_API_Documentation_2021-04-06.html +16240 -0
- data/docker-compose.yml +11 -0
- data/lib/alula/alula_response.rb +20 -0
- data/lib/alula/api_operations/delete.rb +52 -0
- data/lib/alula/api_operations/list.rb +45 -0
- data/lib/alula/api_operations/request.rb +44 -0
- data/lib/alula/api_operations/save.rb +81 -0
- data/lib/alula/api_resource.rb +196 -0
- data/lib/alula/client.rb +142 -0
- data/lib/alula/errors.rb +169 -0
- data/lib/alula/filter_builder.rb +271 -0
- data/lib/alula/helpers/device_attribute_translations.rb +68 -0
- data/lib/alula/list_object.rb +64 -0
- data/lib/alula/meta.rb +16 -0
- data/lib/alula/monkey_patches.rb +24 -0
- data/lib/alula/oauth.rb +118 -0
- data/lib/alula/pagination.rb +25 -0
- data/lib/alula/procedures/dealer_device_stats_proc.rb +16 -0
- data/lib/alula/procedures/dealer_restore_proc.rb +25 -0
- data/lib/alula/procedures/dealer_suspend_proc.rb +25 -0
- data/lib/alula/procedures/device_assign_proc.rb +23 -0
- data/lib/alula/procedures/device_cellular_history_proc.rb +33 -0
- data/lib/alula/procedures/device_rateplan_get_proc.rb +21 -0
- data/lib/alula/procedures/device_register_proc.rb +31 -0
- data/lib/alula/procedures/device_signal_add_proc.rb +42 -0
- data/lib/alula/procedures/device_signal_delivered_proc.rb +31 -0
- data/lib/alula/procedures/device_signal_update_proc.rb +32 -0
- data/lib/alula/procedures/device_unassign_proc.rb +16 -0
- data/lib/alula/procedures/device_unregister_proc.rb +21 -0
- data/lib/alula/procedures/upload_touchpad_branding_proc.rb +24 -0
- data/lib/alula/procedures/user_plansvideo_price_get.rb +21 -0
- data/lib/alula/procedures/user_transfer_accept.rb +19 -0
- data/lib/alula/procedures/user_transfer_authorize.rb +18 -0
- data/lib/alula/procedures/user_transfer_cancel.rb +18 -0
- data/lib/alula/procedures/user_transfer_deny.rb +19 -0
- data/lib/alula/procedures/user_transfer_reject.rb +19 -0
- data/lib/alula/procedures/user_transfer_request.rb +19 -0
- data/lib/alula/query_interface.rb +142 -0
- data/lib/alula/rate_limit.rb +11 -0
- data/lib/alula/relationship_attributes.rb +107 -0
- data/lib/alula/resource_attributes.rb +206 -0
- data/lib/alula/resources/admin_user.rb +207 -0
- data/lib/alula/resources/billing_program.rb +41 -0
- data/lib/alula/resources/dealer.rb +218 -0
- data/lib/alula/resources/dealer_account_transfer.rb +172 -0
- data/lib/alula/resources/dealer_address.rb +89 -0
- data/lib/alula/resources/dealer_branding.rb +226 -0
- data/lib/alula/resources/dealer_program.rb +75 -0
- data/lib/alula/resources/dealer_suspension_log.rb +49 -0
- data/lib/alula/resources/device.rb +716 -0
- data/lib/alula/resources/device_cellular_status.rb +134 -0
- data/lib/alula/resources/device_charge.rb +70 -0
- data/lib/alula/resources/device_event_log.rb +167 -0
- data/lib/alula/resources/device_program.rb +54 -0
- data/lib/alula/resources/event_trigger.rb +75 -0
- data/lib/alula/resources/event_webhook.rb +47 -0
- data/lib/alula/resources/feature_bysubject.rb +74 -0
- data/lib/alula/resources/feature_plan.rb +57 -0
- data/lib/alula/resources/feature_planvideo.rb +54 -0
- data/lib/alula/resources/feature_price.rb +46 -0
- data/lib/alula/resources/receiver_connection.rb +95 -0
- data/lib/alula/resources/receiver_group.rb +74 -0
- data/lib/alula/resources/revision.rb +91 -0
- data/lib/alula/resources/self.rb +61 -0
- data/lib/alula/resources/station.rb +130 -0
- data/lib/alula/resources/token_exchange.rb +34 -0
- data/lib/alula/resources/user.rb +229 -0
- data/lib/alula/resources/user_address.rb +121 -0
- data/lib/alula/resources/user_phone.rb +116 -0
- data/lib/alula/resources/user_preferences.rb +57 -0
- data/lib/alula/resources/user_pushtoken.rb +75 -0
- data/lib/alula/resources/user_videoprofile.rb +38 -0
- data/lib/alula/rest_resource.rb +17 -0
- data/lib/alula/rpc_resource.rb +40 -0
- data/lib/alula/rpc_response.rb +14 -0
- data/lib/alula/singleton_rest_resource.rb +26 -0
- data/lib/alula/util.rb +107 -0
- data/lib/alula/version.rb +5 -0
- data/lib/alula.rb +135 -0
- data/lib/parser.rb +199 -0
- metadata +282 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 12adde5899a82d4987e9babe90aaf6db9798e092461fa9332d58d6f57abf8a29
|
4
|
+
data.tar.gz: d149bfe4009b83514fc9935e886f0c08c22749df42b4e3dfb9e8c24df95b293e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4d309a84193c63371e8ac6740a015c4ce0b60d802c3b25ca921ec2d212ee769c180cea34f895e211052ab326221392393a09dd2b1fee46deff876aca020d00c9
|
7
|
+
data.tar.gz: 657c5fcbfcebfe04d7d1e6ecd96913b854b9031a5ea8b28309bfe672e13663a6193f073e8204cbd3bafc29f0b338b85aa2b201995aa6b4a7c77d67d11ca4a954
|
@@ -0,0 +1,14 @@
|
|
1
|
+
version: 2
|
2
|
+
jobs:
|
3
|
+
build:
|
4
|
+
machine: true
|
5
|
+
steps:
|
6
|
+
- checkout
|
7
|
+
## Launch ipdapi stack
|
8
|
+
- run: docker-compose -f alula-docker-compose.yml up -d
|
9
|
+
## Prepare ruby container to execute tests
|
10
|
+
- run: docker-compose build
|
11
|
+
- run: docker-compose run --name alula-ruby-build alula-ruby bin/setup
|
12
|
+
- run: docker commit alula-ruby-build alula-ruby
|
13
|
+
## Execute tests
|
14
|
+
- run: docker-compose run alula-ruby bundle exec rspec
|
data/.env.example
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
name: Build + Publish
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
permissions:
|
14
|
+
contents: read
|
15
|
+
packages: write
|
16
|
+
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- name: Set up Ruby 2.6
|
20
|
+
uses: actions/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: 2.6.x
|
23
|
+
|
24
|
+
- name: Publish to GPR
|
25
|
+
run: |
|
26
|
+
mkdir -p $HOME/.gem
|
27
|
+
touch $HOME/.gem/credentials
|
28
|
+
chmod 0600 $HOME/.gem/credentials
|
29
|
+
printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
30
|
+
gem build *.gemspec
|
31
|
+
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
32
|
+
env:
|
33
|
+
GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
|
34
|
+
OWNER: ${{ github.repository_owner }}
|
35
|
+
|
36
|
+
- name: Publish to RubyGems
|
37
|
+
run: |
|
38
|
+
mkdir -p $HOME/.gem
|
39
|
+
touch $HOME/.gem/credentials
|
40
|
+
chmod 0600 $HOME/.gem/credentials
|
41
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
42
|
+
gem build *.gemspec
|
43
|
+
gem push *.gem
|
44
|
+
env:
|
45
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/_yardoc/
|
4
|
+
/coverage/
|
5
|
+
/doc/
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/tmp/
|
9
|
+
/data/generated/
|
10
|
+
/data/resource_fixtures/
|
11
|
+
|
12
|
+
# rspec failure tracking
|
13
|
+
.rspec_status
|
14
|
+
.byebug_history
|
15
|
+
.env
|
16
|
+
|
17
|
+
.DS_Store
|
18
|
+
|
19
|
+
*.sublime-project
|
20
|
+
*.sublime-workspace
|
21
|
+
.idea
|
22
|
+
spec/testDataResult.json
|
23
|
+
/Gemfile.lock
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Dockerfile
ADDED
data/Gemfile
ADDED
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/README.md
ADDED
@@ -0,0 +1,423 @@
|
|
1
|
+
# Alula-Ruby
|
2
|
+
|
3
|
+
This is the official Alula ruby API client.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
This gem is private, and can be installed via bundler using a Git fetch strategy:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'alula-ruby', git: "git@github.com:alula-net/alula-ruby.git", tag: 'v0.2.0'
|
11
|
+
```
|
12
|
+
|
13
|
+
If you use `Sidekiq` and plan on using the Alula-Ruby gem in your Sidekiq workers, you should also bundle the `RequestStore-Sidekiq` extension. This extension is used to store client authorization info in `Thread.current` for multithreading support.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'request_store-sidekiq', '~> 0.1'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
## Authorization
|
24
|
+
|
25
|
+
Alula-Ruby requires an OAuth access token to use. You can obtain a token like so:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
# Authorize the OAuth client
|
29
|
+
Alula::Oauth.configure(client_id: "your client id", client_secret: "your client secret", api_url: "the API URL")
|
30
|
+
|
31
|
+
# Obtain OAuth tokens with username & password
|
32
|
+
creds = Alula::Oauth.authenticate(username: "a username", password: "a password")
|
33
|
+
|
34
|
+
#> creds.token_type = bearer
|
35
|
+
#> creds.access_token = some string
|
36
|
+
#> creds.refresh_token = some string
|
37
|
+
#> creds.expires_in = integer
|
38
|
+
#> creds.scope = string
|
39
|
+
|
40
|
+
```
|
41
|
+
|
42
|
+
## Configuring
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Alula::Client.api_key = your API key
|
46
|
+
Alula::Client.api_url = The environment URL you will be accessing
|
47
|
+
Alula::Client.user_agent = A short name for your script/application
|
48
|
+
```
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
Once you have obtained an access token, configure the client to use that token. You should perform this initialization at the start of every request, and at the start of any thread that performs work. The Alula-Ruby client uses the `RequestStore` gem to stash its configuration data in `Thread.current`, so keep this limitation in mind.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
Alula::Client.api_key = "your access token"
|
56
|
+
```
|
57
|
+
|
58
|
+
If you need to save any records, you will also need to tell the `Alula::Client` about your users' authorized role. Depending on the role certain fields may be writeable or protected. You can set your role one of 3 ways:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
myself = Alula::Self.retrieve
|
62
|
+
|
63
|
+
# The user role is inferred from the Alula::Self object
|
64
|
+
Alula::Client.role = myself
|
65
|
+
|
66
|
+
# Explicitly set the role via a role symbol
|
67
|
+
Alula::Client.role = :dealer
|
68
|
+
|
69
|
+
# Explicitly set the role via a u_type integer
|
70
|
+
Alula::Client.u_type = 8
|
71
|
+
```
|
72
|
+
|
73
|
+
Any method will do, you just need to perform one of these prior to attempting to save a resource. Role symbols map to uType resources. The Alula Gem supports the following roles & their corrisponding uType:
|
74
|
+
|
75
|
+
```
|
76
|
+
| Name | uType | Role | Description |
|
77
|
+
|---------------|-------|----------------|----------------------------------------|
|
78
|
+
| System | 2 | :system | Highly-privileged system user |
|
79
|
+
| Station | 4 | :station | Central station user |
|
80
|
+
| Dealer | 8 | :dealer | Dealer user; child of station users |
|
81
|
+
| Technician | 16 | :technician | Technician user; child of dealer users |
|
82
|
+
| User | 32 | :user | Normal user; child of dealer users |
|
83
|
+
| Sub-User | 64 | :sub_user | Child user; child of normal users |
|
84
|
+
| User/Sub-User | 96 | :user_sub_user | Child user; child of normal users |
|
85
|
+
```
|
86
|
+
|
87
|
+
See the official Alula API docs for a detailed breakdown of which fields on which resources are changable by which roles.
|
88
|
+
|
89
|
+
A Hash object is provided on the `Alula::User` model mapping `uType` to `Role`:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
pp Alula::User::UTYPE_ROLE_MAP
|
93
|
+
{
|
94
|
+
2 => :system,
|
95
|
+
4 => :station,
|
96
|
+
8 => :dealer,
|
97
|
+
16 => :technician,
|
98
|
+
32 => :user,
|
99
|
+
64 => :sub_user,
|
100
|
+
96 => :user_sub_user
|
101
|
+
}
|
102
|
+
```
|
103
|
+
|
104
|
+
### Retrieving Records
|
105
|
+
|
106
|
+
Records can be fetched as a single record:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
# Fetch a single device
|
110
|
+
device = Alula::Device.retrieve(device_id)
|
111
|
+
|
112
|
+
device.friendly_name
|
113
|
+
|
114
|
+
#> 'Test Device'
|
115
|
+
|
116
|
+
# Fetch singleton resources, like self, without an ID
|
117
|
+
me = Alula::Self.retrieve
|
118
|
+
```
|
119
|
+
|
120
|
+
Collections of records can also be fetched:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
# Fetch devices
|
124
|
+
devices = Alula::Device.list
|
125
|
+
|
126
|
+
# Fetch user data
|
127
|
+
users = Alula::User.list
|
128
|
+
```
|
129
|
+
|
130
|
+
#### Paging collections
|
131
|
+
|
132
|
+
Collections can be paginated
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
# Use .offset to request different pages
|
136
|
+
devices = Alula::Device.offset(2).list
|
137
|
+
|
138
|
+
# Use .size to change the default page size
|
139
|
+
devices = Alula::Device.size(100).list
|
140
|
+
|
141
|
+
# Use together
|
142
|
+
devices = Alula::Device.offset(20).size(25).list
|
143
|
+
|
144
|
+
# A raw .page method is offered:
|
145
|
+
devices = Alula::Device.page(size: 20, number: 2).list
|
146
|
+
```
|
147
|
+
|
148
|
+
#### Sorting collections
|
149
|
+
|
150
|
+
Collections can be sorted. See the Alula API documentation for details of which fields can be sorted.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
devices = Alula::DeviceEventLog.sort(date_entered: :desc).where_like(mac: '%54%').list
|
154
|
+
```
|
155
|
+
|
156
|
+
#### Including related models
|
157
|
+
|
158
|
+
Many models relate to other models. When loading a model you can specify related models to 'include' in the fetch, and if they exist they will be available on the model.
|
159
|
+
|
160
|
+
An `Alula::Device` has a single `Dealer` that relates to it, as defined in the Device metadata:
|
161
|
+
|
162
|
+
`relationship :dealer, type: 'dealers', cardinality: 'To-one'`
|
163
|
+
|
164
|
+
When you load the Device with and include the Dealer, any available `Dealer` data will become available as an `Alula::Dealer` object like so:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
device = Alula::Device.includes(:dealer).retrieve(some_id)
|
168
|
+
puts device.dealer
|
169
|
+
# A Dealer model will be output here
|
170
|
+
puts device.dealer.company_name
|
171
|
+
# A company name here...
|
172
|
+
```
|
173
|
+
|
174
|
+
`TODO: This section is in progress! We are missing many models, so not all relationships are ready for inclusion.`
|
175
|
+
|
176
|
+
#### Filter collections
|
177
|
+
|
178
|
+
Collections of records can be filtered against using a fluent query API. Check the API documentation to see which fields can be filtered for each model. Errors will be raised when invalid field filter options are selected.
|
179
|
+
|
180
|
+
Filters directly map to their [Sequelize operator](https://sequelize.org/master/manual/querying.html#operators), when composing complex queries it is helpful to know how the Sequelize operators work.
|
181
|
+
|
182
|
+
*Field Names*
|
183
|
+
|
184
|
+
All filters take field names as hash keys, and filter values as hash values. You can use the camelCase representation of each field, or a snake_case represenation. Internally all keys are cast to snake_case for validation, and to camelCase for actual querying.
|
185
|
+
|
186
|
+
*$where filter*
|
187
|
+
|
188
|
+
The `$where` filter is the most basic filter. It creates an exact match for any fields passed in:
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
devices = Alula::Device.where(friendly_name: 'TitusTestDevice', program_id: 33).list
|
192
|
+
devices = Alula::Device.where(friendly_name: 'TitusTestDevice').where(program_id: 33).list
|
193
|
+
```
|
194
|
+
|
195
|
+
*$and filter*
|
196
|
+
|
197
|
+
The `$and` filter is very similar to the `$where` filter. Unlike `$where`, each field passed into the `$and` filter will be wrapped in the explicit `$and` clause.
|
198
|
+
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
devices = Alula::Device.and(friendly_name: 'TitusTestDevice', program_id: 33).list
|
202
|
+
```
|
203
|
+
|
204
|
+
*$like and $notLike filters*
|
205
|
+
|
206
|
+
The `$like` and `$notLike` filters allow for wildcard searches across multiple fields.
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
# Find all devices where the friendly_name starts with 'Titus'
|
210
|
+
devices = Alula::Device.like(friendly_name: 'Titus%').list
|
211
|
+
|
212
|
+
# Find all devices where the term 'Titus' is not present in the friendly_name
|
213
|
+
devices = Alula::Device.not_like(friendly_name: '%Titus%').list
|
214
|
+
```
|
215
|
+
|
216
|
+
*$in and $notIn filters*
|
217
|
+
|
218
|
+
The `$in` and `$notIn` filters accept an array of matches to be queried. The values should be simple values, numbers, strings, and ISO8601 string representations of dates.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
# Find records with a value containing one of an array of values
|
222
|
+
devices = Alula::Device.in(program_id: [1, 22, 39]).list
|
223
|
+
|
224
|
+
# Exclude records with an array of values
|
225
|
+
devices = Alula::Device.not_in(program_id: [8, 33, 1]).list
|
226
|
+
```
|
227
|
+
|
228
|
+
*$between and $notBetween filters*
|
229
|
+
|
230
|
+
The `$between` and `$notBetween` filters are similar to an `$in` query, but they take an array of 2 values and request records between those values. Pass in numbers, ISO8601-formatted date strings, or strings where MariaDB can figure out what it means to be "between" each string.
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
# Find records created between dates
|
234
|
+
customers = Alula::User.between(date_entered: [1.year.ago.iso8601, Time.now.iso8601]).list
|
235
|
+
|
236
|
+
# Find records outside of a range of values
|
237
|
+
devices = Alula::Device.not_between(online_status_timestamp: [1.week.ago.to_i, Time.now.to_i]).list
|
238
|
+
```
|
239
|
+
|
240
|
+
*$not and $ne filters*
|
241
|
+
|
242
|
+
The `$not` and `$ne` filters ($ne for Not Equivilant) operators are roughly analagous, though they use different matchers when constructing their SQL queries.
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
# Where a field is not strictly a value
|
246
|
+
devices = Alula::Device.not(friendly_name: 'TitusTestDevice').list
|
247
|
+
|
248
|
+
# Where a field is not equivilant to a value
|
249
|
+
devices = Alula::Device.ne(program_id: '2').list
|
250
|
+
```
|
251
|
+
|
252
|
+
*$lt, $lte, $gt, $gte filters*
|
253
|
+
|
254
|
+
The family of less-than, less-than-or-equal, greater-than, and greater-than-or-equal filters all works the same way. They accept a scalar value, a number or ISO8601 date string, and return records that match their constraints.
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
devices = Alula::Device.gt(:online_status_timestamp: 1.year.ago.to_i).list
|
258
|
+
devices = Alula::Device.gte(:online_status_timestamp: 1.year.ago.to_i).list
|
259
|
+
|
260
|
+
devices = Alula::Device.lt(:online_status_timestamp: 1.week.ago.to_i).list
|
261
|
+
devices = Alula::Device.lte(:online_status_timestamp: 1.week.ago.to_i).list
|
262
|
+
```
|
263
|
+
|
264
|
+
*$or filters*
|
265
|
+
|
266
|
+
The `$or` filter is unique in how it is called, and how it constructs a query. `$or` can take any nested set of other filter operators, so you can combine `$like` and `$in` filters into a single `$or` query to retrieve a broad set of records.
|
267
|
+
|
268
|
+
The `$or` filter provides a strict match key->value interface, and a fluent block interface.
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# Build an $or query for strict field values
|
272
|
+
# Will return any device with the friendly_name 'TitusTestDevice' OR a program_id of 33
|
273
|
+
devices = Alula::Device.or(friendly_name: 'TitusTestDevice', program_id: 33).list
|
274
|
+
|
275
|
+
# Build an expressive $or query
|
276
|
+
# The provided `query_builder` param allows full access to the filter API
|
277
|
+
# This will search for devices with a program_id of 2, 33, 85, or 12,
|
278
|
+
# where the friendly_name contains 'Helix', and no device is of program_id 2
|
279
|
+
#
|
280
|
+
devices = Alula::Device.or do |query_builder|
|
281
|
+
query_builder.in(program_id: [2, 33, 85, 12])
|
282
|
+
.like(friendly_name: '%Helix%')
|
283
|
+
end
|
284
|
+
devices = devices.not(program_id: 5).page(20).list
|
285
|
+
```
|
286
|
+
|
287
|
+
*$or $like filters*
|
288
|
+
|
289
|
+
The OR LIKE pattern is commonly used to do a wildcard search for a term across a bunch of fields at the same time. A shorthand query method is provided to make this easier. As this is a LIKE query, you can use wildcards (`%`) in your term.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
devices = Alula::Device.or_like(friendy_name: '%Titus%', email: '%@alula.net').list
|
293
|
+
```
|
294
|
+
|
295
|
+
*Constructing your own filters*
|
296
|
+
|
297
|
+
You can build your own JSON API-compliant filters with the `.filter` method if any of the built in operators do not work for you. Be aware that you *must* use the `camelCase` field name format, and no validation is performed against your query.
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
# All devices with a strict friendly_name match and an $in query on the program_id
|
301
|
+
devices = Alula::Device.filter({
|
302
|
+
friendlyName: 'TitusTestDevice',
|
303
|
+
$or: {
|
304
|
+
$in: {
|
305
|
+
programId: [2, 10]
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}).list
|
309
|
+
```
|
310
|
+
|
311
|
+
*Debugging Filters*
|
312
|
+
|
313
|
+
Before calling `.list` to execute your query, you can call `.as_json`, and be given a JSON representation of your built query. Be aware multiple filters against the same field may overwrite one another, so get a JSON dump to ensure that your assembled query is in the form you intend.
|
314
|
+
|
315
|
+
*Supported API filter methods*
|
316
|
+
|
317
|
+
The Alula API supports the following filter methods. Access to these filter methods are provided with Ruby methods on collection objects.
|
318
|
+
|
319
|
+
| API Operator | Ruby Method | Value | Comment |
|
320
|
+
|---------------|---------------|--------------------------------|----------|
|
321
|
+
| $gt | .gt | Scalar; number, date | Greater than (numeric) |
|
322
|
+
| $gte | .gte | ^ Ditto | Greater than or equal |
|
323
|
+
| $lt | .lt | ^ Ditto | Less than |
|
324
|
+
| $lte | .lte | ^ Ditto | Less than or equal |
|
325
|
+
| $not | .not | Scalar; any | Not |
|
326
|
+
| $ne | .ne | Scalar; any | Not equal |
|
327
|
+
| $between | .between | Array; numbers, dates | Between range |
|
328
|
+
| $notBetween | .not_between | ^ Ditto | Not between range |
|
329
|
+
| $in | .in | Array; any | Array of matches |
|
330
|
+
| $notIn | .notIn | Array; any | Array of negative matches |
|
331
|
+
| $like | .like | Scalar; string | Match string where % can be wildcard |
|
332
|
+
| $notLike | .not_like | Scalar; string | Negative match string where % can be wildcard |
|
333
|
+
| $or | .or | Complex | See example |
|
334
|
+
| $and | .and | Array of associative arrays | Assoc. array where keys are field names, values like at top-level |
|
335
|
+
|
336
|
+
|
337
|
+
### Saving Records
|
338
|
+
|
339
|
+
And you can save records
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
device.friendly_name = 'Waaaluigi'
|
343
|
+
device.save
|
344
|
+
#> true
|
345
|
+
```
|
346
|
+
|
347
|
+
Errors saving are reflected with a `false` return to the save call, and errors on the model:
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
device.meid = 'Test Test'
|
351
|
+
device.save
|
352
|
+
#> false
|
353
|
+
device.errors.first
|
354
|
+
```
|
355
|
+
|
356
|
+
### Remote Procedure Calls (RPC)
|
357
|
+
|
358
|
+
Alula-Ruby partially supports the Alula API's RPC namespace. All RPC methods use the same style method signature, differing only in what params are passed. See the API documentation for a list of params each method supports.
|
359
|
+
|
360
|
+
Each remote procedure call supports a single method, named `call`. This method takes a param list equal to the remote procedures params (underscored, not camelcased).
|
361
|
+
|
362
|
+
Responses respond to the method `.ok?` for inferring if an error took place.
|
363
|
+
|
364
|
+
Success responses are custom per RPC method. Some provide response data, some do not. Response data is raw JSON and is available via `response.result`, and it will be a Hash or Array.
|
365
|
+
|
366
|
+
|
367
|
+
## Development
|
368
|
+
|
369
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bundle exec bin/console` for an interactive prompt that will allow you to experiment.
|
370
|
+
|
371
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, and push git commits and tags.
|
372
|
+
|
373
|
+
### Using Docker
|
374
|
+
|
375
|
+
Alula Ruby runs its unit & integratin tests against a copy of the API running in Docker. The remote API is cleaned up (DB truncated & re-seeded fresh) between every `describe` or `context` block.
|
376
|
+
|
377
|
+
1. Set up local `ipdapi` cluster/swarm using `docker-compose`:
|
378
|
+
|
379
|
+
docker-compose -f alula-docker-compose.yml up -d
|
380
|
+
|
381
|
+
or use the `alula-docker-compose` approach:
|
382
|
+
|
383
|
+
alula-docker-compose -I @test-helper --registry '6z1wlx5zf1.execute-api.us-east-1.amazonaws.com/' -- up -d
|
384
|
+
|
385
|
+
1. Configure your shell with some shortcuts. Note: If you don't have these present in your shell nothing will work.
|
386
|
+
|
387
|
+
export API_URL=http://127.0.0.1:8800
|
388
|
+
export ALULA_SWARM_TEST_HELPER_URL=http://127.0.0.1:8850
|
389
|
+
export ALULA_SWARM_TEST_HELPER_PORT=8850
|
390
|
+
|
391
|
+
1. Run the complete test suite:
|
392
|
+
|
393
|
+
`bundle exec rspec`
|
394
|
+
|
395
|
+
1. Run a specific test file:
|
396
|
+
|
397
|
+
`bundle exec rspec ./spec/alula/oauth_spec.rb`
|
398
|
+
|
399
|
+
1. Run Guard to have tests run on file change
|
400
|
+
|
401
|
+
`bundle exec guard`
|
402
|
+
|
403
|
+
1. Update all Docker images to the latest images:
|
404
|
+
|
405
|
+
`docker-compose -f alula-docker-compose.yml pull`
|
406
|
+
|
407
|
+
Occasionally under heavy use the dockerized API may lose or drop its databases, resulting in the test suite erroring completly and very quickly. To fix this simply restart the API with `docker-compose -f alula-docker-compose.yml down && docker-compose -f alula-docker-compose.yml up -d`
|
408
|
+
|
409
|
+
|
410
|
+
## Releasing
|
411
|
+
|
412
|
+
In your PR:
|
413
|
+
|
414
|
+
- Update VERSION.md with a new version number and a change list
|
415
|
+
- Update `lib/alula/version.rb` with the new version number
|
416
|
+
- Execute `bundle install` to update the lockfile
|
417
|
+
- Commit these changes & include them in your PR.
|
418
|
+
|
419
|
+
After merging your PR:
|
420
|
+
|
421
|
+
- Check out `master` and pull to get the latest
|
422
|
+
- Run `bundle exec rake release`.
|
423
|
+
_A tag will be pushed to Github and then the UI will ask you input to push to a nonexistant URL. Just spam the enter key and let it error out. It's the tag on github that we care about._
|