fmrest-core 0.16.0 → 0.18.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02e8b51abdf2fc9a5b7df29cdad8d5acef8f04b5c6167d7ff94040a716117449
4
- data.tar.gz: 6a78ce64f39cd5e41910114eb22948eb5c4f26d26a892ab2b09c0bdcaa6c3fb3
3
+ metadata.gz: 27b42f06b36c99b967649c4257ea1b4df30e70f3e30efce3c1a82e74b511a368
4
+ data.tar.gz: 46cade48892b6cedca8f18fc40ccb88c3da2e1893bb09df020fcc0847782ede4
5
5
  SHA512:
6
- metadata.gz: 3bdd91f7e25de359a7a28746d4c7be4eae7a684b63d909eacae6dd48a7053666386ff6dd3b87895aaf315e6a6b923aadcf0b8713aefa696fadbef25656dbae3b
7
- data.tar.gz: 37d292af43be4a6a42b4f4044d3078ec40edff189622e3d4390d4a9ec65e34b0b910b2542ca4293ea3d3925eea8c84f1c944a5d7c7cc22f47bf989113b1017d7
6
+ metadata.gz: 79335b4c02f13f90dde6052998cd2f11e29ff4da10a6622b8d789f7ec443828afea5a869c10b7f40c79f10cf4b16b88f69691eab0e7605a6372b69ec24b07f6b
7
+ data.tar.gz: 7f3a11e55de51d92680a76812a63b6e3e2fa8ecbc3ac37949af7a365c67911d7867625781c4f5947c6079d1a99f31a499a354a3999d6d3415e5a08a982cc08a1
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 |
@@ -18,6 +18,7 @@ module FmRest
18
18
  database
19
19
  username
20
20
  password
21
+ fmid_token
21
22
  token
22
23
  token_store
23
24
  autologin
@@ -69,13 +70,12 @@ module FmRest
69
70
 
70
71
  PROPERTIES.each do |p|
71
72
  define_method(p) do
72
- get(p)
73
+ get_eval(p)
73
74
  end
74
75
 
75
76
  define_method("#{p}!") do
76
- r = get(p)
77
- raise MissingSetting, "Missing required setting: `#{p}'" if r.nil?
78
- r
77
+ raise MissingSetting, "Missing required setting: `#{p}'" if get(p).nil?
78
+ get_eval(p)
79
79
  end
80
80
 
81
81
  define_method("#{p}?") do
@@ -85,7 +85,7 @@ module FmRest
85
85
 
86
86
  def [](key)
87
87
  raise ArgumentError, "Unknown setting `#{key}'" unless PROPERTIES.include?(key.to_sym)
88
- get(key)
88
+ get_eval(key)
89
89
  end
90
90
 
91
91
  def to_h
@@ -104,13 +104,18 @@ module FmRest
104
104
  missing = REQUIRED.select { |r| get(r).nil? }.map { |m| "`#{m}'" }
105
105
  raise MissingSetting, "Missing required setting(s): #{missing.join(', ')}" unless missing.empty?
106
106
 
107
- unless username? || token?
108
- raise MissingSetting, "A minimum of `username' or `token' are required to be able to establish a connection"
107
+ unless username? || fmid_token? || token?
108
+ raise MissingSetting, "A minimum of `username', `fmid_token' or `token' are required to be able to establish a connection"
109
109
  end
110
110
  end
111
111
 
112
112
  private
113
113
 
114
+ def get_eval(key)
115
+ c = get(key)
116
+ c.kind_of?(Proc) ? c.call : c
117
+ end
118
+
114
119
  def get(key)
115
120
  return @settings[key.to_sym] if @settings.has_key?(key.to_sym)
116
121
  return @settings[key.to_s] if @settings.has_key?(key.to_s)
@@ -3,6 +3,8 @@
3
3
  module FmRest
4
4
  module V1
5
5
  module Auth
6
+ ACCESS_TOKEN_HEADER = "X-FM-Data-Access-Token"
7
+
6
8
  # Requests a token through basic auth
7
9
  #
8
10
  # @param connection [Faraday] the auth connection to use for
@@ -23,7 +25,7 @@ module FmRest
23
25
  # @raise [FmRest::APIError::AccountError] if authentication failed
24
26
  def request_auth_token!(connection = FmRest.V1.auth_connection)
25
27
  resp = connection.post(V1.session_path)
26
- resp.body["response"]["token"]
28
+ resp.headers[ACCESS_TOKEN_HEADER] || resp.body["response"]["token"]
27
29
  end
28
30
  end
29
31
  end
@@ -9,6 +9,7 @@ module FmRest
9
9
  DATABASES_PATH = "#{BASE_PATH}/databases"
10
10
 
11
11
  AUTH_HEADERS = { "Content-Type" => "application/json" }.freeze
12
+ CLARIS_ID_HTTP_AUTH_TYPE = "FMID"
12
13
 
13
14
  # Builds a complete DAPI Faraday connection with middleware already
14
15
  # configured to handle authentication, JSON parsing, logging and DAPI
@@ -58,7 +59,11 @@ module FmRest
58
59
  base_connection(settings, { headers: AUTH_HEADERS }) do |conn|
59
60
  conn.use RaiseErrors
60
61
 
61
- conn.basic_auth settings.username!, settings.password!
62
+ if settings.fmid_token?
63
+ conn.authorization CLARIS_ID_HTTP_AUTH_TYPE, settings.fmid_token
64
+ else
65
+ conn.basic_auth settings.username!, settings.password!
66
+ end
62
67
 
63
68
  if settings.log
64
69
  conn.response :logger, FmRest.logger, bodies: true, headers: true, log_level: settings.log_level
@@ -100,7 +100,13 @@ module FmRest
100
100
  # Strip the host part to just the hostname (i.e. no scheme or port)
101
101
  host = @settings.host!
102
102
  host = URI(host).hostname if host =~ /\Ahttps?:\/\//
103
- "#{host}:#{@settings.database!}:#{@settings.username!}"
103
+ identity_segment = if fmid_token = @settings.fmid_token
104
+ require "digest"
105
+ Digest::SHA256.hexdigest(fmid_token)
106
+ else
107
+ @settings.username!
108
+ end
109
+ "#{host}:#{@settings.database!}:#{identity_segment}"
104
110
  end
105
111
  end
106
112
 
@@ -8,6 +8,8 @@ module FmRest
8
8
  # See https://help.claris.com/en/pro-help/content/finding-text.html
9
9
  FM_FIND_OPERATORS_RE = /([@\*#\?!=<>"])/
10
10
 
11
+ FULLY_QUALIFIED_FIELD_NAME_MATCHER = /\A[^:]+::[^:]+\Z/.freeze
12
+
11
13
  # Converts custom script options to a hash with the Data API's expected
12
14
  # JSON script format.
13
15
  #
@@ -104,6 +106,18 @@ module FmRest
104
106
  s.gsub(FM_FIND_OPERATORS_RE, "\\\\\\1")
105
107
  end
106
108
 
109
+ # Returns whether the given FileMaker field name is a fully-qualified
110
+ # name. In other words, whether it contains the string "::".
111
+ #
112
+ # Note that this is a simple naive check which doesn't account for
113
+ # invalid field names.
114
+ #
115
+ # @param field_name [String] The field name to test
116
+ # @return [Boolean] Whether the field is a FQN
117
+ def is_fully_qualified?(field_name)
118
+ FULLY_QUALIFIED_FIELD_NAME_MATCHER === field_name.to_s
119
+ end
120
+
107
121
  private
108
122
 
109
123
  def convert_script_arguments(script_arguments, suffix = nil)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FmRest
4
- VERSION = "0.16.0"
4
+ VERSION = "0.18.0.rc1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fmrest-core
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: exe
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: faraday
@@ -104,11 +104,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
104
  version: '0'
105
105
  required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  requirements:
107
- - - ">="
107
+ - - ">"
108
108
  - !ruby/object:Gem::Version
109
- version: '0'
109
+ version: 1.3.1
110
110
  requirements: []
111
- rubygems_version: 3.0.6
111
+ rubygems_version: 3.2.3
112
112
  signing_key:
113
113
  specification_version: 4
114
114
  summary: FileMaker Data API client using Faraday, core library