fmrest 0.1.0 → 0.2.0

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.yardopts +1 -0
  4. data/README.md +101 -7
  5. data/fmrest.gemspec +3 -0
  6. data/lib/fmrest.rb +2 -0
  7. data/lib/fmrest/errors.rb +27 -0
  8. data/lib/fmrest/spyke.rb +9 -0
  9. data/lib/fmrest/spyke/base.rb +2 -0
  10. data/lib/fmrest/spyke/container_field.rb +59 -0
  11. data/lib/fmrest/spyke/json_parser.rb +83 -24
  12. data/lib/fmrest/spyke/model.rb +7 -0
  13. data/lib/fmrest/spyke/model/associations.rb +2 -0
  14. data/lib/fmrest/spyke/model/attributes.rb +14 -55
  15. data/lib/fmrest/spyke/model/connection.rb +2 -0
  16. data/lib/fmrest/spyke/model/container_fields.rb +25 -0
  17. data/lib/fmrest/spyke/model/orm.rb +72 -5
  18. data/lib/fmrest/spyke/model/serialization.rb +80 -0
  19. data/lib/fmrest/spyke/model/uri.rb +2 -0
  20. data/lib/fmrest/spyke/portal.rb +2 -0
  21. data/lib/fmrest/spyke/relation.rb +30 -14
  22. data/lib/fmrest/token_store.rb +6 -0
  23. data/lib/fmrest/token_store/active_record.rb +74 -0
  24. data/lib/fmrest/token_store/base.rb +25 -0
  25. data/lib/fmrest/token_store/memory.rb +26 -0
  26. data/lib/fmrest/token_store/redis.rb +45 -0
  27. data/lib/fmrest/v1.rb +10 -49
  28. data/lib/fmrest/v1/connection.rb +57 -0
  29. data/lib/fmrest/v1/container_fields.rb +73 -0
  30. data/lib/fmrest/v1/paths.rb +36 -0
  31. data/lib/fmrest/v1/raise_errors.rb +55 -0
  32. data/lib/fmrest/v1/token_session.rb +32 -12
  33. data/lib/fmrest/v1/token_store/active_record.rb +6 -66
  34. data/lib/fmrest/v1/token_store/memory.rb +6 -19
  35. data/lib/fmrest/v1/utils.rb +94 -0
  36. data/lib/fmrest/version.rb +3 -1
  37. metadata +60 -5
  38. data/lib/fmrest/v1/token_store.rb +0 -6
  39. data/lib/fmrest/v1/token_store/base.rb +0 -14
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FmRest
2
4
  module V1
3
5
  # FM Data API authentication middleware using the credentials strategy
4
6
  #
5
7
  class TokenSession < Faraday::Middleware
6
8
  HEADER_KEY = "Authorization".freeze
9
+ TOKEN_STORE_INTERFACE = [:load, :store, :delete].freeze
7
10
 
8
11
  def initialize(app, options = FmRest.config)
9
12
  super(app)
@@ -20,7 +23,7 @@ module FmRest
20
23
  @app.call(env).on_complete do |response_env|
21
24
  if response_env[:status] == 401 # Unauthorized
22
25
  env[:body] = request_body
23
- token_store.clear
26
+ token_store.delete(token_store_key)
24
27
  set_auth_header(env)
25
28
  return @app.call(env)
26
29
  end
@@ -38,11 +41,11 @@ module FmRest
38
41
  # otherwise raises an exception.
39
42
  #
40
43
  def token
41
- token = token_store.fetch
44
+ token = token_store.load(token_store_key)
42
45
  return token if token
43
46
 
44
47
  if token = request_token
45
- token_store.store(token)
48
+ token_store.store(token_store_key, token)
46
49
  return token
47
50
  end
48
51
 
@@ -61,19 +64,36 @@ module FmRest
61
64
  false
62
65
  end
63
66
 
64
- def token_store
65
- @token_store ||= token_store_class.new(@options.fetch(:host), @options.fetch(:database))
67
+ # The key to use to store a token, uses the format host:database
68
+ #
69
+ def token_store_key
70
+ @token_store_key ||=
71
+ begin
72
+ # Strip the host part to just the hostname (i.e. no scheme or port)
73
+ host = @options.fetch(:host)
74
+ host = URI(host).hostname if host.match?(/\Ahttps?:\/\//)
75
+ "#{host}:#{@options.fetch(:database)}"
76
+ end
66
77
  end
67
78
 
68
- def token_store_class
69
- FmRest.token_store ||
79
+ def token_store
80
+ @token_store ||=
70
81
  begin
71
- # TODO: Make this less ugly
72
- require "fmrest/v1/token_store/memory"
73
- TokenStore::Memory
82
+ if TOKEN_STORE_INTERFACE.all? { |method| token_store_option.respond_to?(method) }
83
+ token_store_option
84
+ elsif token_store_option.kind_of?(Class)
85
+ token_store_option.new
86
+ else
87
+ require "fmrest/token_store/memory"
88
+ TokenStore::Memory.new
89
+ end
74
90
  end
75
91
  end
76
92
 
93
+ def token_store_option
94
+ @options[:token_store] || FmRest.token_store
95
+ end
96
+
77
97
  def auth_connection
78
98
  @auth_connection ||= V1.base_connection(@options) do |conn|
79
99
  conn.basic_auth @options.fetch(:username), @options.fetch(:password)
@@ -82,8 +102,8 @@ module FmRest
82
102
  conn.response :logger, nil, bodies: true, headers: true
83
103
  end
84
104
 
85
- conn.response :json
86
- conn.adapter Faraday.default_adapter
105
+ conn.response :json
106
+ conn.adapter Faraday.default_adapter
87
107
  end
88
108
  end
89
109
  end
@@ -1,73 +1,13 @@
1
- require "fmrest/v1/token_store/base"
1
+ # frozen_string_literal: true
2
+
3
+ warn "FmRest::V1::TokenStore::ActiveRecord is deprecated, use FmRest::TokenStore::ActiveRecord instead"
4
+
5
+ require "fmrest/token_store/active_record"
2
6
 
3
7
  module FmRest
4
8
  module V1
5
9
  module TokenStore
6
- # Heavily inspired by Moneta's ActiveRecord store:
7
- #
8
- # https://github.com/minad/moneta/blob/master/lib/moneta/adapters/activerecord.rb
9
- #
10
- class ActiveRecord < Base
11
- DEFAULT_TABLE_NAME = "fmrest_session_tokens".freeze
12
-
13
- @connection_lock = ::Mutex.new
14
- class << self
15
- attr_reader :connection_lock
16
- end
17
-
18
- attr_reader :connection_pool, :model
19
-
20
- delegate :with_connection, to: :connection_pool
21
-
22
- def initialize(host, database, options = {})
23
- super
24
-
25
- @connection_pool = ::ActiveRecord::Base.connection_pool
26
-
27
- create_table
28
-
29
- @model = Class.new(::ActiveRecord::Base)
30
- @model.table_name = table_name
31
- end
32
-
33
- def clear
34
- model.where(scope: scope).delete_all
35
- end
36
-
37
- def fetch
38
- model.where(scope: scope).pluck(:token).first
39
- end
40
-
41
- def store(token)
42
- record = model.find_or_initialize_by(scope: scope)
43
- record.token = token
44
- record.save!
45
- token
46
- end
47
-
48
- private
49
-
50
- def create_table
51
- with_connection do |conn|
52
- return if conn.table_exists?(table_name)
53
-
54
- # Prevent multiple connections from attempting to create the table simultaneously.
55
- self.class.connection_lock.synchronize do
56
- conn.create_table(table_name, id: false) do |t|
57
- t.string :scope, null: false
58
- t.string :token, null: false
59
- t.datetime :updated_at
60
- end
61
- conn.add_index(table_name, :scope, unique: true)
62
- conn.add_index(table_name, [:scope, :token])
63
- end
64
- end
65
- end
66
-
67
- def table_name
68
- options[:table_name] || DEFAULT_TABLE_NAME
69
- end
70
- end
10
+ ActiveRecord = ::FmRest::TokenStore::ActiveRecord
71
11
  end
72
12
  end
73
13
  end
@@ -1,26 +1,13 @@
1
- require "fmrest/v1/token_store/base"
1
+ # frozen_string_literal: true
2
+
3
+ warn "FmRest::V1::TokenStore::Memory is deprecated, use FmRest::TokenStore::Memory instead"
4
+
5
+ require "fmrest/token_store/memory"
2
6
 
3
7
  module FmRest
4
8
  module V1
5
9
  module TokenStore
6
- class Memory < Base
7
- def initialize(host, database, options = {})
8
- super
9
- @tokens = {}
10
- end
11
-
12
- def clear
13
- @tokens.delete(scope)
14
- end
15
-
16
- def fetch
17
- @tokens[scope]
18
- end
19
-
20
- def store(token)
21
- @tokens[scope] = token
22
- end
23
- end
10
+ Memory = ::FmRest::TokenStore::Memory
24
11
  end
25
12
  end
26
13
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FmRest
4
+ module V1
5
+ module Utils
6
+ VALID_SCRIPT_KEYS = [:prerequest, :presort, :after].freeze
7
+
8
+ # Converts custom script options to a hash with the Data API's expected
9
+ # JSON script format.
10
+ #
11
+ # If script_options is a string or symbol it will be passed as the name
12
+ # of the script to execute (after the action, e.g. save).
13
+ #
14
+ # If script_options is an array the first element will be the name of the
15
+ # script to execute (after the action) and the second element (if any)
16
+ # will be its param value.
17
+ #
18
+ # If script_options is a hash it will expect to contain one or more of
19
+ # the following keys: :prerequest, :presort, :after
20
+ #
21
+ # Any of those keys should contain either a string/symbol or array, which
22
+ # will be treated as described above, except for their own script
23
+ # execution order (prerequest, presort or after action).
24
+ #
25
+ # Examples:
26
+ #
27
+ # convert_script_params("My Script")
28
+ # # => { "script": "My Script" }
29
+ #
30
+ # convert_script_params(["My Script", "the param"])
31
+ # # => { "script": "My Script", "script.param": "the param" }
32
+ #
33
+ # convert_script_params(after: "After Script", prerequest: "Prerequest Script")
34
+ # # => { "script": "After Script", "script.prerequest": "Prerequest Script" }
35
+ #
36
+ # convert_script_params(presort: ["Presort Script", "foo"], prerequest: "Prerequest Script")
37
+ # # => {
38
+ # # "script.presort": "After Script",
39
+ # # "script.presort.param": "foo",
40
+ # # "script.prerequest": "Prerequest Script"
41
+ # # }
42
+ #
43
+ def convert_script_params(script_options)
44
+ params = {}
45
+
46
+ case script_options
47
+ when String, Symbol
48
+ params[:script] = script_options.to_s
49
+
50
+ when Array
51
+ params.merge!(convert_script_arguments(script_options))
52
+
53
+ when Hash
54
+ script_options.each_key do |key|
55
+ next if VALID_SCRIPT_KEYS.include?(key)
56
+ raise ArgumentError, "Invalid script option #{key.inspect}"
57
+ end
58
+
59
+ if script_options.has_key?(:prerequest)
60
+ params.merge!(convert_script_arguments(script_options[:prerequest], :prerequest))
61
+ end
62
+
63
+ if script_options.has_key?(:presort)
64
+ params.merge!(convert_script_arguments(script_options[:presort], :presort))
65
+ end
66
+
67
+ if script_options.has_key?(:after)
68
+ params.merge!(convert_script_arguments(script_options[:after]))
69
+ end
70
+ end
71
+
72
+ params
73
+ end
74
+
75
+ private
76
+
77
+ def convert_script_arguments(script_arguments, suffix = nil)
78
+ base = suffix ? "script.#{suffix}".to_sym : :script
79
+
80
+ {}.tap do |params|
81
+ case script_arguments
82
+ when String, Symbol
83
+ params[base] = script_arguments.to_s
84
+ when Array
85
+ params[base] = script_arguments.first.to_s
86
+ params["#{base}.param".to_sym] = script_arguments[1] if script_arguments[1]
87
+ else
88
+ raise ArgumentError, "Script arguments are expected as a String, Symbol or Array"
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FmRest
2
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
3
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fmrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Carbajal
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-10 00:00:00.000000000 Z
11
+ date: 2019-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -148,6 +148,48 @@ dependencies:
148
148
  - - ">="
149
149
  - !ruby/object:Gem::Version
150
150
  version: '0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: activerecord
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ - !ruby/object:Gem::Dependency
166
+ name: sqlite3
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: 1.3.6
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: 1.3.6
179
+ - !ruby/object:Gem::Dependency
180
+ name: mock_redis
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
151
193
  description: FileMaker Data API client using Faraday, with optional ActiveRecord-like
152
194
  ORM based on Spyke
153
195
  email:
@@ -159,6 +201,7 @@ files:
159
201
  - ".gitignore"
160
202
  - ".rspec"
161
203
  - ".travis.yml"
204
+ - ".yardopts"
162
205
  - CODE_OF_CONDUCT.md
163
206
  - Gemfile
164
207
  - LICENSE.txt
@@ -166,23 +209,35 @@ files:
166
209
  - Rakefile
167
210
  - fmrest.gemspec
168
211
  - lib/fmrest.rb
212
+ - lib/fmrest/errors.rb
169
213
  - lib/fmrest/spyke.rb
170
214
  - lib/fmrest/spyke/base.rb
215
+ - lib/fmrest/spyke/container_field.rb
171
216
  - lib/fmrest/spyke/json_parser.rb
172
217
  - lib/fmrest/spyke/model.rb
173
218
  - lib/fmrest/spyke/model/associations.rb
174
219
  - lib/fmrest/spyke/model/attributes.rb
175
220
  - lib/fmrest/spyke/model/connection.rb
221
+ - lib/fmrest/spyke/model/container_fields.rb
176
222
  - lib/fmrest/spyke/model/orm.rb
223
+ - lib/fmrest/spyke/model/serialization.rb
177
224
  - lib/fmrest/spyke/model/uri.rb
178
225
  - lib/fmrest/spyke/portal.rb
179
226
  - lib/fmrest/spyke/relation.rb
227
+ - lib/fmrest/token_store.rb
228
+ - lib/fmrest/token_store/active_record.rb
229
+ - lib/fmrest/token_store/base.rb
230
+ - lib/fmrest/token_store/memory.rb
231
+ - lib/fmrest/token_store/redis.rb
180
232
  - lib/fmrest/v1.rb
233
+ - lib/fmrest/v1/connection.rb
234
+ - lib/fmrest/v1/container_fields.rb
235
+ - lib/fmrest/v1/paths.rb
236
+ - lib/fmrest/v1/raise_errors.rb
181
237
  - lib/fmrest/v1/token_session.rb
182
- - lib/fmrest/v1/token_store.rb
183
238
  - lib/fmrest/v1/token_store/active_record.rb
184
- - lib/fmrest/v1/token_store/base.rb
185
239
  - lib/fmrest/v1/token_store/memory.rb
240
+ - lib/fmrest/v1/utils.rb
186
241
  - lib/fmrest/version.rb
187
242
  homepage: https://github.com/beezwax/fmrest-ruby
188
243
  licenses:
@@ -204,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
259
  version: '0'
205
260
  requirements: []
206
261
  rubyforge_project:
207
- rubygems_version: 2.7.7
262
+ rubygems_version: 2.7.8
208
263
  signing_key:
209
264
  specification_version: 4
210
265
  summary: FileMaker Data API client using Faraday
@@ -1,6 +0,0 @@
1
- module FmRest
2
- module V1
3
- module TokenStore
4
- end
5
- end
6
- end
@@ -1,14 +0,0 @@
1
- module FmRest
2
- module V1
3
- module TokenStore
4
- class Base
5
- attr_reader :scope, :options
6
-
7
- def initialize(host, database, options = {})
8
- @scope = "#{host.to_s}:#{database.to_s}"
9
- @options = options
10
- end
11
- end
12
- end
13
- end
14
- end