Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .yardopts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
--markup markdown
--plugin activesupport-concern
--exclude lib/generators/*
-
lib/**/*.rb
-
docs/*
23 changes: 23 additions & 0 deletions lib/fmrest/spyke.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@

module FmRest
module Spyke
class << self
# Sets the bahavior to use when creating an ORM query that can't be
# logically satisified. See the section on querying in the README for
# more info.
#
# Possible values:
#
# * `:raise` - Raise an `FmRest::Spyke::UnsatisfiableQuery` exception
# (inherits from `ArgumentError`) when the unsatisifiable query is
# created
# * `:request_silent` - Silently allow the unsatisifiable query to go
# through, which will be translated in the Data API as `field:
# "1001..1000"` (ensures zero results)
# * `:return_silent` - Silently return an empty resultset without issuing
# a request to the Data API. Use this option if you don't care about
# potential server-side side effects (e.g. scripts) of running a query.
# * `:return_warn` - Same as `:return_silent` but will `warn()` about the
# unsatisifiable query
# * `:request_warn`/`nil` (default) - Same as `:request_silent`, but will
# `warn()` about the unsatisifiable query
attr_accessor :on_unsatisifiable_query
end

def self.included(base)
base.include Model
end
Expand Down
32 changes: 22 additions & 10 deletions lib/fmrest/spyke/model/orm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,24 @@ def all
def fetch(options = {})
if current_scope.has_query?
scope = extend_scope_with_fm_params(current_scope, prefixed: false)
scope = scope.where(query: scope.query_params)
scope = extend_scope_with_query_params(scope)
scope = scope.with(FmRest::V1::find_path(layout))
else
scope = extend_scope_with_fm_params(current_scope, prefixed: true)
end

previous, self.current_scope = current_scope, scope

# The DAPI returns a 401 "No records match the request" error when
# nothing matches a _find request, so we need to catch it in order
# to provide sane behavior (i.e. return an empty resultset)
begin
current_scope.has_query? ? scoped_request(:post) : super()
rescue FmRest::APIError::NoMatchingRecordsError => e
raise e if options[:raise_on_no_matching_records]
::Spyke::Result.new({})
end
current_scope.has_query? ? scoped_request(:post) : super()

# The DAPI returns a 401 "No records match the request" error when
# nothing matches a _find request, so we need to catch it in order
# to provide sane behavior (i.e. return an empty resultset)
rescue FmRest::APIError::NoMatchingRecordsError => e
raise e if options[:raise_on_no_matching_records]
::Spyke::Result.new({})
rescue Relation::UnsatisfiableQuery
::Spyke::Result.new({})
ensure
self.current_scope = previous
end
Expand All @@ -80,6 +81,17 @@ def create!(attributes = {})

private

def extend_scope_with_query_params(scope)
query_params = scope.query_params

case FmRest::Spyke.on_unsatisifiable_query
when :return_silent, 'return_silent', :return_warn, 'return_warn'
query_params = scope.satisifiable_query_params
end

scope.where(query: query_params)
end

def extend_scope_with_fm_params(scope, prefixed: false)
prefix = prefixed ? "_" : nil

Expand Down
43 changes: 36 additions & 7 deletions lib/fmrest/spyke/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def u.to_s; ZERO_RESULTS_QUERY; end
NORMALIZED_OMIT_KEY = 'omit'

class UnknownQueryKey < ArgumentError; end
class UnsatisfiableQuery < ArgumentError; end

# NOTE: We need to keep limit, offset, sort, query and portal accessors
# separate from regular params because FM Data API uses either "limit" or
Expand Down Expand Up @@ -467,6 +468,15 @@ def find_each(batch_size: 1000)
end
end

def satisifiable_query_params(raise_on_empty: true)
params = self.query_params =
query_params.reject { |q| q.value?(UNSATISFIABLE_QUERY_VALUE) }

raise UnsatisfiableQuery if params.empty? && raise_on_empty

params
end

protected

def set_portal_params(params_hash, param)
Expand Down Expand Up @@ -504,13 +514,32 @@ def cartesian_product_query_params(params)

def unsatisfiable(field, a, b)
unless a == UNSATISFIABLE_QUERY_VALUE || b == UNSATISFIABLE_QUERY_VALUE
# TODO: Add a setting to make this an exception instead of a warning?
warn(
"An FmRest query using `and' required that `#{field}' match " \
"'#{a}' and '#{b}' at the same time which can't be satisified. " \
"This will appear in the find request as '#{UNSATISFIABLE_QUERY_VALUE}' " \
"and may result in an empty resultset."
)
case FmRest::Spyke.on_unsatisifiable_query
when :request_silent, :return_silent, 'request_silent', 'return_silent'
when :raise, 'raise'
raise ArgumentError,
"An FmRest query using `.and' required that '#{field}' match " \
"'#{a}' & '#{b}' at the same time which can't be satisified."
when :return_warn, 'return_warn'
warn(
"An FmRest query using `.and' required that '#{field}' match " \
"'#{a}' & '#{b}' at the same time which can't be satisified. " \
"The conflicting part of the query will be omitted from the " \
"request, and no Data API request will be sent if there's " \
"nothing left to query for."
)
when :request_warn, 'request_warn', nil
warn(
"An FmRest query using `.and' required that '#{field}' match " \
"'#{a}' & '#{b}' at the same time which can't be satisified. " \
"This will appear in the find request as '#{UNSATISFIABLE_QUERY_VALUE}' " \
"and may result in an empty resultset."
)
else
raise ArgumentError, "Unrecognized value for " \
"FmRest::Spyke.on_unsatisifiable_query " \
"`#{FmRest::Spyke.on_unsatisifiable_query}'"
end
end

UNSATISFIABLE_QUERY_VALUE
Expand Down