fmrest 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +11 -0
  4. data/README.md +175 -809
  5. metadata +61 -109
  6. data/.gitignore +0 -26
  7. data/.rspec +0 -3
  8. data/.travis.yml +0 -5
  9. data/Gemfile +0 -3
  10. data/Rakefile +0 -6
  11. data/UPGRADING +0 -15
  12. data/fmrest.gemspec +0 -49
  13. data/lib/fmrest.rb +0 -36
  14. data/lib/fmrest/connection_settings.rb +0 -124
  15. data/lib/fmrest/errors.rb +0 -30
  16. data/lib/fmrest/spyke.rb +0 -21
  17. data/lib/fmrest/spyke/base.rb +0 -9
  18. data/lib/fmrest/spyke/container_field.rb +0 -59
  19. data/lib/fmrest/spyke/model.rb +0 -33
  20. data/lib/fmrest/spyke/model/associations.rb +0 -86
  21. data/lib/fmrest/spyke/model/attributes.rb +0 -163
  22. data/lib/fmrest/spyke/model/auth.rb +0 -43
  23. data/lib/fmrest/spyke/model/connection.rb +0 -163
  24. data/lib/fmrest/spyke/model/container_fields.rb +0 -40
  25. data/lib/fmrest/spyke/model/global_fields.rb +0 -40
  26. data/lib/fmrest/spyke/model/http.rb +0 -77
  27. data/lib/fmrest/spyke/model/orm.rb +0 -256
  28. data/lib/fmrest/spyke/model/record_id.rb +0 -78
  29. data/lib/fmrest/spyke/model/serialization.rb +0 -99
  30. data/lib/fmrest/spyke/model/uri.rb +0 -29
  31. data/lib/fmrest/spyke/portal.rb +0 -55
  32. data/lib/fmrest/spyke/relation.rb +0 -359
  33. data/lib/fmrest/spyke/spyke_formatter.rb +0 -273
  34. data/lib/fmrest/spyke/validation_error.rb +0 -25
  35. data/lib/fmrest/string_date.rb +0 -220
  36. data/lib/fmrest/token_store.rb +0 -12
  37. data/lib/fmrest/token_store/active_record.rb +0 -74
  38. data/lib/fmrest/token_store/base.rb +0 -25
  39. data/lib/fmrest/token_store/memory.rb +0 -26
  40. data/lib/fmrest/token_store/moneta.rb +0 -41
  41. data/lib/fmrest/token_store/null.rb +0 -20
  42. data/lib/fmrest/token_store/redis.rb +0 -45
  43. data/lib/fmrest/v1.rb +0 -23
  44. data/lib/fmrest/v1/auth.rb +0 -30
  45. data/lib/fmrest/v1/connection.rb +0 -115
  46. data/lib/fmrest/v1/container_fields.rb +0 -114
  47. data/lib/fmrest/v1/dates.rb +0 -81
  48. data/lib/fmrest/v1/paths.rb +0 -47
  49. data/lib/fmrest/v1/raise_errors.rb +0 -57
  50. data/lib/fmrest/v1/token_session.rb +0 -133
  51. data/lib/fmrest/v1/token_store/active_record.rb +0 -13
  52. data/lib/fmrest/v1/token_store/memory.rb +0 -13
  53. data/lib/fmrest/v1/type_coercer.rb +0 -192
  54. data/lib/fmrest/v1/utils.rb +0 -94
  55. data/lib/fmrest/version.rb +0 -5
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module TokenStore
5
- autoload :Base, "fmrest/token_store/base"
6
- autoload :Memory, "fmrest/token_store/memory"
7
- autoload :Null, "fmrest/token_store/null"
8
- autoload :ActiveRecord, "fmrest/token_store/active_record"
9
- autoload :Moneta, "fmrest/token_store/moneta"
10
- autoload :Redis, "fmrest/token_store/redis"
11
- end
12
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/token_store/base"
4
- require "active_record"
5
-
6
- module FmRest
7
- module TokenStore
8
- # Heavily inspired by Moneta's ActiveRecord store:
9
- #
10
- # https://github.com/minad/moneta/blob/master/lib/moneta/adapters/activerecord.rb
11
- #
12
- class ActiveRecord < Base
13
- DEFAULT_TABLE_NAME = "fmrest_session_tokens".freeze
14
-
15
- @connection_lock = ::Mutex.new
16
- class << self
17
- attr_reader :connection_lock
18
- end
19
-
20
- attr_reader :connection_pool, :model
21
-
22
- delegate :with_connection, to: :connection_pool
23
-
24
- def initialize(options = {})
25
- super
26
-
27
- @connection_pool = ::ActiveRecord::Base.connection_pool
28
-
29
- create_table
30
-
31
- @model = Class.new(::ActiveRecord::Base)
32
- @model.table_name = table_name
33
- end
34
-
35
- def delete(key)
36
- model.where(scope: key).delete_all
37
- end
38
-
39
- def load(key)
40
- model.where(scope: key).pluck(:token).first
41
- end
42
-
43
- def store(key, value)
44
- record = model.find_or_initialize_by(scope: key)
45
- record.token = value
46
- record.save!
47
- value
48
- end
49
-
50
- private
51
-
52
- def create_table
53
- with_connection do |conn|
54
- return if conn.table_exists?(table_name)
55
-
56
- # Prevent multiple connections from attempting to create the table simultaneously.
57
- self.class.connection_lock.synchronize do
58
- conn.create_table(table_name, id: false) do |t|
59
- t.string :scope, null: false
60
- t.string :token, null: false
61
- t.datetime :updated_at
62
- end
63
- conn.add_index(table_name, :scope, unique: true)
64
- conn.add_index(table_name, [:scope, :token])
65
- end
66
- end
67
- end
68
-
69
- def table_name
70
- options[:table_name] || DEFAULT_TABLE_NAME
71
- end
72
- end
73
- end
74
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module TokenStore
5
- class Base
6
- attr_reader :options
7
-
8
- def initialize(options = {})
9
- @options = options
10
- end
11
-
12
- def load(key)
13
- raise NotImplementedError
14
- end
15
-
16
- def store(key, value)
17
- raise NotImplementedError
18
- end
19
-
20
- def delete(key)
21
- raise NotImplementedError
22
- end
23
- end
24
- end
25
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/token_store/base"
4
-
5
- module FmRest
6
- module TokenStore
7
- class Memory < Base
8
- def initialize(*args)
9
- super
10
- @tokens = {}
11
- end
12
-
13
- def delete(key)
14
- @tokens.delete(key)
15
- end
16
-
17
- def load(key)
18
- @tokens[key]
19
- end
20
-
21
- def store(key, value)
22
- @tokens[key] = value
23
- end
24
- end
25
- end
26
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/token_store/base"
4
- require "moneta"
5
-
6
- module FmRest
7
- module TokenStore
8
- class Moneta < Base
9
- DEFAULT_ADAPTER = :Memory
10
- DEFAULT_PREFIX = "fmrest-token:".freeze
11
-
12
- attr_reader :moneta
13
-
14
- # @param options [Hash]
15
- # Options to pass to `Moneta.new`
16
- # @option options [Symbol] :adapter (:Memory)
17
- # The Moneta adapter to use
18
- # @option options [String] :prefix (DEFAULT_PREFIX)
19
- # The prefix to use for keys
20
- def initialize(options = {})
21
- options = options.dup
22
- super(options)
23
- adapter = options.delete(:adapter) || DEFAULT_ADAPTER
24
- options[:prefix] ||= DEFAULT_PREFIX
25
- @moneta = ::Moneta.new(adapter, options)
26
- end
27
-
28
- def load(key)
29
- moneta[key]
30
- end
31
-
32
- def delete(key)
33
- moneta.delete(key)
34
- end
35
-
36
- def store(key, value)
37
- moneta[key] = value
38
- end
39
- end
40
- end
41
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "singleton"
4
-
5
- module FmRest
6
- module TokenStore
7
- module Null < Base
8
- include Singleton
9
-
10
- def delete(key)
11
- end
12
-
13
- def load(key)
14
- end
15
-
16
- def store(key, value)
17
- end
18
- end
19
- end
20
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/token_store/base"
4
- require "redis" unless defined?(MockRedis)
5
-
6
- module FmRest
7
- module TokenStore
8
- class Redis < Base
9
- DEFAULT_PREFIX = "fmrest-token:".freeze
10
-
11
- STORE_OPTIONS = [:redis, :prefix].freeze
12
-
13
- def initialize(options = {})
14
- super
15
- @redis = @options[:redis] || ::Redis.new(options_for_redis)
16
- @prefix = @options[:prefix] || DEFAULT_PREFIX
17
- end
18
-
19
- def load(key)
20
- @redis.get(prefix_key(key))
21
- end
22
-
23
- def store(key, value)
24
- @redis.set(prefix_key(key), value)
25
- value
26
- end
27
-
28
- def delete(key)
29
- @redis.del(prefix_key(key))
30
- end
31
-
32
- private
33
-
34
- def options_for_redis
35
- @options.dup.tap do |options|
36
- STORE_OPTIONS.each { |opt| options.delete(opt) }
37
- end
38
- end
39
-
40
- def prefix_key(key)
41
- "#{@prefix}#{key}"
42
- end
43
- end
44
- end
45
- end
data/lib/fmrest/v1.rb DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/v1/connection"
4
- require "fmrest/v1/paths"
5
- require "fmrest/v1/container_fields"
6
- require "fmrest/v1/utils"
7
- require "fmrest/v1/dates"
8
- require "fmrest/v1/auth"
9
-
10
- module FmRest
11
- module V1
12
- extend Connection
13
- extend Paths
14
- extend ContainerFields
15
- extend Utils
16
- extend Dates
17
- extend Auth
18
-
19
- autoload :TokenSession, "fmrest/v1/token_session"
20
- autoload :RaiseErrors, "fmrest/v1/raise_errors"
21
- autoload :TypeCoercer, "fmrest/v1/type_coercer"
22
- end
23
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module V1
5
- module Auth
6
- # Requests a token through basic auth
7
- #
8
- # @param connection [Faraday] the auth connection to use for
9
- # the request
10
- # @return The token if successful
11
- # @return `false` if authentication failed
12
- def request_auth_token(connection = FmRest::V1.auth_connection)
13
- request_auth_token!(connection)
14
- rescue FmRest::APIError::AccountError
15
- false
16
- end
17
-
18
- # Requests a token through basic auth, raising
19
- # `FmRest::APIError::AccountError` if auth fails
20
- #
21
- # @param (see #request_auth_token)
22
- # @return The token if successful
23
- # @raise [FmRest::APIError::AccountError] if authentication failed
24
- def request_auth_token!(connection = FmRest.V1.auth_connection)
25
- resp = connection.post(V1.session_path)
26
- resp.body["response"]["token"]
27
- end
28
- end
29
- end
30
- end
@@ -1,115 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "uri"
4
-
5
- module FmRest
6
- module V1
7
- module Connection
8
- BASE_PATH = "/fmi/data/v1/databases"
9
-
10
- AUTH_HEADERS = { "Content-Type" => "application/json" }.freeze
11
-
12
- # Builds a complete DAPI Faraday connection with middleware already
13
- # configured to handle authentication, JSON parsing, logging and DAPI
14
- # error handling. A block can be optionally given for additional
15
- # middleware configuration
16
- #
17
- # @option (see #base_connection)
18
- # @return (see #base_connection)
19
- def build_connection(settings = FmRest.default_connection_settings, &block)
20
- settings = ConnectionSettings.wrap(settings)
21
-
22
- base_connection(settings) do |conn|
23
- conn.use RaiseErrors
24
- conn.use TokenSession, settings
25
-
26
- # The EncodeJson and Multipart middlewares only encode the request
27
- # when the content type matches, so we can have them both here and
28
- # still play nice with each other, we just need to set the content
29
- # type to multipart/form-data when we want to submit a container
30
- # field
31
- conn.request :multipart
32
- conn.request :json
33
-
34
- # Allow overriding the default response middleware
35
- if block_given?
36
- yield conn, settings
37
- else
38
- conn.use TypeCoercer, settings
39
- conn.response :json
40
- end
41
-
42
- if settings.log
43
- conn.response :logger, nil, bodies: true, headers: true
44
- end
45
-
46
- conn.adapter Faraday.default_adapter
47
- end
48
- end
49
-
50
- # Builds a Faraday connection to use for DAPI basic auth login
51
- #
52
- # @option (see #base_connection)
53
- # @return (see #base_connection)
54
- def auth_connection(settings = FmRest.default_connection_settings)
55
- settings = ConnectionSettings.wrap(settings)
56
-
57
- base_connection(settings, { headers: AUTH_HEADERS }) do |conn|
58
- conn.use RaiseErrors
59
-
60
- conn.basic_auth settings.username!, settings.password!
61
-
62
- if settings.log
63
- conn.response :logger, nil, bodies: true, headers: true
64
- end
65
-
66
- conn.response :json
67
- conn.adapter Faraday.default_adapter
68
- end
69
- end
70
-
71
- # Builds a base Faraday connection with base URL constructed from
72
- # connection settings and passes it the given block
73
- #
74
- # @option settings [String] :host The hostname for the FM server
75
- # @option settings [String] :database The FM database name
76
- # @option settings [String] :username The username for DAPI authentication
77
- # @option settings [String] :account_name Alias of :username for
78
- # compatibility with Rfm gem
79
- # @option settings [String] :password The password for DAPI authentication
80
- # @option settings [String] :ssl SSL settings to forward to the Faraday
81
- # connection
82
- # @option settings [String] :proxy Proxy options to forward to the Faraday
83
- # connection
84
- # @param faraday_options [Hash] additional options for Faraday object
85
- # @return [Faraday] The new Faraday connection
86
- def base_connection(settings = FmRest.default_connection_settings, faraday_options = nil, &block)
87
- settings = ConnectionSettings.wrap(settings)
88
-
89
- host = settings.host!
90
-
91
- # Default to HTTPS
92
- scheme = "https"
93
-
94
- if host.match(/\Ahttps?:\/\//)
95
- uri = URI(host)
96
- host = uri.hostname
97
- host += ":#{uri.port}" if uri.port != uri.default_port
98
- scheme = uri.scheme
99
- end
100
-
101
- faraday_options = (faraday_options || {}).dup
102
- faraday_options[:ssl] = settings.ssl if settings.ssl?
103
- faraday_options[:proxy] = settings.proxy if settings.proxy?
104
-
105
- database = URI.encode_www_form_component(settings.database!)
106
-
107
- Faraday.new(
108
- "#{scheme}://#{host}#{BASE_PATH}/#{database}/".freeze,
109
- faraday_options,
110
- &block
111
- )
112
- end
113
- end
114
- end
115
- end
@@ -1,114 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module V1
5
- module ContainerFields
6
- DEFAULT_UPLOAD_CONTENT_TYPE = "application/octet-stream".freeze
7
-
8
- # Given a container field URL it tries to fetch it and returns an IO
9
- # object with its body content (see Ruby's OpenURI for how the IO object
10
- # is extended with useful HTTP response information).
11
- #
12
- # This method uses OpenURI instead of Faraday for fetching the actual
13
- # container file.
14
- #
15
- # @raise [FmRest::ContainerFieldError] if any step fails
16
- # @param container_field_url [String] The URL to the container to
17
- # download
18
- # @param base_connection [Faraday::Connection] An optional Faraday
19
- # connection to use as base settings for the container requests, useful
20
- # if you need to set SSL or proxy settings. If given, this connection
21
- # will not be used directly, but rather a new one with copied SSL and
22
- # proxy options. If omitted, `FmRest.default_connection_settings`'s
23
- # `:ssl` and `:proxy` options will be used instead (if available)
24
- # @return [IO] The contents of the container
25
- def fetch_container_data(container_field_url, base_connection = nil)
26
- require "open-uri"
27
-
28
- begin
29
- url = URI(container_field_url)
30
- rescue ::URI::InvalidURIError
31
- raise FmRest::ContainerFieldError, "Invalid container field URL `#{container_field_url}'"
32
- end
33
-
34
- # Make sure we don't try to open anything on the file:/ URI scheme
35
- unless url.scheme.match(/\Ahttps?\Z/)
36
- raise FmRest::ContainerFieldError, "Container URL is not HTTP (#{container_field_url})"
37
- end
38
-
39
- ssl_options = base_connection && base_connection.ssl && base_connection.ssl.to_hash
40
- proxy_options = base_connection && base_connection.proxy && base_connection.proxy.to_hash
41
-
42
- conn =
43
- Faraday.new(nil,
44
- ssl: ssl_options || FmRest.default_connection_settings[:ssl],
45
- proxy: proxy_options || FmRest.default_connection_settings[:proxy]
46
- )
47
-
48
- # Requesting the container URL with no cookie set will respond with a
49
- # redirect and a session cookie
50
- cookie_response = conn.get url
51
-
52
- unless cookie = cookie_response.headers["Set-Cookie"]
53
- raise FmRest::ContainerFieldError, "Container field's initial request didn't return a session cookie, the URL may be stale (try downloading it again immediately after retrieving the record)"
54
- end
55
-
56
- # Now request the URL again with the proper session cookie using
57
- # OpenURI, which wraps the response in an IO object which also responds
58
- # to #content_type
59
- url.open(faraday_connection_to_openuri_options(conn).merge("Cookie" => cookie))
60
- end
61
-
62
- # Handles the core logic of uploading a file into a container field
63
- #
64
- # @param connection [Faraday::Connection] the Faraday connection to use
65
- # @param container_path [String] the path to the container
66
- # @param filename_or_io [String, IO] a path to the file to upload or an
67
- # IO object
68
- # @param options [Hash]
69
- # @option options [String] :content_type (DEFAULT_UPLOAD_CONTENT_TYPE)
70
- # The content type for the uploaded file
71
- # @option options [String] :filename The filename to use for the uploaded
72
- # file, defaults to `filename_or_io.original_filename` if available
73
- def upload_container_data(connection, container_path, filename_or_io, options = {})
74
- content_type = options[:content_type] || DEFAULT_UPLOAD_CONTENT_TYPE
75
-
76
- connection.post do |request|
77
- request.url container_path
78
- request.headers['Content-Type'] = ::Faraday::Request::Multipart.mime_type
79
-
80
- filename = options[:filename] || filename_or_io.try(:original_filename)
81
-
82
- request.body = { upload: Faraday::UploadIO.new(filename_or_io, content_type, filename) }
83
- end
84
- end
85
-
86
- private
87
-
88
- # Copies a Faraday::Connection's relevant options to
89
- # OpenURI::OpenRead#open format
90
- #
91
- def faraday_connection_to_openuri_options(conn)
92
- openuri_opts = {}
93
-
94
- if !conn.ssl.empty?
95
- openuri_opts[:ssl_verify_mode] =
96
- conn.ssl.fetch(:verify, true) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
97
-
98
- openuri_opts[:ssl_ca_cert] = conn.ssl.cert_store if conn.ssl.cert_store
99
- end
100
-
101
- if conn.proxy && !conn.proxy.empty?
102
- if conn.proxy.user && conn.proxy.password
103
- openuri_opts[:proxy_http_basic_authentication] =
104
- [conn.proxy.uri.tap { |u| u.userinfo = ""}, conn.proxy.user, conn.proxy.password]
105
- else
106
- openuri_opts[:proxy] = conn.proxy.uri
107
- end
108
- end
109
-
110
- openuri_opts
111
- end
112
- end
113
- end
114
- end