alula-ruby 0.50.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +14 -0
  3. data/.env.example +8 -0
  4. data/.github/workflows/gem-push.yml +45 -0
  5. data/.gitignore +23 -0
  6. data/.rspec +3 -0
  7. data/.travis.yml +7 -0
  8. data/Dockerfile +6 -0
  9. data/Gemfile +12 -0
  10. data/Guardfile +42 -0
  11. data/README.md +423 -0
  12. data/Rakefile +6 -0
  13. data/VERSION.md +84 -0
  14. data/alula-docker-compose.yml +80 -0
  15. data/alula.gemspec +38 -0
  16. data/bin/console +15 -0
  17. data/bin/docparse +36 -0
  18. data/bin/genresource +79 -0
  19. data/bin/setup +8 -0
  20. data/bin/testauth +24 -0
  21. data/bin/testprep +9 -0
  22. data/data/docs/Alula_API_Documentation_2021-04-06.html +16240 -0
  23. data/docker-compose.yml +11 -0
  24. data/lib/alula/alula_response.rb +20 -0
  25. data/lib/alula/api_operations/delete.rb +52 -0
  26. data/lib/alula/api_operations/list.rb +45 -0
  27. data/lib/alula/api_operations/request.rb +44 -0
  28. data/lib/alula/api_operations/save.rb +81 -0
  29. data/lib/alula/api_resource.rb +196 -0
  30. data/lib/alula/client.rb +142 -0
  31. data/lib/alula/errors.rb +169 -0
  32. data/lib/alula/filter_builder.rb +271 -0
  33. data/lib/alula/helpers/device_attribute_translations.rb +68 -0
  34. data/lib/alula/list_object.rb +64 -0
  35. data/lib/alula/meta.rb +16 -0
  36. data/lib/alula/monkey_patches.rb +24 -0
  37. data/lib/alula/oauth.rb +118 -0
  38. data/lib/alula/pagination.rb +25 -0
  39. data/lib/alula/procedures/dealer_device_stats_proc.rb +16 -0
  40. data/lib/alula/procedures/dealer_restore_proc.rb +25 -0
  41. data/lib/alula/procedures/dealer_suspend_proc.rb +25 -0
  42. data/lib/alula/procedures/device_assign_proc.rb +23 -0
  43. data/lib/alula/procedures/device_cellular_history_proc.rb +33 -0
  44. data/lib/alula/procedures/device_rateplan_get_proc.rb +21 -0
  45. data/lib/alula/procedures/device_register_proc.rb +31 -0
  46. data/lib/alula/procedures/device_signal_add_proc.rb +42 -0
  47. data/lib/alula/procedures/device_signal_delivered_proc.rb +31 -0
  48. data/lib/alula/procedures/device_signal_update_proc.rb +32 -0
  49. data/lib/alula/procedures/device_unassign_proc.rb +16 -0
  50. data/lib/alula/procedures/device_unregister_proc.rb +21 -0
  51. data/lib/alula/procedures/upload_touchpad_branding_proc.rb +24 -0
  52. data/lib/alula/procedures/user_plansvideo_price_get.rb +21 -0
  53. data/lib/alula/procedures/user_transfer_accept.rb +19 -0
  54. data/lib/alula/procedures/user_transfer_authorize.rb +18 -0
  55. data/lib/alula/procedures/user_transfer_cancel.rb +18 -0
  56. data/lib/alula/procedures/user_transfer_deny.rb +19 -0
  57. data/lib/alula/procedures/user_transfer_reject.rb +19 -0
  58. data/lib/alula/procedures/user_transfer_request.rb +19 -0
  59. data/lib/alula/query_interface.rb +142 -0
  60. data/lib/alula/rate_limit.rb +11 -0
  61. data/lib/alula/relationship_attributes.rb +107 -0
  62. data/lib/alula/resource_attributes.rb +206 -0
  63. data/lib/alula/resources/admin_user.rb +207 -0
  64. data/lib/alula/resources/billing_program.rb +41 -0
  65. data/lib/alula/resources/dealer.rb +218 -0
  66. data/lib/alula/resources/dealer_account_transfer.rb +172 -0
  67. data/lib/alula/resources/dealer_address.rb +89 -0
  68. data/lib/alula/resources/dealer_branding.rb +226 -0
  69. data/lib/alula/resources/dealer_program.rb +75 -0
  70. data/lib/alula/resources/dealer_suspension_log.rb +49 -0
  71. data/lib/alula/resources/device.rb +716 -0
  72. data/lib/alula/resources/device_cellular_status.rb +134 -0
  73. data/lib/alula/resources/device_charge.rb +70 -0
  74. data/lib/alula/resources/device_event_log.rb +167 -0
  75. data/lib/alula/resources/device_program.rb +54 -0
  76. data/lib/alula/resources/event_trigger.rb +75 -0
  77. data/lib/alula/resources/event_webhook.rb +47 -0
  78. data/lib/alula/resources/feature_bysubject.rb +74 -0
  79. data/lib/alula/resources/feature_plan.rb +57 -0
  80. data/lib/alula/resources/feature_planvideo.rb +54 -0
  81. data/lib/alula/resources/feature_price.rb +46 -0
  82. data/lib/alula/resources/receiver_connection.rb +95 -0
  83. data/lib/alula/resources/receiver_group.rb +74 -0
  84. data/lib/alula/resources/revision.rb +91 -0
  85. data/lib/alula/resources/self.rb +61 -0
  86. data/lib/alula/resources/station.rb +130 -0
  87. data/lib/alula/resources/token_exchange.rb +34 -0
  88. data/lib/alula/resources/user.rb +229 -0
  89. data/lib/alula/resources/user_address.rb +121 -0
  90. data/lib/alula/resources/user_phone.rb +116 -0
  91. data/lib/alula/resources/user_preferences.rb +57 -0
  92. data/lib/alula/resources/user_pushtoken.rb +75 -0
  93. data/lib/alula/resources/user_videoprofile.rb +38 -0
  94. data/lib/alula/rest_resource.rb +17 -0
  95. data/lib/alula/rpc_resource.rb +40 -0
  96. data/lib/alula/rpc_response.rb +14 -0
  97. data/lib/alula/singleton_rest_resource.rb +26 -0
  98. data/lib/alula/util.rb +107 -0
  99. data/lib/alula/version.rb +5 -0
  100. data/lib/alula.rb +135 -0
  101. data/lib/parser.rb +199 -0
  102. 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,8 @@
1
+ CLIENT_ID=<oauth_id>
2
+ CLIENT_SECRET=<oauth_secret>
3
+ API_URL=<api_url>
4
+ DEALER_ACCOUNT_UN=<dealer_account>
5
+ DEALER_ACCOUNT_PW=<dealer_password>
6
+ ADMIN_ACCOUNT_UN=<admin_username>
7
+ ADMIN_ACCOUNT_PW=<admin_password>
8
+ TEST_ACCESS_TOKEN=<test_access_token>
@@ -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
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.2
7
+ before_install: gem install bundler -v 2.0.1
data/Dockerfile ADDED
@@ -0,0 +1,6 @@
1
+ FROM ruby:2.6-alpine
2
+
3
+ RUN gem install bundler
4
+
5
+ RUN apk add bash git build-base jq curl
6
+
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in alula.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ platforms :mri do
8
+ gem "byebug"
9
+ gem "pry"
10
+ gem "pry-byebug"
11
+ end
12
+ end
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._
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec