fmrest-core 0.18.0.rc3 → 0.20.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +62 -91
- data/lib/fmrest/connection_settings.rb +6 -1
- data/lib/fmrest/token_store/memory.rb +4 -4
- data/lib/fmrest/token_store/null.rb +4 -13
- data/lib/fmrest/token_store/short_memory.rb +29 -0
- data/lib/fmrest/token_store.rb +24 -0
- data/lib/fmrest/v1/connection.rb +22 -3
- data/lib/fmrest/v1/raise_errors.rb +11 -0
- data/lib/fmrest/v1/token_session.rb +6 -19
- data/lib/fmrest/v1/type_coercer.rb +1 -1
- data/lib/fmrest/version.rb +1 -1
- data/lib/fmrest.rb +7 -0
- metadata +5 -5
- data/lib/fmrest-spyke.rb +0 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e65b80ac2c60bb8c3ba6c356c55c720dec5ed59ee8afdb94e18c025bf327a3fe
         | 
| 4 | 
            +
              data.tar.gz: c144aaafd1b237bd67fdbd90a99423dae90638555703dd4b6b2eb05dbc7f687e
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ff81e141c358ae0f4c95d1d4b82a58b717caa550239b358345d6fcc0a1c3e9af1df3dee9e5c014e2163a67a73fbf83ab4d2bd1df674a0e16261f3c57c3e6a826
         | 
| 7 | 
            +
              data.tar.gz: 0ed42801b904cc7967dab1c5990aa8389d07727b7a5d3234db6efb40b61d5c3312fbe1e45522bb05058fefc237571456546c209b3f8f752ff0c4a9026770f181
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,13 @@ | |
| 1 1 | 
             
            ## Changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ### 0.20.0
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * Forward proxy options to AWS Client when using `fmrest-cloud` gem
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### 0.19.0
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            * Added native support for FileMaker Cloud through the `fmrest-cloud` gem
         | 
| 10 | 
            +
             | 
| 3 11 | 
             
            ### 0.18.0
         | 
| 4 12 |  | 
| 5 13 | 
             
            * Better support for portals with mismatching field qualifiers
         | 
| @@ -7,6 +15,9 @@ | |
| 7 15 | 
             
            * Defining an attribute on a model that would collide with an existing method
         | 
| 8 16 | 
             
              now raises an error
         | 
| 9 17 | 
             
            * Cleared Faraday deprecation messages on authentication methods
         | 
| 18 | 
            +
            * Handle FileMaker Cloud case where HTTP 401 Unauthorized with content-type
         | 
| 19 | 
            +
              text/html is returned after token expiry
         | 
| 20 | 
            +
            * Add retry option to Rescuable mixin
         | 
| 10 21 | 
             
            * Added fmrest-ruby/VERSION to User-Agent headers
         | 
| 11 22 |  | 
| 12 23 | 
             
            ### 0.17.1
         | 
    
        data/README.md
    CHANGED
    
    | @@ -5,14 +5,27 @@ | |
| 5 5 | 
             
            [](https://rubydoc.info/github/beezwax/fmrest-ruby)
         | 
| 6 6 |  | 
| 7 7 | 
             
            A Ruby client for
         | 
| 8 | 
            -
            [FileMaker | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 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)
         | 
| 16 29 |  | 
| 17 30 | 
             
            ## Gems
         | 
| 18 31 |  | 
| @@ -20,28 +33,26 @@ The `fmrest` gem is a wrapper for two other gems: | |
| 20 33 |  | 
| 21 34 | 
             
            * `fmrest-spyke`, providing an ActiveRecord-like ORM library built on top
         | 
| 22 35 | 
             
              of `fmrest-core` and [Spyke](https://github.com/balvig/spyke).
         | 
| 23 | 
            -
            * `fmrest-core`, providing the core | 
| 36 | 
            +
            * `fmrest-core`, providing the core
         | 
| 37 | 
            +
              [Faraday](https://github.com/lostisland/faraday) connection builder, session
         | 
| 24 38 | 
             
              management, and other core utilities.
         | 
| 25 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 | 
            +
             | 
| 26 44 | 
             
            ## Installation
         | 
| 27 45 |  | 
| 28 | 
            -
             | 
| 46 | 
            +
            In your Gemfile:
         | 
| 29 47 |  | 
| 30 48 | 
             
            ```ruby
         | 
| 31 49 | 
             
            gem 'fmrest'
         | 
| 32 | 
            -
            ```
         | 
| 33 | 
            -
             | 
| 34 | 
            -
            Or if you just want to use the Faraday connection without the ORM features:
         | 
| 35 50 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
            gem 'fmrest- | 
| 51 | 
            +
            # Optional: if your files are hosted on FileMaker Cloud
         | 
| 52 | 
            +
            gem 'fmrest-cloud'
         | 
| 38 53 | 
             
            ```
         | 
| 39 54 |  | 
| 40 | 
            -
            ## Simple  | 
| 41 | 
            -
             | 
| 42 | 
            -
            ### ORM example
         | 
| 43 | 
            -
             | 
| 44 | 
            -
            Most people would want to use the ORM features:
         | 
| 55 | 
            +
            ## Simple example
         | 
| 45 56 |  | 
| 46 57 | 
             
            ```ruby
         | 
| 47 58 | 
             
            # A Layout model connecting to the "Honeybees Web" FileMaker layout
         | 
| @@ -90,32 +101,9 @@ bee.tasks.build(urgency: "Today") | |
| 90 101 | 
             
            bee.save
         | 
| 91 102 | 
             
            ```
         | 
| 92 103 |  | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
            Data API interaction and just want a lightweight solution) you can simply use
         | 
| 97 | 
            -
            the Faraday connection provided by `fmrest-core`:
         | 
| 98 | 
            -
             | 
| 99 | 
            -
            ```ruby
         | 
| 100 | 
            -
            connection = FmRest::V1.build_connection(
         | 
| 101 | 
            -
              host:     "…",
         | 
| 102 | 
            -
              database: "…",
         | 
| 103 | 
            -
              username: "…",
         | 
| 104 | 
            -
              password: "…"
         | 
| 105 | 
            -
            )
         | 
| 106 | 
            -
             | 
| 107 | 
            -
            # Get all records (as parsed JSON)
         | 
| 108 | 
            -
            connection.get("layouts/FancyLayout/records")
         | 
| 109 | 
            -
             | 
| 110 | 
            -
            # Create new record
         | 
| 111 | 
            -
            connection.post do |req|
         | 
| 112 | 
            -
              req.url "layouts/FancyLayout/records"
         | 
| 113 | 
            -
             | 
| 114 | 
            -
              # You can just pass a hash for the JSON body
         | 
| 115 | 
            -
              req.body = { … }
         | 
| 116 | 
            -
            end
         | 
| 117 | 
            -
            ```
         | 
| 118 | 
            -
             | 
| 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`.
         | 
| 119 107 | 
             
            See the [main document on using the base
         | 
| 120 108 | 
             
            connection](docs/BaseConnectionUsage.md) for more.
         | 
| 121 109 |  | 
| @@ -125,10 +113,6 @@ The minimum required connection settings are `:host`, `:database`, `:username` | |
| 125 113 | 
             
            and `:password`, but fmrest-ruby has many other options you can pass when
         | 
| 126 114 | 
             
            setting up a connection (see [full list](#full-list-of-available-options) below).
         | 
| 127 115 |  | 
| 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 | 
            -
             | 
| 132 116 | 
             
            `:ssl` and `:proxy` are forwarded to the underlying
         | 
| 133 117 | 
             
            [Faraday](https://github.com/lostisland/faraday) connection. You can use this
         | 
| 134 118 | 
             
            to, for instance, disable SSL verification:
         | 
| @@ -153,9 +137,8 @@ Option              | Description                                | Format | |
| 153 137 | 
             
            `:username`         | A Data API-ready account                   | String                      | None
         | 
| 154 138 | 
             
            `:password`         | Your password                              | String                      | None
         | 
| 155 139 | 
             
            `:account_name`     | Alias of `:username`                       | String                      | None
         | 
| 156 | 
            -
            `:fmid_token`       | Claris ID token (only needed for FileMaker Cloud) | String               | None
         | 
| 157 140 | 
             
            `:ssl`              | SSL options to be forwarded to Faraday     | Faraday SSL options         | None
         | 
| 158 | 
            -
            `:proxy`            | Proxy  | 
| 141 | 
            +
            `:proxy`            | Proxy URI e.g. `http://username:password@proxy.host:5000` | String / URI | None
         | 
| 159 142 | 
             
            `:log`              | Log JSON responses to STDOUT               | Boolean                     | `false`
         | 
| 160 143 | 
             
            `:log_level`        | Which log level to log into                | Values accepted by `Logger#level=` | `:debug`
         | 
| 161 144 | 
             
            `:coerce_dates`     | See section on [date fields](#date-fields-and-timezones) | Boolean \| `:hybrid` \| `:full` | `false`
         | 
| @@ -165,6 +148,11 @@ Option              | Description                                | Format | |
| 165 148 | 
             
            `:timezone`         | The timezone for the FM server             | `:local` \| `:utc` \| `nil` | `nil`
         | 
| 166 149 | 
             
            `:autologin`        | Whether to automatically start Data API sessions | Boolean               | `true`
         | 
| 167 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
         | 
| 168 156 |  | 
| 169 157 | 
             
            ### Default connection settings
         | 
| 170 158 |  | 
| @@ -207,7 +195,7 @@ See the [main document on date fields](docs/DateFields.md) for more info. | |
| 207 195 | 
             
            ## ActiveRecord-like ORM (fmrest-spyke)
         | 
| 208 196 |  | 
| 209 197 | 
             
            [Spyke](https://github.com/balvig/spyke) is an ActiveRecord-like gem for
         | 
| 210 | 
            -
            building REST ORM models. fmrest-ruby  | 
| 198 | 
            +
            building REST ORM models. fmrest-ruby uses it to build its ORM features,
         | 
| 211 199 | 
             
            bundled in the `fmrest-spyke` gem (already included if you're using the
         | 
| 212 200 | 
             
            `fmrest` gem).
         | 
| 213 201 |  | 
| @@ -265,39 +253,33 @@ class Honeybee < FmRest::Layout | |
| 265 253 | 
             
            end
         | 
| 266 254 | 
             
            ```
         | 
| 267 255 |  | 
| 268 | 
            -
             | 
| 269 | 
            -
            connection settings, so you don't have to worry about setting that up.
         | 
| 270 | 
            -
             | 
| 271 | 
            -
            Note that these settings are inheritable, so you could create a base class that
         | 
| 256 | 
            +
            These settings are class-inheritable, so you could create a base class that
         | 
| 272 257 | 
             
            does the initial connection setup and then inherit from it in models using that
         | 
| 273 258 | 
             
            same connection. E.g.:
         | 
| 274 259 |  | 
| 275 260 | 
             
            ```ruby
         | 
| 276 | 
            -
            class  | 
| 261 | 
            +
            class ApplicationFmLayout < FmRest::Layout
         | 
| 277 262 | 
             
              self.fmrest_config = { host: "…", database: "…", … }
         | 
| 278 263 | 
             
            end
         | 
| 279 264 |  | 
| 280 | 
            -
            class Honeybee <  | 
| 281 | 
            -
              # This model will use the same connection as  | 
| 265 | 
            +
            class Honeybee < ApplicationFmLayout
         | 
| 266 | 
            +
              # This model will use the same connection as ApplicationFmLayout
         | 
| 282 267 | 
             
            end
         | 
| 283 268 | 
             
            ```
         | 
| 284 269 |  | 
| 285 | 
            -
             | 
| 270 | 
            +
            If `fmrest_config` is not set, your model will try to use
         | 
| 286 271 | 
             
            `FmRest.default_connection_settings` instead.
         | 
| 287 272 |  | 
| 288 273 | 
             
            #### Connection settings overlays
         | 
| 289 274 |  | 
| 290 275 | 
             
            There may be cases where you want to use a different set of connection settings
         | 
| 291 | 
            -
            depending on context,  | 
| 292 | 
            -
             | 
| 293 | 
            -
             | 
| 294 | 
            -
             | 
| 295 | 
            -
            the model in one context would also change it in all other contexts, leading to
         | 
| 296 | 
            -
            security issues.
         | 
| 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.
         | 
| 297 280 |  | 
| 298 | 
            -
            To solve this scenario, fmrest-ruby provides a way of defining thread-local | 
| 299 | 
            -
            reversible connection settings overlays through
         | 
| 300 | 
            -
            `.fmrest_config_overlay=`.
         | 
| 281 | 
            +
            To solve this scenario, fmrest-ruby provides a way of defining thread-local,
         | 
| 282 | 
            +
            reversible connection settings overlays through `.fmrest_config_overlay=`.
         | 
| 301 283 |  | 
| 302 284 | 
             
            See the [main document on connection setting overlays](docs/ConfigOverlays.md)
         | 
| 303 285 | 
             
            for details on how it works.
         | 
| @@ -461,12 +443,10 @@ for details. | |
| 461 443 |  | 
| 462 444 | 
             
            ### Rescuable mixin
         | 
| 463 445 |  | 
| 464 | 
            -
            Sometimes you may want to handle Data API errors at the model level. For
         | 
| 465 | 
            -
             | 
| 466 | 
            -
             | 
| 467 | 
            -
             | 
| 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.
         | 
| 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.
         | 
| 470 450 |  | 
| 471 451 | 
             
            ```ruby
         | 
| 472 452 | 
             
            class BeeBase < FmRest::Layout
         | 
| @@ -474,9 +454,6 @@ class BeeBase < FmRest::Layout | |
| 474 454 |  | 
| 475 455 | 
             
              rescue_from FmRest::APIError::SystemError, with: :notify_admin_of_system_error
         | 
| 476 456 |  | 
| 477 | 
            -
              # Shorthand for rescue_with FmRest::APIError::AccountError, ...
         | 
| 478 | 
            -
              rescue_account_error { ClarisIDTokenManager.expire_token }
         | 
| 479 | 
            -
             | 
| 480 457 | 
             
              def self.notify_admin_of_system_error(e)
         | 
| 481 458 | 
             
                # Shoot an email to the FM admin...
         | 
| 482 459 | 
             
              end
         | 
| @@ -549,14 +526,14 @@ Read about unexpected scenarios in the [gotchas doc](docs/Gotchas.md). | |
| 549 526 |  | 
| 550 527 | 
             
            ## API implementation completeness table
         | 
| 551 528 |  | 
| 552 | 
            -
            FM Data API reference: https:// | 
| 529 | 
            +
            FM Data API reference: https://help.claris.com/en/data-api-guide/
         | 
| 553 530 |  | 
| 554 | 
            -
            | FM  | 
| 531 | 
            +
            | FM 19 Data API feature              | Supported by basic connection | Supported by FmRest::Layout |
         | 
| 555 532 | 
             
            |-------------------------------------|-------------------------------|-----------------------------|
         | 
| 556 533 | 
             
            | Log in using HTTP Basic Auth        | Yes                           | Yes                         |
         | 
| 557 534 | 
             
            | Log in using OAuth                  | No                            | No                          |
         | 
| 558 535 | 
             
            | Log in to an external data source   | No                            | No                          |
         | 
| 559 | 
            -
            | Log in using Claris ID account | 
| 536 | 
            +
            | Log in using Claris ID account (FileMaker Cloud) | Yes              | Yes                         |
         | 
| 560 537 | 
             
            | Log out                             | Yes                           | Yes                         |
         | 
| 561 538 | 
             
            | Get product information             | Manual*                       | No                          |
         | 
| 562 539 | 
             
            | Get database names                  | Manual*                       | No                          |
         | 
| @@ -592,9 +569,9 @@ the following Ruby implementations: | |
| 592 569 | 
             
            ## Gem development
         | 
| 593 570 |  | 
| 594 571 | 
             
            After checking out the repo, run `bin/setup` to install dependencies. Then, run
         | 
| 595 | 
            -
            ` | 
| 596 | 
            -
            prompt that will allow you to experiment (it will auto-load all | 
| 597 | 
            -
            spec/fixtures).
         | 
| 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).
         | 
| 598 575 |  | 
| 599 576 | 
             
            To install all gems onto your local machine, run
         | 
| 600 577 | 
             
            `bundle exec rake all:install`. To release a new version, update the version
         | 
| @@ -602,12 +579,6 @@ number in `lib/fmrest/version.rb`, and then run `bundle exec rake all:release`, | |
| 602 579 | 
             
            which will create a git tag for the version, push git commits and tags, and
         | 
| 603 580 | 
             
            push the `.gem` files to [rubygems.org](https://rubygems.org).
         | 
| 604 581 |  | 
| 605 | 
            -
            ## License
         | 
| 606 | 
            -
             | 
| 607 | 
            -
            The gem is available as open source under the terms of the
         | 
| 608 | 
            -
            [MIT License](https://opensource.org/licenses/MIT).
         | 
| 609 | 
            -
            See [LICENSE.txt](LICENSE.txt).
         | 
| 610 | 
            -
             | 
| 611 582 | 
             
            ## Disclaimer
         | 
| 612 583 |  | 
| 613 584 | 
             
            This project is not sponsored by or otherwise affiliated with Claris
         | 
| @@ -31,6 +31,10 @@ module FmRest | |
| 31 31 | 
             
                  timestamp_format
         | 
| 32 32 | 
             
                  time_format
         | 
| 33 33 | 
             
                  timezone
         | 
| 34 | 
            +
                  cognito_client_id
         | 
| 35 | 
            +
                  cognito_pool_id
         | 
| 36 | 
            +
                  aws_region
         | 
| 37 | 
            +
                  cloud
         | 
| 34 38 | 
             
                ).freeze
         | 
| 35 39 |  | 
| 36 40 | 
             
                # NOTE: password intentionally left non-required since it's only really
         | 
| @@ -51,7 +55,8 @@ module FmRest | |
| 51 55 | 
             
                  date_format:      DEFAULT_DATE_FORMAT,
         | 
| 52 56 | 
             
                  time_format:      DEFAULT_TIME_FORMAT,
         | 
| 53 57 | 
             
                  timestamp_format: DEFAULT_TIMESTAMP_FORMAT,
         | 
| 54 | 
            -
                  coerce_dates:     false
         | 
| 58 | 
            +
                  coerce_dates:     false,
         | 
| 59 | 
            +
                  cloud:            :auto,
         | 
| 55 60 | 
             
                }.freeze
         | 
| 56 61 |  | 
| 57 62 | 
             
                def self.wrap(settings, skip_validation: false)
         | 
| @@ -7,19 +7,19 @@ module FmRest | |
| 7 7 | 
             
                class Memory < Base
         | 
| 8 8 | 
             
                  def initialize(*args)
         | 
| 9 9 | 
             
                    super
         | 
| 10 | 
            -
                     | 
| 10 | 
            +
                    @@tokens ||= {}
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 13 | 
             
                  def delete(key)
         | 
| 14 | 
            -
                     | 
| 14 | 
            +
                    @@tokens.delete(key)
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 17 | 
             
                  def load(key)
         | 
| 18 | 
            -
                     | 
| 18 | 
            +
                    @@tokens[key]
         | 
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 21 | 
             
                  def store(key, value)
         | 
| 22 | 
            -
                     | 
| 22 | 
            +
                    @@tokens[key] = value
         | 
| 23 23 | 
             
                  end
         | 
| 24 24 | 
             
                end
         | 
| 25 25 | 
             
              end
         | 
| @@ -1,20 +1,11 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "singleton"
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module FmRest
         | 
| 6 4 | 
             
              module TokenStore
         | 
| 7 | 
            -
                 | 
| 8 | 
            -
                   | 
| 9 | 
            -
             | 
| 10 | 
            -
                  def  | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  def load(key)
         | 
| 14 | 
            -
                  end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                  def store(key, value)
         | 
| 17 | 
            -
                  end
         | 
| 5 | 
            +
                class Null < Base
         | 
| 6 | 
            +
                  def delete(key); end
         | 
| 7 | 
            +
                  def load(key); end
         | 
| 8 | 
            +
                  def store(key, value); end
         | 
| 18 9 | 
             
                end
         | 
| 19 10 | 
             
              end
         | 
| 20 11 | 
             
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "fmrest/token_store/base"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module FmRest
         | 
| 6 | 
            +
              module TokenStore
         | 
| 7 | 
            +
                # Similar to Memory token store, but using instance vars instead of class
         | 
| 8 | 
            +
                # vars. Mainly useful for specs, where we want to scope token persistance
         | 
| 9 | 
            +
                # to a spec's context only
         | 
| 10 | 
            +
                class ShortMemory < Base
         | 
| 11 | 
            +
                  def initialize(*args)
         | 
| 12 | 
            +
                    super
         | 
| 13 | 
            +
                    @tokens ||= {}
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def delete(key)
         | 
| 17 | 
            +
                    @tokens.delete(key)
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def load(key)
         | 
| 21 | 
            +
                    @tokens[key]
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def store(key, value)
         | 
| 25 | 
            +
                    @tokens[key] = value
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
    
        data/lib/fmrest/token_store.rb
    CHANGED
    
    | @@ -8,5 +8,29 @@ module FmRest | |
| 8 8 | 
             
                autoload :ActiveRecord, "fmrest/token_store/active_record"
         | 
| 9 9 | 
             
                autoload :Moneta,       "fmrest/token_store/moneta"
         | 
| 10 10 | 
             
                autoload :Redis,        "fmrest/token_store/redis"
         | 
| 11 | 
            +
                autoload :ShortMemory,  "fmrest/token_store/short_memory"
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                TOKEN_STORE_INTERFACE = [:load, :store, :delete].freeze
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                private
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def token_store
         | 
| 18 | 
            +
                  @token_store ||=
         | 
| 19 | 
            +
                    if TOKEN_STORE_INTERFACE.all? { |method| token_store_option.respond_to?(method) }
         | 
| 20 | 
            +
                      token_store_option
         | 
| 21 | 
            +
                    elsif token_store_option.kind_of?(Class)
         | 
| 22 | 
            +
                      if token_store_option.respond_to?(:instance)
         | 
| 23 | 
            +
                        token_store_option.instance
         | 
| 24 | 
            +
                      else
         | 
| 25 | 
            +
                        token_store_option.new
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      FmRest::TokenStore::Memory.new
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def token_store_option
         | 
| 33 | 
            +
                  raise NotImplementedError
         | 
| 34 | 
            +
                end
         | 
| 11 35 | 
             
              end
         | 
| 12 36 | 
             
            end
         | 
    
        data/lib/fmrest/v1/connection.rb
    CHANGED
    
    | @@ -12,6 +12,8 @@ module FmRest | |
| 12 12 | 
             
                  AUTH_CONNECTION_HEADERS = DEFAULT_HEADERS.merge("Content-Type" => "application/json").freeze
         | 
| 13 13 | 
             
                  CLARIS_ID_HTTP_AUTH_TYPE = "FMID"
         | 
| 14 14 |  | 
| 15 | 
            +
                  FILEMAKER_CLOUD_HOST_MATCHER = /\.filemaker-cloud\.com\Z/.freeze
         | 
| 16 | 
            +
             | 
| 15 17 | 
             
                  # Builds a complete DAPI Faraday connection with middleware already
         | 
| 16 18 | 
             
                  # configured to handle authentication, JSON parsing, logging and DAPI
         | 
| 17 19 | 
             
                  # error handling. A block can be optionally given for additional
         | 
| @@ -39,7 +41,7 @@ module FmRest | |
| 39 41 | 
             
                        yield conn, settings
         | 
| 40 42 | 
             
                      else
         | 
| 41 43 | 
             
                        conn.use TypeCoercer, settings
         | 
| 42 | 
            -
                        conn.response :json
         | 
| 44 | 
            +
                        conn.response :json, content_type: /\bjson$/
         | 
| 43 45 | 
             
                      end
         | 
| 44 46 |  | 
| 45 47 | 
             
                      if settings.log
         | 
| @@ -57,11 +59,18 @@ module FmRest | |
| 57 59 | 
             
                  def auth_connection(settings = FmRest.default_connection_settings)
         | 
| 58 60 | 
             
                    settings = ConnectionSettings.wrap(settings)
         | 
| 59 61 |  | 
| 62 | 
            +
                    if is_cloud_host = cloud_host?(settings)
         | 
| 63 | 
            +
                      FmRest.require_cloud_support
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 60 66 | 
             
                    base_connection(settings, headers: AUTH_CONNECTION_HEADERS) do |conn|
         | 
| 67 | 
            +
                      conn.use Cloud::AuthErrorHandler, settings if is_cloud_host
         | 
| 61 68 | 
             
                      conn.use RaiseErrors
         | 
| 62 69 |  | 
| 63 70 | 
             
                      if settings.fmid_token?
         | 
| 64 | 
            -
                        conn.request :authorization, CLARIS_ID_HTTP_AUTH_TYPE, settings.fmid_token
         | 
| 71 | 
            +
                        conn.request :authorization, CLARIS_ID_HTTP_AUTH_TYPE, -> { settings.fmid_token }
         | 
| 72 | 
            +
                      elsif is_cloud_host
         | 
| 73 | 
            +
                        conn.request :authorization, CLARIS_ID_HTTP_AUTH_TYPE, -> { Cloud::ClarisIdTokenManager.new(settings).fetch_token }
         | 
| 65 74 | 
             
                      else
         | 
| 66 75 | 
             
                        conn.request :basic_auth, settings.username!, settings.password!
         | 
| 67 76 | 
             
                      end
         | 
| @@ -70,7 +79,7 @@ module FmRest | |
| 70 79 | 
             
                        conn.response :logger, FmRest.logger, bodies: true, headers: true, log_level: settings.log_level
         | 
| 71 80 | 
             
                      end
         | 
| 72 81 |  | 
| 73 | 
            -
                      conn.response :json
         | 
| 82 | 
            +
                      conn.response :json, content_type: /\bjson$/
         | 
| 74 83 | 
             
                      conn.adapter Faraday.default_adapter
         | 
| 75 84 | 
             
                    end
         | 
| 76 85 | 
             
                  end
         | 
| @@ -117,6 +126,16 @@ module FmRest | |
| 117 126 | 
             
                      &block
         | 
| 118 127 | 
             
                    )
         | 
| 119 128 | 
             
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  private
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  def cloud_host?(settings)
         | 
| 133 | 
            +
                    if settings.cloud == :auto
         | 
| 134 | 
            +
                      return FILEMAKER_CLOUD_HOST_MATCHER =~ settings.host
         | 
| 135 | 
            +
                    end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    settings.cloud
         | 
| 138 | 
            +
                  end
         | 
| 120 139 | 
             
                end
         | 
| 121 140 | 
             
              end
         | 
| 122 141 | 
             
            end
         | 
| @@ -27,6 +27,17 @@ module FmRest | |
| 27 27 | 
             
                  }.freeze
         | 
| 28 28 |  | 
| 29 29 | 
             
                  def on_complete(env)
         | 
| 30 | 
            +
                    # Sometimes, especially when using FileMaker Cloud, a failed
         | 
| 31 | 
            +
                    # authorization request will return a 401 (Unauthorized) with text/html
         | 
| 32 | 
            +
                    # content-type instead of the regular JSON, so we need to catch it
         | 
| 33 | 
            +
                    # manually here, emulating a regular account error
         | 
| 34 | 
            +
                    if !(/\bjson$/ === env.response_headers["content-type"]) && env.status == 401
         | 
| 35 | 
            +
                      raise FmRest::APIError::AccountError.new(212, "Authentication failed (HTTP 401: Unauthorized)")
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    # From this point on we want JSON
         | 
| 39 | 
            +
                    return unless env.body.is_a?(Hash)
         | 
| 40 | 
            +
             | 
| 30 41 | 
             
                    # Sniff for either straight JSON parsing or Spyke's format
         | 
| 31 42 | 
             
                    if env.body[:metadata] && env.body[:metadata][:messages]
         | 
| 32 43 | 
             
                      check_errors(env.body[:metadata][:messages])
         | 
| @@ -7,10 +7,11 @@ module FmRest | |
| 7 7 | 
             
                # FM Data API authentication middleware using the credentials strategy
         | 
| 8 8 | 
             
                #
         | 
| 9 9 | 
             
                class TokenSession < Faraday::Middleware
         | 
| 10 | 
            +
                  include TokenStore
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
                  class NoSessionTokenSet < FmRest::Error; end
         | 
| 11 13 |  | 
| 12 14 | 
             
                  HEADER_KEY = "Authorization"
         | 
| 13 | 
            -
                  TOKEN_STORE_INTERFACE = [:load, :store, :delete].freeze
         | 
| 14 15 | 
             
                  LOGOUT_PATH_MATCHER = %r{\A(#{FmRest::V1::Connection::DATABASES_PATH}/[^/]+/sessions/)[^/]+\Z}.freeze
         | 
| 15 16 |  | 
| 16 17 | 
             
                  # @param app [#call]
         | 
| @@ -110,29 +111,15 @@ module FmRest | |
| 110 111 | 
             
                      end
         | 
| 111 112 | 
             
                  end
         | 
| 112 113 |  | 
| 113 | 
            -
                  def token_store
         | 
| 114 | 
            -
                    @token_store ||=
         | 
| 115 | 
            -
                      begin
         | 
| 116 | 
            -
                        if TOKEN_STORE_INTERFACE.all? { |method| token_store_option.respond_to?(method) }
         | 
| 117 | 
            -
                          token_store_option
         | 
| 118 | 
            -
                        elsif token_store_option.kind_of?(Class)
         | 
| 119 | 
            -
                          if token_store_option.respond_to?(:instance)
         | 
| 120 | 
            -
                            token_store_option.instance
         | 
| 121 | 
            -
                          else
         | 
| 122 | 
            -
                            token_store_option.new
         | 
| 123 | 
            -
                          end
         | 
| 124 | 
            -
                        else
         | 
| 125 | 
            -
                          FmRest::TokenStore::Memory.new
         | 
| 126 | 
            -
                        end
         | 
| 127 | 
            -
                      end
         | 
| 128 | 
            -
                  end
         | 
| 129 | 
            -
             | 
| 130 114 | 
             
                  def token_store_option
         | 
| 131 115 | 
             
                    @settings.token_store || FmRest.token_store
         | 
| 132 116 | 
             
                  end
         | 
| 133 117 |  | 
| 134 118 | 
             
                  def auth_connection
         | 
| 135 | 
            -
                     | 
| 119 | 
            +
                    # NOTE: this is purposely not memoized so that settings can be
         | 
| 120 | 
            +
                    # refreshed (since proc-based settings will not be automatically
         | 
| 121 | 
            +
                    # re-eval'd, for example for fmid_token-based auth)
         | 
| 122 | 
            +
                    V1.auth_connection(@settings)
         | 
| 136 123 | 
             
                  end
         | 
| 137 124 | 
             
                end
         | 
| 138 125 | 
             
              end
         | 
    
        data/lib/fmrest/version.rb
    CHANGED
    
    
    
        data/lib/fmrest.rb
    CHANGED
    
    | @@ -59,5 +59,12 @@ module FmRest | |
| 59 59 | 
             
                  require "fmrest/spyke"
         | 
| 60 60 | 
             
                  self.Layout(*_)
         | 
| 61 61 | 
             
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def require_cloud_support
         | 
| 64 | 
            +
                  require "fmrest/cloud"
         | 
| 65 | 
            +
                rescue LoadError => e
         | 
| 66 | 
            +
                  e.message << " (Did you include fmrest-cloud in your Gemfile?)" unless e.message.frozen?
         | 
| 67 | 
            +
                  raise e
         | 
| 68 | 
            +
                end
         | 
| 62 69 | 
             
              end
         | 
| 63 70 | 
             
            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. | 
| 4 | 
            +
              version: 0.20.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- | 
| 11 | 
            +
            date: 2021-10-19 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: faraday
         | 
| @@ -16,7 +16,7 @@ dependencies: | |
| 16 16 | 
             
                requirements:
         | 
| 17 17 | 
             
                - - ">="
         | 
| 18 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version:  | 
| 19 | 
            +
                    version: 1.8.0
         | 
| 20 20 | 
             
                - - "<"
         | 
| 21 21 | 
             
                  - !ruby/object:Gem::Version
         | 
| 22 22 | 
             
                    version: '2.0'
         | 
| @@ -26,7 +26,7 @@ dependencies: | |
| 26 26 | 
             
                requirements:
         | 
| 27 27 | 
             
                - - ">="
         | 
| 28 28 | 
             
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            -
                    version:  | 
| 29 | 
            +
                    version: 1.8.0
         | 
| 30 30 | 
             
                - - "<"
         | 
| 31 31 | 
             
                  - !ruby/object:Gem::Version
         | 
| 32 32 | 
             
                    version: '2.0'
         | 
| @@ -64,7 +64,6 @@ files: | |
| 64 64 | 
             
            - LICENSE.txt
         | 
| 65 65 | 
             
            - README.md
         | 
| 66 66 | 
             
            - lib/fmrest-core.rb
         | 
| 67 | 
            -
            - lib/fmrest-spyke.rb
         | 
| 68 67 | 
             
            - lib/fmrest.rb
         | 
| 69 68 | 
             
            - lib/fmrest/connection_settings.rb
         | 
| 70 69 | 
             
            - lib/fmrest/errors.rb
         | 
| @@ -76,6 +75,7 @@ files: | |
| 76 75 | 
             
            - lib/fmrest/token_store/moneta.rb
         | 
| 77 76 | 
             
            - lib/fmrest/token_store/null.rb
         | 
| 78 77 | 
             
            - lib/fmrest/token_store/redis.rb
         | 
| 78 | 
            +
            - lib/fmrest/token_store/short_memory.rb
         | 
| 79 79 | 
             
            - lib/fmrest/v1.rb
         | 
| 80 80 | 
             
            - lib/fmrest/v1/auth.rb
         | 
| 81 81 | 
             
            - lib/fmrest/v1/connection.rb
         | 
    
        data/lib/fmrest-spyke.rb
    DELETED