fmrest-cloud 0.19.0.rc1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fdace6ff94d00d799393765c739dc33c1634c283518c19c4b7318f46adf3f7f7
4
+ data.tar.gz: 7fa1956020d4e54e658964ab33a34df571ee2d48ab085d74c832207d1e920542
5
+ SHA512:
6
+ metadata.gz: 8081823bf1d53a0760fd3384bb718a95ce994d614742efdb0cd8630a54dbd20e54191c1bd864d4613e844d1739cac915465174be5e1c8406671a46d1bbc53cfc
7
+ data.tar.gz: 65bc966479036e35c5bdc4994caed746d817e84f78c4405bcd0e5c063d02a792ca48d2881ea8f99d7aa5b7c0b6bfdedaa1cf59a90bef69e1c5e9c7808c61f0b7
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --markup markdown
2
+ --plugin activesupport-concern
3
+ lib/**/*.rb
4
+ -
5
+ docs/*
data/CHANGELOG.md ADDED
@@ -0,0 +1,178 @@
1
+ ## Changelog
2
+
3
+ ### 0.19.0
4
+
5
+ * Add native support for FileMaker cloud through the `fmrest-cloud` gem
6
+
7
+ ### 0.18.0
8
+
9
+ * Better support for portals with mismatching field qualifiers
10
+ * Better ergonomics for script execution, improved documentation
11
+ * Defining an attribute on a model that would collide with an existing method
12
+ now raises an error
13
+ * Cleared Faraday deprecation messages on authentication methods
14
+ * Handle FileMaker Cloud case where HTTP 401 Unauthorized with content-type
15
+ text/html is returned after token expiry
16
+ * Add retry option to Rescuable mixin
17
+ * Added fmrest-ruby/VERSION to User-Agent headers
18
+
19
+ ### 0.17.1
20
+
21
+ * Fixed crash when `fmid_token` is set but `username` isn't
22
+
23
+ ### 0.17.0
24
+
25
+ * Added support for Claris ID token login
26
+ * Added ability to use procs in settings
27
+ * Added `Rescuable` mixin
28
+
29
+ ### 0.16.0
30
+
31
+ * Added `FmRest.logger=`
32
+ * Handle serialization of `nil`, `true` and `false` values
33
+
34
+ ### 0.15.2
35
+
36
+ * Fixed autoloading of `FmRest::Layout`
37
+
38
+ ### 0.15.0
39
+
40
+ * Much improved querying API (see documentation on querying), adding new
41
+ `.query` capabilities, as well as two new methods: `.match` and `.or`
42
+
43
+ ### 0.14.0
44
+
45
+ * Aliased `FmRest::Spyke::Base` as `FmRest::Layout` (now preferred), and
46
+ provided a shortcut version for setting the layout name (e.g. `class Foo <
47
+ FmRest::Layout("LayoutName")`)
48
+ * Made `layout` class setting subclass-inheritable
49
+
50
+ ### 0.13.1
51
+
52
+ * Fixed downloading of container field data from FMS19+
53
+
54
+ ### 0.13.0
55
+
56
+ * Split `fmrest` gem into `fmrest-core` and `fmrest-spyke`. `fmrest` becomes a
57
+ wrapper for the two new gems.
58
+ * Fixed bug preventing connection databases with spaces in their names.
59
+ * Improved portal support with ability to delete portal records, and better
60
+ refreshing of portal records after saving the parent.
61
+ * `FmRest::Spyke::Base#__record_id` and `FmRest::Spyke::Base#__mod_id` now
62
+ always return integers if set.
63
+
64
+ ### 0.12.0
65
+
66
+ * Rename `FmRest::Spyke::Base#id=` to `FmRest::Spyke::Base#__record_id=` to
67
+ prevent clobbering of FileMaker layout-defined fields
68
+ * Removed previously deprecated `FmRest::Spyke::Base(config)` syntax
69
+ * Better yard documentation
70
+
71
+ ### 0.11.1
72
+
73
+ * Fixed a couple crashes due to missing constants
74
+
75
+ ### 0.11.0
76
+
77
+ * Added custom class for connection settings, providing indifferent access
78
+ (i.e. keys can be strings or symbols), and centralized default values and
79
+ validations
80
+ * Added `:autologin`, `:token` and `:token_store` connection settings
81
+ * Added `FmRest::Base.fmrest_config_overlay=` and related methods
82
+ * Added `FmRest::V1.request_auth_token` and
83
+ `FmRest::Spyke::Base.request_auth_token` (as well as `!`-suffixed versions
84
+ which raise exceptions on failure)
85
+
86
+ ### 0.10.1
87
+
88
+ * Fixed `URI.escape` obsolete warning messages in Ruby 2.7 by replacing it with
89
+ `URI.encode_www_form_component`
90
+ ([PR#40](https://github.com/beezwax/fmrest-ruby/pull/40))
91
+
92
+ ### 0.10.0
93
+
94
+ * Added `FmRest::StringDateAwareness` module to correct some issues when using
95
+ `FmRest::StringDate`
96
+ * Added basic timezones support
97
+ * Deprecated `class < FmRest::Spyke::Base(config_hash)` syntax in favor of
98
+ using `self.fmrest_config=`
99
+
100
+ ### 0.9.0
101
+
102
+ * Added `FmRest::Spyke::Base.set_globals`
103
+
104
+ ### 0.8.0
105
+
106
+ * Improved metadata when using `FmRest::Spyke::Model`. Metadata now uses
107
+ Struct/OpenStruct, so properties are accessible through `.property`, as well
108
+ as `[:property]`
109
+ * Added batch-finders `.find_in_batches` and `.find_each` for
110
+ * `FmRest::Spyke::Base`
111
+
112
+ ### 0.7.1
113
+
114
+ * Made sure `Model.find_one` and `Model.find_some` work without needing to call
115
+ `Model.all` in between
116
+
117
+ ### 0.7.0
118
+
119
+ * Added date coercion feature
120
+
121
+ ### 0.6.0
122
+
123
+ * Implemented session logout
124
+ ([#16](https://github.com/beezwax/fmrest-ruby/issues/16))
125
+
126
+ ### 0.5.2
127
+
128
+ * Improved support for legacy ActiveModel 4.x
129
+
130
+ ### 0.5.1
131
+
132
+ * Alias `:username` option as `:account_name` for ginjo-rfm gem
133
+ cross-compatibility
134
+
135
+ ### 0.5.0
136
+
137
+ * Much improved script execution support
138
+ ([#20](https://github.com/beezwax/fmrest-ruby/issues/20))
139
+ * Fixed bug when setting `default_limi` and trying to find a record
140
+ ([35](https://github.com/beezwax/fmrest-ruby/issues/35))
141
+
142
+ ### 0.4.1
143
+
144
+ * Prevent raising an exception when a /\_find request yields no results
145
+ ([#33](https://github.com/beezwax/fmrest-ruby/issues/33) and
146
+ [#34](https://github.com/beezwax/fmrest-ruby/issues/34))
147
+
148
+ ### 0.4.0
149
+
150
+ * Implement ability to set limit and offset for portals
151
+ * Implement disabling and requesting all portals
152
+
153
+ ### 0.3.3
154
+
155
+ * Fix encoding of paths for layouts with brackets in them (e.g. `"\[Very Ugly\]
156
+ Layout"`)
157
+ * Raise an error if `"id"` is assigned as an attribute on a model, as it's
158
+ currently a reserved method name by Spyke
159
+
160
+ ### 0.3.2
161
+
162
+ * Fixed support for ActiveSupport < 5.2
163
+ ([#27](https://github.com/beezwax/fmrest-ruby/issues/27))
164
+
165
+ ### 0.3.0
166
+
167
+ * Added Moneta token store
168
+
169
+ ### 0.2.5
170
+
171
+ * Fixed crash in `fetch_container_data` when no proxy options were set
172
+
173
+ ### 0.2.4
174
+
175
+ * Use `String#=~` instead of `String#match?` for Ruby <2.4 compatibility (Fixes
176
+ [#26](https://github.com/beezwax/fmrest-ruby/issues/26))
177
+ * Deprecated `FmRest.config` in favor of `FmRest.default_connection_settings`
178
+ * Honor Faraday SSL and proxy settings when fetching container files
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Pedro Carbajal and Beezwax Datatools, Inc.
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,586 @@
1
+ # fmrest-ruby
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/fmrest.svg?style=flat)](https://rubygems.org/gems/fmrest)
4
+ ![CI](https://github.com/beezwax/fmrest-ruby/workflows/CI/badge.svg)
5
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://rubydoc.info/github/beezwax/fmrest-ruby)
6
+
7
+ A Ruby client for
8
+ [FileMaker's Data API](https://help.claris.com/en/data-api-guide)
9
+ with ActiveRecord-ish ORM features.
10
+
11
+ While pretty feature-rich, fmrest-ruby doesn't yet support 100% of FileMaker
12
+ 19's Data API features. See the [implementation completeness
13
+ table](#api-implementation-completeness-table) to check if a feature you need
14
+ is natively supported by the gem.
15
+
16
+ ## Contents
17
+
18
+ * [Gems](#gems)
19
+ * [Installation](#installation)
20
+ * [Simple example](#simple-example)
21
+ * [Connection settings](#connection-settings)
22
+ * [Session token store](#session-token-store)
23
+ * [Date fields and timezones](#date-fields-and-timezones)
24
+ * [ActiveRecord-like ORM (fmrest-spyke)](#activerecord-like-orm--fmrest-spyke-)
25
+ * [Logging](#logging)
26
+ * [Gotchas](#gotchas)
27
+ * [API implementation completeness table](#api-implementation-completeness-table)
28
+ * [Supported Ruby versions](#supported-ruby-versions)
29
+
30
+ ## Gems
31
+
32
+ The `fmrest` gem is a wrapper for two other gems:
33
+
34
+ * `fmrest-spyke`, providing an ActiveRecord-like ORM library built on top
35
+ of `fmrest-core` and [Spyke](https://github.com/balvig/spyke).
36
+ * `fmrest-core`, providing the core
37
+ [Faraday](https://github.com/lostisland/faraday) connection builder, session
38
+ management, and other core utilities.
39
+
40
+ In addition, the optional `fmrest-cloud` gem adds support for FileMaker Cloud.
41
+ See the [main document on connecting to FileMaker
42
+ Cloud](docs/FileMakerCloud.md).
43
+
44
+ ## Installation
45
+
46
+ In your Gemfile:
47
+
48
+ ```ruby
49
+ gem 'fmrest'
50
+
51
+ # Optional: if your files are hosted on FileMaker Cloud
52
+ gem 'fmrest-cloud'
53
+ ```
54
+
55
+ ## Simple example
56
+
57
+ ```ruby
58
+ # A Layout model connecting to the "Honeybees Web" FileMaker layout
59
+ class Honeybee < FmRest::Layout("Honeybees Web")
60
+ # Connection settings
61
+ self.fmrest_config = {
62
+ host: "…",
63
+ database: "…",
64
+ username: "…",
65
+ password: "…"
66
+ }
67
+
68
+ # Mapped attributes
69
+ attributes name: "Bee Name", age: "Bee Age", created_on: "Created On"
70
+
71
+ # Portal associations
72
+ has_portal :tasks
73
+
74
+ # File containers
75
+ container :photo, field_name: "Bee Photo"
76
+
77
+ # Scopes
78
+ scope :can_legally_fly, -> { query(age: ">18") }
79
+
80
+ # Client-side validations
81
+ validates :name, presence: true
82
+
83
+ # Callbacks
84
+ before_save :set_created_on
85
+
86
+ private
87
+
88
+ def set_created_on
89
+ self.created_on = Date.today
90
+ end
91
+ end
92
+
93
+ # Find a record by id
94
+ bee = Honeybee.find(9)
95
+
96
+ bee.name = "Hutch"
97
+
98
+ # Add a new record to portal
99
+ bee.tasks.build(urgency: "Today")
100
+
101
+ bee.save
102
+ ```
103
+
104
+ In case you don't want the ORM features (i.e. you only need authentication and
105
+ JSON parsing, and are comfortable writing the API requests manually without the
106
+ ORM overhead) you can use the Faraday connection provided by `fmrest-core`.
107
+ See the [main document on using the base
108
+ connection](docs/BaseConnectionUsage.md) for more.
109
+
110
+ ## Connection settings
111
+
112
+ The minimum required connection settings are `:host`, `:database`, `:username`
113
+ and `:password`, but fmrest-ruby has many other options you can pass when
114
+ setting up a connection (see [full list](#full-list-of-available-options) below).
115
+
116
+ `:ssl` and `:proxy` are forwarded to the underlying
117
+ [Faraday](https://github.com/lostisland/faraday) connection. You can use this
118
+ to, for instance, disable SSL verification:
119
+
120
+ ```ruby
121
+ {
122
+ host: "…",
123
+
124
+ ssl: { verify: false }
125
+ }
126
+ ```
127
+
128
+ You can also pass a `:log` option for basic request logging, see the section on
129
+ [Logging](#Logging) below.
130
+
131
+ ### Full list of available options
132
+
133
+ Option | Description | Format | Default
134
+ --------------------|--------------------------------------------|-----------------------------|--------
135
+ `:host` | Hostname with optional port, e.g. `"example.com:9000"` | String | None
136
+ `:database` | The name of the database to connect to | String | None
137
+ `:username` | A Data API-ready account | String | None
138
+ `:password` | Your password | String | None
139
+ `:account_name` | Alias of `:username` | String | None
140
+ `:ssl` | SSL options to be forwarded to Faraday | Faraday SSL options | None
141
+ `:proxy` | Proxy options to be forwarded to Faraday | Faraday proxy options | None
142
+ `:log` | Log JSON responses to STDOUT | Boolean | `false`
143
+ `:log_level` | Which log level to log into | Values accepted by `Logger#level=` | `:debug`
144
+ `:coerce_dates` | See section on [date fields](#date-fields-and-timezones) | Boolean \| `:hybrid` \| `:full` | `false`
145
+ `:date_format` | Date parsing format | String (FM date format) | `"MM/dd/yyyy"`
146
+ `:timestamp_format` | Timestmap parsing format | String (FM date format) | `"MM/dd/yyyy HH:mm:ss"`
147
+ `:time_format` | Time parsing format | String (FM date format) | `"HH:mm:ss"`
148
+ `:timezone` | The timezone for the FM server | `:local` \| `:utc` \| `nil` | `nil`
149
+ `:autologin` | Whether to automatically start Data API sessions | Boolean | `true`
150
+ `:token` | Used to manually provide a session token (e.g. if `:autologin` is `false`) | String | None
151
+ `:fmid_token` | Claris ID token (only needed if manually obtaining the token) | String | None
152
+ `:cloud` | Specifies whether the host is using FileMaker Cloud | `:auto` \| Boolean | `:auto`
153
+ `:cognito_client_id`| Overwrites the hardcoded FileMaker Cloud Cognito Client ID | String | None
154
+ `:cognito_pool_id` | Overwrites the hardcoded FileMaker Cloud Cognito Pool ID | String | None
155
+ `:aws_region` | Overwrites the hardcoded FileMaker Cloud AWS Region | String | None
156
+
157
+ ### Default connection settings
158
+
159
+ If you're only connecting to a single FM database you can configure it globally
160
+ through `FmRest.default_connection_settings=`. E.g.:
161
+
162
+ ```ruby
163
+ FmRest.default_connection_settings = {
164
+ host: "…",
165
+ database: "…",
166
+ username: "…",
167
+ password: "…"
168
+ }
169
+ ```
170
+
171
+ These settings will be used by default by `FmRest::Layout` models whenever you
172
+ don't set `fmrest_config=` explicitly, as well as by
173
+ `FmRest::V1.build_connection` in case you're setting up your Faraday connection
174
+ manually.
175
+
176
+ ## Session token store
177
+
178
+ fmrest-ruby includes a number of options for storing session tokens:
179
+
180
+ * Memory
181
+ * ActiveRecord
182
+ * Redis
183
+ * Moneta
184
+
185
+ See the [main document on token stores](docs/TokenStore.md) for detailed info
186
+ on how to set up each store.
187
+
188
+ ## Date fields and timezones
189
+
190
+ fmrest-ruby has automatic detection and coercion of date fields to and from
191
+ Ruby date/time objects. Basic timezone support is also provided.
192
+
193
+ See the [main document on date fields](docs/DateFields.md) for more info.
194
+
195
+ ## ActiveRecord-like ORM (fmrest-spyke)
196
+
197
+ [Spyke](https://github.com/balvig/spyke) is an ActiveRecord-like gem for
198
+ building REST ORM models. fmrest-ruby uses it to build its ORM features,
199
+ bundled in the `fmrest-spyke` gem (already included if you're using the
200
+ `fmrest` gem).
201
+
202
+ To create a model you can inherit directly from `FmRest::Layout` (itself a
203
+ subclass of `Spyke::Base`).
204
+
205
+ ```ruby
206
+ class Honeybee < FmRest::Layout
207
+ end
208
+ ```
209
+
210
+ All of Spyke's basic ORM operations work as expected:
211
+
212
+ ```ruby
213
+ bee = Honeybee.new
214
+
215
+ bee.name = "Hutch"
216
+ bee.save # POST request (creates new record)
217
+
218
+ bee.name = "ハッチ"
219
+ bee.save # PATCH request (updates existing record)
220
+
221
+ bee.reload # GET request
222
+
223
+ bee.destroy # DELETE request
224
+
225
+ bee = Honeybee.find(9) # GET request
226
+ ```
227
+
228
+ It's recommended that you read Spyke's documentation for more information on
229
+ these basic features. If you've used ActiveRecord or similar ORM libraries
230
+ you'll find it quite familiar.
231
+
232
+ Notice that `FmRest::Layout` is aliased as `FmRest::Spyke::Base`. Previous
233
+ versions of fmrest-ruby only provided the latter version, so if you're already
234
+ using `FmRest::Spyke::Base` there's no need to rename your classes to
235
+ `FmRest::Layout`, both will continue to work interchangeably.
236
+
237
+ In addition, `FmRest::Layout` extends `Spyke::Base` with the following
238
+ features:
239
+
240
+ ### FmRest::Layout.fmrest_config=
241
+
242
+ This allows you to set Data API connection settings specific to your model
243
+ class:
244
+
245
+ ```ruby
246
+ class Honeybee < FmRest::Layout
247
+ self.fmrest_config = {
248
+ host: "…",
249
+ database: "…",
250
+ username: "…",
251
+ password: "…"
252
+ }
253
+ end
254
+ ```
255
+
256
+ These settings are class-inheritable, so you could create a base class that
257
+ does the initial connection setup and then inherit from it in models using that
258
+ same connection. E.g.:
259
+
260
+ ```ruby
261
+ class ApplicationFmLayout < FmRest::Layout
262
+ self.fmrest_config = { host: "…", database: "…", … }
263
+ end
264
+
265
+ class Honeybee < ApplicationFmLayout
266
+ # This model will use the same connection as ApplicationFmLayout
267
+ end
268
+ ```
269
+
270
+ If `fmrest_config` is not set, your model will try to use
271
+ `FmRest.default_connection_settings` instead.
272
+
273
+ #### Connection settings overlays
274
+
275
+ There may be cases where you want to use a different set of connection settings
276
+ depending on context. For example, if you want to use username and password
277
+ provided by the user in a web application. Since `.fmrest_config` is set at the
278
+ class level, changing the username/password for the model in one context would
279
+ also change it in all other contexts, leading to security issues.
280
+
281
+ To solve this scenario, fmrest-ruby provides a way of defining thread-local,
282
+ reversible connection settings overlays through `.fmrest_config_overlay=`.
283
+
284
+ See the [main document on connection setting overlays](docs/ConfigOverlays.md)
285
+ for details on how it works.
286
+
287
+ ### FmRest::Layout.layout
288
+
289
+ Use `layout` to set the layout name for your model.
290
+
291
+ ```ruby
292
+ class Honeybee < FmRest::Layout
293
+ layout "Honeybees Web"
294
+ end
295
+ ```
296
+
297
+ Alternatively, if you're inheriting from `FmRest::Layout` directly you can set
298
+ the layout name in the class definition line:
299
+
300
+ ```ruby
301
+ class Honeybee < FmRest::Layout("Honeybees Web")
302
+ ```
303
+
304
+ Note that you only need to manually set the layout name if the name of the
305
+ class and the name of the layout differ, otherwise fmrest-ruby will just use
306
+ the name of the class.
307
+
308
+ ### FmRest::Layout.request_auth_token
309
+
310
+ Requests a Data API session token using the connection settings in
311
+ `fmrest_config` and returns it if successful, otherwise returns `false`.
312
+
313
+ You normally don't need to use this method as fmrest-ruby will automatically
314
+ request and store session tokens for you (provided that `:autologin` is
315
+ `true` in the connection settings, which it is by default).
316
+
317
+ ### FmRest::Layout.logout
318
+
319
+ Use `.logout` to log out from the database session (you may call it on any
320
+ model that uses the database session you want to log out from).
321
+
322
+ ```ruby
323
+ Honeybee.logout
324
+ ```
325
+
326
+ ### Mapped FmRest::Layout.attributes
327
+
328
+ Spyke allows you to define your model's attributes using `attributes`, however
329
+ sometimes FileMaker's field names aren't very Ruby-ORM-friendly, especially
330
+ since they may sometimes contain spaces and other special characters, so
331
+ fmrest-ruby extends `attributes`' functionality to allow you to map
332
+ Ruby-friendly attribute names to FileMaker field names. E.g.:
333
+
334
+ ```ruby
335
+ class Honeybee < FmRest::Layout
336
+ attributes first_name: "First Name", last_name: "Last Name"
337
+ end
338
+ ```
339
+
340
+ You can then simply use the pretty attribute names whenever working with your
341
+ model and they will get mapped to their FileMaker fields:
342
+
343
+ ```ruby
344
+ bee = Honeybee.find(1)
345
+
346
+ bee.first_name # => "Princess"
347
+ bee.last_name # => "Buzz"
348
+
349
+ bee.first_name = "Queen"
350
+
351
+ bee.attributes # => { "First Name": "Queen", "Last Name": "Buzz" }
352
+ ```
353
+
354
+ ### FmRest::Layout.has_portal
355
+
356
+ You can define portal associations on your model wth `has_portal`, as such:
357
+
358
+ ```ruby
359
+ class Honeybee < FmRest::Layout
360
+ has_portal :flowers
361
+ end
362
+
363
+ class Flower < FmRest::Layout
364
+ attributes :color, :species
365
+ end
366
+ ```
367
+
368
+ See the [main document on portal associations](docs/Portals.md) for details.
369
+
370
+ ### Dirty attributes
371
+
372
+ fmrest-ruby includes support for ActiveModel's Dirty mixin out of the box,
373
+ providing methods like:
374
+
375
+ ```ruby
376
+ bee = Honeybee.new
377
+
378
+ bee.changed? # => false
379
+
380
+ bee.name = "Maya"
381
+
382
+ bee.changed? # => true
383
+
384
+ bee.name_changed? # => true
385
+ ```
386
+
387
+ fmrest-ruby uses the Dirty functionality to only send changed attributes back
388
+ to the server on save.
389
+
390
+ You can read more about [ActiveModel's Dirty in Rails
391
+ Guides](https://guides.rubyonrails.org/active_model_basics.html#dirty).
392
+
393
+ ### Query API
394
+
395
+ Since Spyke is API-agnostic it only provides a wide-purpose `.where` method for
396
+ passing arbitrary parameters to the REST backend. fmrest-ruby however is well
397
+ aware of its backend API, so it extends Spkye models with a bunch of useful
398
+ querying methods: `.query`, `.match`, `.omit`, `.limit`, `.offset`, `.sort`,
399
+ `.portal`, `.script`, etc.
400
+
401
+ See the [main document on querying](docs/Querying.md) for detailed information
402
+ on the query API methods.
403
+
404
+ ### Finding records in batches
405
+
406
+ Sometimes you want to iterate over a very large number of records to do some
407
+ processing, but requesting them all at once would result in one huge request to
408
+ the Data API, and loading too many records in memory all at once.
409
+
410
+ To mitigate this problem you can use `.find_in_batches` and `.find_each`.
411
+
412
+ See the [main document on finding in batches](docs/FindInBatches.md) for
413
+ detailed information on how those work.
414
+
415
+ ### Container fields
416
+
417
+ You can define container fields on your model class with `container`:
418
+
419
+ ```ruby
420
+ class Honeybee < FmRest::Layout
421
+ container :photo, field_name: "Beehive Photo ID"
422
+ end
423
+ ```
424
+
425
+ See the [main document on container fields](docs/ContainerFields.md) for
426
+ details on how to use it.
427
+
428
+ ### Script execution
429
+
430
+ The FM Data API allows running scripts as part of many types of requests, and
431
+ `fmrest-spyke` provides mechanisms for all of them.
432
+
433
+ See the [main document on script execution](docs/ScriptExecution.md) for
434
+ details.
435
+
436
+ ### Setting global field values
437
+
438
+ You can call `.set_globals` on any `FmRest::Layout` model to set global
439
+ field values on the database that model is configured for.
440
+
441
+ See the [main document on setting global field values](docs/GlobalFields.md)
442
+ for details.
443
+
444
+ ### Rescuable mixin
445
+
446
+ Sometimes you may want to handle Data API errors at the model level. For such
447
+ cases fmrest-ruby provides an off-by-default mixin called `Rescuable` that
448
+ provides convenience macros for that. If you've used Ruby on Rails you may be
449
+ familiar with its syntax from controllers. E.g.
450
+
451
+ ```ruby
452
+ class BeeBase < FmRest::Layout
453
+ include FmRest::Spyke::Model::Rescuable
454
+
455
+ rescue_from FmRest::APIError::SystemError, with: :notify_admin_of_system_error
456
+
457
+ def self.notify_admin_of_system_error(e)
458
+ # Shoot an email to the FM admin...
459
+ end
460
+ end
461
+ ```
462
+
463
+ Since `Rescuable` uses `ActiveSupport::Rescuable` internally, you may want to
464
+ check [Rails'
465
+ documentation](https://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html)
466
+ too for details on how it works.
467
+
468
+ One caveat of using `rescue_from` is that it always catches exceptions at the
469
+ class level, so if you pass a method name to `with:` that method has to be a
470
+ class method. Also note that this will only catch exceptions raised during an
471
+ API call to the Data API server (in other words, only on actions that perform
472
+ an HTTP request).
473
+
474
+ ## Logging
475
+
476
+ If using `fmrest-spyke` with Rails then pretty log output will be set up for
477
+ you automatically by Spyke (see [their
478
+ README](https://github.com/balvig/spyke#log-output)).
479
+
480
+ You can also enable simple Faraday logging of raw requests (useful for
481
+ debugging) by passing `log: true` in the options hash for either
482
+ `FmRest.default_connection_settings=` or your models' `fmrest_config=`, e.g.:
483
+
484
+ ```ruby
485
+ FmRest.default_connection_settings = {
486
+ host: "…",
487
+
488
+ log: true
489
+ }
490
+
491
+ # Or in your model
492
+ class LoggyBee < FmRest::Layout
493
+ self.fmrest_config = {
494
+ host: "…",
495
+
496
+ log: true
497
+ }
498
+ end
499
+ ```
500
+
501
+ You can also pass `log_level` to connection settings to change the severity of
502
+ log output (defaults to `:debug`).
503
+
504
+ By default fmrest-ruby logs to STDOUT or to Rails' logger object if available.
505
+ You can change this by providing your own logger object to `FmRest.logger=`:
506
+
507
+ ```ruby
508
+ FmRest.logger = Logger.new("fmrest.log")
509
+ ```
510
+
511
+ If you need to set up more complex logging for your models you can use the
512
+ `faraday` block inside your class to inject your own logger middleware into the
513
+ Faraday connection, e.g.:
514
+
515
+ ```ruby
516
+ class LoggyBee < FmRest::Layout
517
+ faraday do |conn|
518
+ conn.response :logger, MyApp.logger, bodies: true
519
+ end
520
+ end
521
+ ```
522
+
523
+ ## Gotchas
524
+
525
+ Read about unexpected scenarios in the [gotchas doc](docs/Gotchas.md).
526
+
527
+ ## API implementation completeness table
528
+
529
+ FM Data API reference: https://help.claris.com/en/data-api-guide/
530
+
531
+ | FM 19 Data API feature | Supported by basic connection | Supported by FmRest::Layout |
532
+ |-------------------------------------|-------------------------------|-----------------------------|
533
+ | Log in using HTTP Basic Auth | Yes | Yes |
534
+ | Log in using OAuth | No | No |
535
+ | Log in to an external data source | No | No |
536
+ | Log in using Claris ID account (FileMaker Cloud) | Yes | Yes |
537
+ | Log out | Yes | Yes |
538
+ | Get product information | Manual* | No |
539
+ | Get database names | Manual* | No |
540
+ | Get script names | Manual* | No |
541
+ | Get layout names | Manual* | No |
542
+ | Get layout metadata | Manual* | No |
543
+ | Create a record | Manual* | Yes |
544
+ | Edit a record | Manual* | Yes |
545
+ | Duplicate a record | Manual* | No |
546
+ | Delete a record | Manual* | Yes |
547
+ | Edit portal records | Manual* | Yes |
548
+ | Get a single record | Manual* | Yes |
549
+ | Get a range of records | Manual* | Yes |
550
+ | Get container data | Manual* | Yes |
551
+ | Upload container data | Manual* | Yes |
552
+ | Perform a find request | Manual* | Yes |
553
+ | Set global field values | Manual* | Yes |
554
+ | Run a script | Manual* | Yes |
555
+ | Run a script with another request | Manual* | Yes |
556
+
557
+ \* You can manually supply the URL and JSON to a `FmRest` connection.
558
+
559
+ ## Supported Ruby versions
560
+
561
+ fmrest-ruby aims to support and is [tested against](https://github.com/beezwax/fmrest-ruby/actions?query=workflow%3ACI)
562
+ the following Ruby implementations:
563
+
564
+ * Ruby 2.5
565
+ * Ruby 2.6
566
+ * Ruby 2.7
567
+ * Ruby 3.0
568
+
569
+ ## Gem development
570
+
571
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
572
+ `bundle exec rspec` to run the specs. You can also run `bin/console` for an
573
+ interactive prompt that will allow you to experiment (it will auto-load all
574
+ fixtures in spec/fixtures).
575
+
576
+ To install all gems onto your local machine, run
577
+ `bundle exec rake all:install`. To release a new version, update the version
578
+ number in `lib/fmrest/version.rb`, and then run `bundle exec rake all:release`,
579
+ which will create a git tag for the version, push git commits and tags, and
580
+ push the `.gem` files to [rubygems.org](https://rubygems.org).
581
+
582
+ ## Disclaimer
583
+
584
+ This project is not sponsored by or otherwise affiliated with Claris
585
+ International Inc., an Apple Inc. subsidiary. FileMaker is a trademark of
586
+ Claris International Inc., registered in the U.S. and other countries.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FmRest
4
+ module Cloud
5
+ class AuthErrorHandler < Faraday::Middleware
6
+ def initialize(app, settings)
7
+ super(app)
8
+ @settings = settings
9
+ end
10
+
11
+ def call(env)
12
+ request_body = env[:body] # After failure env[:body] is set to the response body
13
+ @app.call(env)
14
+ rescue APIError::AccountError => e
15
+ ClarisIdTokenManager.new(@settings).expire_token
16
+ # Faraday::Request::Authorization will not get a new token if the
17
+ # Authorization header is already set
18
+ env.request_headers.delete("Authorization")
19
+ env[:body] = request_body
20
+ @app.call(env)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-cognito-srp"
4
+
5
+ module FmRest
6
+ module Cloud
7
+ class ClarisIdTokenManager
8
+ include TokenStore
9
+
10
+ COGNITO_CLIENT_ID = "4l9rvl4mv5es1eep1qe97cautn"
11
+ COGNITO_POOL_ID = "us-west-2_NqkuZcXQY"
12
+ AWS_REGION = "us-west-2"
13
+
14
+ TOKEN_STORE_PREFIX = "claris-cognito"
15
+
16
+ def initialize(settings)
17
+ @settings = settings
18
+ end
19
+
20
+ def fetch_token
21
+ if token = token_store.load(token_store_key)
22
+ return token
23
+ end
24
+
25
+ tokens = get_cognito_tokens
26
+
27
+ token_store.store(token_store_key, tokens.id_token)
28
+ token_store.store(token_store_key(:refresh), tokens.refresh_token) if tokens.refresh_token
29
+
30
+ tokens.id_token
31
+ end
32
+
33
+ def expire_token
34
+ token_store.delete(token_store_key)
35
+ end
36
+
37
+ private
38
+
39
+ def get_cognito_tokens
40
+ # Use refresh mechanism first if we have a refresh token
41
+ refresh_cognito_token || cognito_srp_client.authenticate
42
+ end
43
+
44
+ def refresh_cognito_token
45
+ return unless refresh_token = token_store.load(token_store_key(:refresh))
46
+
47
+ begin
48
+ cognito_srp_client.refresh_tokens(refresh_token)
49
+ rescue Aws::CognitoIdentityProvider::Errors::NotAuthorizedException
50
+ nil
51
+ end
52
+ end
53
+
54
+ def cognito_srp_client
55
+ @cognito_srp_client ||=
56
+ Aws::CognitoSrp.new(
57
+ username: @settings.username!,
58
+ password: @settings.password!,
59
+ pool_id: @settings.cognito_pool_id || COGNITO_POOL_ID,
60
+ client_id: @settings.cognito_client_id || COGNITO_CLIENT_ID,
61
+ aws_client: Aws::CognitoIdentityProvider::Client.new(region: @settings.aws_region || AWS_REGION)
62
+ )
63
+ end
64
+
65
+ def token_store_key(token_type = :id)
66
+ "#{TOKEN_STORE_PREFIX}:#{token_type}:#{@settings.username!}"
67
+ end
68
+
69
+ def token_store_option
70
+ @settings.token_store || FmRest.token_store
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FmRest
4
+ module Cloud
5
+ autoload :ClarisIdTokenManager, "fmrest/cloud/claris_id_token_manager"
6
+ autoload :AuthErrorHandler, "fmrest/cloud/auth_error_handler"
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fmrest/cloud"
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fmrest-cloud
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.19.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - Pedro Carbajal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fmrest-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.19.0.rc1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.19.0.rc1
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-cognito-srp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.0
41
+ description: fmrest-cloud adds FileMaker Cloud (Cognito auth) support to the fmrest
42
+ gem.
43
+ email:
44
+ - pedro_c@beezwax.net
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".yardopts"
50
+ - CHANGELOG.md
51
+ - LICENSE.txt
52
+ - README.md
53
+ - lib/fmrest-cloud.rb
54
+ - lib/fmrest/cloud.rb
55
+ - lib/fmrest/cloud/auth_error_handler.rb
56
+ - lib/fmrest/cloud/claris_id_token_manager.rb
57
+ homepage: https://github.com/beezwax/fmrest-ruby
58
+ licenses:
59
+ - MIT
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">"
73
+ - !ruby/object:Gem::Version
74
+ version: 1.3.1
75
+ requirements: []
76
+ rubygems_version: 3.2.3
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: FileMaker Cloud support for fmrest gem
80
+ test_files: []