fmrest-spyke 0.16.0 → 0.18.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a91619fdca8717119b8b299648d3388948551e653bececb0a90a422850be0a2
4
- data.tar.gz: 9e666b8f8162522b1269a3b4d18cda8724ad7439d0398672c02d228149cb636d
3
+ metadata.gz: 38cc05be48b9ba5737e7a2c1d89504bab617a2f1d6d7261fde1b5a919aa793e7
4
+ data.tar.gz: 0b1780c8ce2f7d8d8f4c300909b2bafb7f41e18e2ce648f929be9e2d3569ddbe
5
5
  SHA512:
6
- metadata.gz: 6fee0ccdc7bb75ae47f620c0cbf3da137806394225a565f5227d9a63e4ea4612b78200997dfc84594ba4eb2db84a156a28d3b05109d9dfb95f6851434bd94f6b
7
- data.tar.gz: bab901d8024b1331358325f149ebc297c206e549aba81b73187748b2f168c7eecac2409170722e7a062901317084348c4ac3811bddffa07613bf944aa0f61f00
6
+ metadata.gz: 123d5611c9c862605c30643b8034665c67c24fc283d21311f6124baff2271c459bcae0d4834f2480f4aa2e521105a6df58ecb011f46fc4f50a656df4a91108f5
7
+ data.tar.gz: 91a6d0bdf49792a76b0bf817543a89e7b92bcf0c55eaa128d9bee3219f250e47a14bd064f862383f11f66dac70f1830cc6f1017b56d912f4d8be344e7ac86bed
data/CHANGELOG.md CHANGED
@@ -1,13 +1,29 @@
1
1
  ## Changelog
2
2
 
3
+ ### 0.18.0
4
+
5
+ * Better support for portals with mismatching field qualifiers
6
+ * Defining an attribute on a model that would collide with an existing method
7
+ now raises an error
8
+
9
+ ### 0.17.1
10
+
11
+ * Fixed crash when `fmid_token` is set but `username` isn't
12
+
13
+ ### 0.17.0
14
+
15
+ * Added support for Claris ID token login
16
+ * Added ability to use procs in settings
17
+ * Added `Rescuable` mixin
18
+
3
19
  ### 0.16.0
4
20
 
5
- * Add `FmRest.logger=`
21
+ * Added `FmRest.logger=`
6
22
  * Handle serialization of `nil`, `true` and `false` values
7
23
 
8
24
  ### 0.15.2
9
25
 
10
- * Fix autoloading of `FmRest::Layout`
26
+ * Fixed autoloading of `FmRest::Layout`
11
27
 
12
28
  ### 0.15.0
13
29
 
@@ -23,13 +39,13 @@
23
39
 
24
40
  ### 0.13.1
25
41
 
26
- * Fix downloading of container field data from FMS19+
42
+ * Fixed downloading of container field data from FMS19+
27
43
 
28
44
  ### 0.13.0
29
45
 
30
46
  * Split `fmrest` gem into `fmrest-core` and `fmrest-spyke`. `fmrest` becomes a
31
47
  wrapper for the two new gems.
32
- * Fix bug preventing connection databases with spaces in their names.
48
+ * Fixed bug preventing connection databases with spaces in their names.
33
49
  * Improved portal support with ability to delete portal records, and better
34
50
  refreshing of portal records after saving the parent.
35
51
  * `FmRest::Spyke::Base#__record_id` and `FmRest::Spyke::Base#__mod_id` now
@@ -44,7 +60,7 @@
44
60
 
45
61
  ### 0.11.1
46
62
 
47
- * Fix a couple crashes due to missing constants
63
+ * Fixed a couple crashes due to missing constants
48
64
 
49
65
  ### 0.11.0
50
66
 
@@ -59,7 +75,7 @@
59
75
 
60
76
  ### 0.10.1
61
77
 
62
- * Fix `URI.escape` obsolete warning messages in Ruby 2.7 by replacing it with
78
+ * Fixed `URI.escape` obsolete warning messages in Ruby 2.7 by replacing it with
63
79
  `URI.encode_www_form_component`
64
80
  ([PR#40](https://github.com/beezwax/fmrest-ruby/pull/40))
65
81
 
@@ -133,20 +149,20 @@
133
149
 
134
150
  ### 0.3.2
135
151
 
136
- * Fix support for ActiveSupport < 5.2
152
+ * Fixed support for ActiveSupport < 5.2
137
153
  ([#27](https://github.com/beezwax/fmrest-ruby/issues/27))
138
154
 
139
155
  ### 0.3.0
140
156
 
141
- * Add Moneta token store
157
+ * Added Moneta token store
142
158
 
143
159
  ### 0.2.5
144
160
 
145
- * Fix crash in `fetch_container_data` when no proxy options were set
161
+ * Fixed crash in `fetch_container_data` when no proxy options were set
146
162
 
147
163
  ### 0.2.4
148
164
 
149
165
  * Use `String#=~` instead of `String#match?` for Ruby <2.4 compatibility (Fixes
150
166
  [#26](https://github.com/beezwax/fmrest-ruby/issues/26))
151
- * Deprecate `FmRest.config` in favor of `FmRest.default_connection_settings`
167
+ * Deprecated `FmRest.config` in favor of `FmRest.default_connection_settings`
152
168
  * Honor Faraday SSL and proxy settings when fetching container files
data/README.md CHANGED
@@ -125,6 +125,10 @@ The minimum required connection settings are `:host`, `:database`, `:username`
125
125
  and `:password`, but fmrest-ruby has many other options you can pass when
126
126
  setting up a connection (see [full list](#full-list-of-available-options) below).
127
127
 
128
+ If you're using FileMaker Cloud you may need to pass `:fmid_token` instead
129
+ of the regular `:username` and `:password`. See the [main document on
130
+ connecting to FileMaker Cloud](docs/FileMakerCloud.md) for more info.
131
+
128
132
  `:ssl` and `:proxy` are forwarded to the underlying
129
133
  [Faraday](https://github.com/lostisland/faraday) connection. You can use this
130
134
  to, for instance, disable SSL verification:
@@ -149,6 +153,7 @@ Option | Description | Format
149
153
  `:username` | A Data API-ready account | String | None
150
154
  `:password` | Your password | String | None
151
155
  `:account_name` | Alias of `:username` | String | None
156
+ `:fmid_token` | Claris ID token (only needed for FileMaker Cloud) | String | None
152
157
  `:ssl` | SSL options to be forwarded to Faraday | Faraday SSL options | None
153
158
  `:proxy` | Proxy options to be forwarded to Faraday | Faraday proxy options | None
154
159
  `:log` | Log JSON responses to STDOUT | Boolean | `false`
@@ -283,10 +288,12 @@ Also, if not set, your model will try to use
283
288
  #### Connection settings overlays
284
289
 
285
290
  There may be cases where you want to use a different set of connection settings
286
- depending on context. For example, if you want to use username and password
287
- provided by the user in a web application. Since `.fmrest_config`
288
- is set at the class level, changing the username/password for the model in one
289
- context would also change it in all other contexts, leading to security issues.
291
+ depending on context, or simply change the connection settings over time. For
292
+ example, if you want to use username and password provided by the user in a web
293
+ application, or if you're connecting using an expiring Claris ID token. Since
294
+ `.fmrest_config` is set at the class level, changing the username/password for
295
+ the model in one context would also change it in all other contexts, leading to
296
+ security issues.
290
297
 
291
298
  To solve this scenario, fmrest-ruby provides a way of defining thread-local and
292
299
  reversible connection settings overlays through
@@ -323,7 +330,7 @@ Requests a Data API session token using the connection settings in
323
330
 
324
331
  You normally don't need to use this method as fmrest-ruby will automatically
325
332
  request and store session tokens for you (provided that `:autologin` is
326
- `true`).
333
+ `true` in the connection settings, which it is by default).
327
334
 
328
335
  ### FmRest::Layout.logout
329
336
 
@@ -452,6 +459,41 @@ field values on the database that model is configured for.
452
459
  See the [main document on setting global field values](docs/GlobalFields.md)
453
460
  for details.
454
461
 
462
+ ### Rescuable mixin
463
+
464
+ Sometimes you may want to handle Data API errors at the model level. For
465
+ instance, if you're logging in to a file hosted by FileMaker Cloud using a
466
+ Claris ID token, and you want to be able to renew said token when it fails to
467
+ log you in. For such cases fmrest-ruby provides an off-by-default mixin called
468
+ `Rescuable` that provides convenience macros for that. If you've used Ruby on
469
+ Rails you may be familiar with its syntax from controllers. E.g.
470
+
471
+ ```ruby
472
+ class BeeBase < FmRest::Layout
473
+ include FmRest::Spyke::Model::Rescuable
474
+
475
+ rescue_from FmRest::APIError::SystemError, with: :notify_admin_of_system_error
476
+
477
+ # Shorthand for rescue_with FmRest::APIError::AccountError, ...
478
+ rescue_account_error { ClarisIDTokenManager.expire_token }
479
+
480
+ def self.notify_admin_of_system_error(e)
481
+ # Shoot an email to the FM admin...
482
+ end
483
+ end
484
+ ```
485
+
486
+ Since `Rescuable` uses `ActiveSupport::Rescuable` internally, you may want to
487
+ check [Rails'
488
+ documentation](https://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html)
489
+ too for details on how it works.
490
+
491
+ One caveat of using `rescue_from` is that it always catches exceptions at the
492
+ class level, so if you pass a method name to `with:` that method has to be a
493
+ class method. Also note that this will only catch exceptions raised during an
494
+ API call to the Data API server (in other words, only on actions that perform
495
+ an HTTP request).
496
+
455
497
  ## Logging
456
498
 
457
499
  If using `fmrest-spyke` with Rails then pretty log output will be set up for
@@ -501,6 +543,10 @@ class LoggyBee < FmRest::Layout
501
543
  end
502
544
  ```
503
545
 
546
+ ## Gotchas
547
+
548
+ Read about unexpected scenarios in the [gotchas doc](docs/Gotchas.md).
549
+
504
550
  ## API implementation completeness table
505
551
 
506
552
  FM Data API reference: https://fmhelp.filemaker.com/docs/18/en/dataapi/
@@ -510,7 +556,7 @@ FM Data API reference: https://fmhelp.filemaker.com/docs/18/en/dataapi/
510
556
  | Log in using HTTP Basic Auth | Yes | Yes |
511
557
  | Log in using OAuth | No | No |
512
558
  | Log in to an external data source | No | No |
513
- | Log in using a FileMaker ID account | No | No |
559
+ | Log in using Claris ID account | Yes | Yes |
514
560
  | Log out | Yes | Yes |
515
561
  | Get product information | Manual* | No |
516
562
  | Get database names | Manual* | No |
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fmrest/spyke/portal_builder"
3
4
  require "fmrest/spyke/portal"
4
5
 
5
6
  module FmRest
@@ -13,6 +14,8 @@ module FmRest
13
14
  included do
14
15
  # Keep track of portal options by their FM keys as we could need it
15
16
  # to parse the portalData JSON in SpykeFormatter
17
+ #
18
+ # TODO: Replace this with options in PortalBuilder
16
19
  class_attribute :portal_options, instance_accessor: false, instance_predicate: false
17
20
 
18
21
  # class_attribute supports a :default option since ActiveSupport 5.2,
@@ -40,11 +43,13 @@ module FmRest
40
43
  # end
41
44
  #
42
45
  def has_portal(name, options = {})
43
- create_association(name, Portal, options)
46
+ # This is analogous to Spyke's create_association method, but using
47
+ # our custom builder instead
48
+ self.associations = associations.merge(name => PortalBuilder.new(self, name, Portal, options))
44
49
 
45
50
  # Store options for SpykeFormatter to use if needed
46
51
  portal_key = options[:portal_key] || name
47
- self.portal_options = portal_options.merge(portal_key.to_s => options.dup.merge(name: name.to_s)).freeze
52
+ self.portal_options = portal_options.merge(portal_key.to_s => options.dup.merge(name: name.to_s).freeze).freeze
48
53
 
49
54
  define_method "#{name.to_s.singularize}_ids" do
50
55
  association(name).map(&:id)
@@ -94,11 +94,17 @@ module FmRest
94
94
  end
95
95
 
96
96
  def _fmrest_define_attribute(from, to)
97
+ if existing_method = ((method_defined?(from) || private_method_defined?(from)) && from) ||
98
+ ((method_defined?("#{from}=") || private_method_defined?("#{from}=")) && "#{from}=")
99
+
100
+ raise ArgumentError, "You tried to define an attribute named `#{from}' on `#{name}', but this will generate a instance method `#{existing_method}', which is already defined by FmRest::Layout."
101
+ end
102
+
97
103
  # We use a setter here instead of injecting the hash key/value pair
98
104
  # directly with #[]= so that we don't change the mapped_attributes
99
105
  # hash on the parent class. The resulting hash is frozen for the
100
106
  # same reason.
101
- self.mapped_attributes = mapped_attributes.merge(from => to).freeze
107
+ self.mapped_attributes = mapped_attributes.merge(from => to.to_s).freeze
102
108
 
103
109
  _fmrest_attribute_methods_container.module_eval do
104
110
  define_method(from) do
@@ -6,8 +6,6 @@ module FmRest
6
6
  module GlobalFields
7
7
  extend ::ActiveSupport::Concern
8
8
 
9
- FULLY_QUALIFIED_FIELD_NAME_MATCHER = /\A[^:]+::[^:]+\Z/.freeze
10
-
11
9
  class_methods do
12
10
  def set_globals(values_hash)
13
11
  connection.patch(FmRest::V1.globals_path, {
@@ -26,7 +24,7 @@ module FmRest
26
24
  next
27
25
  end
28
26
 
29
- unless FULLY_QUALIFIED_FIELD_NAME_MATCHER === k.to_s
27
+ unless V1.is_fully_qualified?(k.to_s)
30
28
  raise ArgumentError, "global fields must be given in fully qualified format (table name::field name)"
31
29
  end
32
30
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FmRest
4
+ module Spyke
5
+ module Model
6
+ module Rescuable
7
+ extend ::ActiveSupport::Concern
8
+
9
+ include ::ActiveSupport::Rescuable
10
+
11
+ class_methods do
12
+ def request(*args)
13
+ begin
14
+ super
15
+ rescue => e
16
+ rescue_with_handler(e) || raise
17
+ end
18
+ end
19
+
20
+ def rescue_account_error(with: nil, &block)
21
+ rescue_from APIError::AccountError, with: with, &block
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -26,7 +26,7 @@ module FmRest
26
26
  def serialize_for_portal(portal)
27
27
  params =
28
28
  changed_params.except(:__record_id).transform_keys do |key|
29
- "#{portal.attribute_prefix}::#{key}"
29
+ V1.is_fully_qualified?(key) ? key : "#{portal.attribute_prefix}::#{key}"
30
30
  end
31
31
 
32
32
  params[:recordId] = __record_id.to_s if __record_id
@@ -28,6 +28,8 @@ module FmRest
28
28
  include GlobalFields
29
29
  include Http
30
30
  include Auth
31
+
32
+ autoload :Rescuable, "fmrest/spyke/model/rescuable"
31
33
  end
32
34
  end
33
35
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FmRest
4
+ module Spyke
5
+ class PortalBuilder < ::Spyke::Associations::Builder
6
+ attr_reader :options
7
+
8
+ def klass
9
+ begin
10
+ super
11
+ rescue NameError => e
12
+ ::FmRest::Layout
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -195,8 +195,8 @@ module FmRest
195
195
  out
196
196
  end
197
197
 
198
- # Extracts `recordId` and strips the `"PortalName::"` field prefix for each
199
- # portal
198
+ # Extracts `recordId` and strips the `"tableName::"` field qualifier for
199
+ # each portal
200
200
  #
201
201
  # Sample `json_portal_data`:
202
202
  #
@@ -210,19 +210,33 @@ module FmRest
210
210
  # @return [Hash] the portal data in Spyke format
211
211
  def prepare_portal_data(json_portal_data)
212
212
  json_portal_data.each_with_object({}) do |(portal_name, portal_records), out|
213
+
213
214
  portal_options = @model.portal_options[portal_name.to_s] || {}
215
+ portal_builder = @model.associations[portal_options[:name].to_sym]
216
+ portal_class = portal_builder && portal_builder.klass
217
+ portal_attributes = (portal_class && portal_class.mapped_attributes.values) || []
214
218
 
215
219
  out[portal_name] =
216
220
  portal_records.map do |portal_fields|
217
221
  attributes = { __record_id: portal_fields[:recordId] }
218
222
  attributes[:__mod_id] = portal_fields[:modId] if portal_fields[:modId]
219
223
 
220
- prefix = portal_options[:attribute_prefix] || portal_name
221
- prefix_matcher = /\A#{prefix}::/
224
+ qualifier = portal_options[:attribute_prefix] || portal_name
225
+ qualifier_matcher = /\A#{qualifier}::/
222
226
 
223
227
  portal_fields.each do |k, v|
224
228
  next if :recordId == k || :modId == k
225
- attributes[k.to_s.gsub(prefix_matcher, "").to_sym] = v
229
+
230
+ stripped_field_name = k.to_s.gsub(qualifier_matcher, "")
231
+
232
+ # Only use the non-qualified attribute name if it was defined
233
+ # that way on the portal model, otherwise default to the fully
234
+ # qualified name
235
+ if portal_attributes.include?(stripped_field_name)
236
+ attributes[stripped_field_name.to_sym] = v
237
+ else
238
+ attributes[k.to_sym] = v
239
+ end
226
240
  end
227
241
 
228
242
  attributes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fmrest-spyke
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.18.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Carbajal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-14 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fmrest-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.16.0
19
+ version: 0.18.0.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.16.0
26
+ version: 0.18.0.rc1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: spyke
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -64,9 +64,11 @@ files:
64
64
  - lib/fmrest/spyke/model/http.rb
65
65
  - lib/fmrest/spyke/model/orm.rb
66
66
  - lib/fmrest/spyke/model/record_id.rb
67
+ - lib/fmrest/spyke/model/rescuable.rb
67
68
  - lib/fmrest/spyke/model/serialization.rb
68
69
  - lib/fmrest/spyke/model/uri.rb
69
70
  - lib/fmrest/spyke/portal.rb
71
+ - lib/fmrest/spyke/portal_builder.rb
70
72
  - lib/fmrest/spyke/relation.rb
71
73
  - lib/fmrest/spyke/spyke_formatter.rb
72
74
  - lib/fmrest/spyke/validation_error.rb
@@ -85,11 +87,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
87
  version: '0'
86
88
  required_rubygems_version: !ruby/object:Gem::Requirement
87
89
  requirements:
88
- - - ">="
90
+ - - ">"
89
91
  - !ruby/object:Gem::Version
90
- version: '0'
92
+ version: 1.3.1
91
93
  requirements: []
92
- rubygems_version: 3.0.6
94
+ rubygems_version: 3.2.3
93
95
  signing_key:
94
96
  specification_version: 4
95
97
  summary: FileMaker Data API ORM client library