fmrest 0.10.0 → 0.13.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.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/CHANGELOG.md +38 -0
- data/README.md +194 -763
- metadata +70 -97
- data/.gitignore +0 -26
- data/.rspec +0 -3
- data/.travis.yml +0 -5
- data/Gemfile +0 -3
- data/Rakefile +0 -6
- data/fmrest.gemspec +0 -38
- data/lib/fmrest.rb +0 -29
- data/lib/fmrest/errors.rb +0 -28
- data/lib/fmrest/spyke.rb +0 -21
- data/lib/fmrest/spyke/base.rb +0 -23
- data/lib/fmrest/spyke/container_field.rb +0 -59
- data/lib/fmrest/spyke/model.rb +0 -36
- data/lib/fmrest/spyke/model/associations.rb +0 -82
- data/lib/fmrest/spyke/model/attributes.rb +0 -171
- data/lib/fmrest/spyke/model/auth.rb +0 -35
- data/lib/fmrest/spyke/model/connection.rb +0 -74
- data/lib/fmrest/spyke/model/container_fields.rb +0 -25
- data/lib/fmrest/spyke/model/global_fields.rb +0 -40
- data/lib/fmrest/spyke/model/http.rb +0 -37
- data/lib/fmrest/spyke/model/orm.rb +0 -212
- data/lib/fmrest/spyke/model/serialization.rb +0 -91
- data/lib/fmrest/spyke/model/uri.rb +0 -30
- data/lib/fmrest/spyke/portal.rb +0 -55
- data/lib/fmrest/spyke/relation.rb +0 -359
- data/lib/fmrest/spyke/spyke_formatter.rb +0 -273
- data/lib/fmrest/spyke/validation_error.rb +0 -25
- data/lib/fmrest/string_date.rb +0 -220
- data/lib/fmrest/token_store.rb +0 -6
- data/lib/fmrest/token_store/active_record.rb +0 -74
- data/lib/fmrest/token_store/base.rb +0 -25
- data/lib/fmrest/token_store/memory.rb +0 -26
- data/lib/fmrest/token_store/moneta.rb +0 -41
- data/lib/fmrest/token_store/redis.rb +0 -45
- data/lib/fmrest/v1.rb +0 -21
- data/lib/fmrest/v1/connection.rb +0 -89
- data/lib/fmrest/v1/container_fields.rb +0 -114
- data/lib/fmrest/v1/dates.rb +0 -81
- data/lib/fmrest/v1/paths.rb +0 -47
- data/lib/fmrest/v1/raise_errors.rb +0 -57
- data/lib/fmrest/v1/token_session.rb +0 -142
- data/lib/fmrest/v1/token_store/active_record.rb +0 -13
- data/lib/fmrest/v1/token_store/memory.rb +0 -13
- data/lib/fmrest/v1/type_coercer.rb +0 -192
- data/lib/fmrest/v1/utils.rb +0 -95
- data/lib/fmrest/version.rb +0 -5
@@ -1,359 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FmRest
|
4
|
-
module Spyke
|
5
|
-
class Relation < ::Spyke::Relation
|
6
|
-
SORT_PARAM_MATCHER = /(.*?)(!|__desc(?:end)?)?\Z/.freeze
|
7
|
-
|
8
|
-
# NOTE: We need to keep limit, offset, sort, query and portal accessors
|
9
|
-
# separate from regular params because FM Data API uses either "limit" or
|
10
|
-
# "_limit" (or "_offset", etc.) as param keys depending on the type of
|
11
|
-
# request, so we can't set the params until the last moment
|
12
|
-
|
13
|
-
|
14
|
-
attr_accessor :limit_value, :offset_value, :sort_params, :query_params,
|
15
|
-
:included_portals, :portal_params, :script_params
|
16
|
-
|
17
|
-
def initialize(*_args)
|
18
|
-
super
|
19
|
-
|
20
|
-
@limit_value = klass.default_limit
|
21
|
-
|
22
|
-
if klass.default_sort.present?
|
23
|
-
@sort_params = Array.wrap(klass.default_sort).map { |s| normalize_sort_param(s) }
|
24
|
-
end
|
25
|
-
|
26
|
-
@query_params = []
|
27
|
-
|
28
|
-
@included_portals = nil
|
29
|
-
@portal_params = {}
|
30
|
-
@script_params = {}
|
31
|
-
end
|
32
|
-
|
33
|
-
# @param options [String, Array, Hash, nil, false] sets script params to
|
34
|
-
# execute in the next get or find request
|
35
|
-
#
|
36
|
-
# @example
|
37
|
-
# # Find records and run the script named "My script"
|
38
|
-
# Person.script("My script").find_some
|
39
|
-
#
|
40
|
-
# # Find records and run the script named "My script" with param "the param"
|
41
|
-
# Person.script(["My script", "the param"]).find_some
|
42
|
-
#
|
43
|
-
# # Find records and run a prerequest, presort and after (normal) script
|
44
|
-
# Person.script(after: "Script", prerequest: "Prereq script", presort: "Presort script").find_some
|
45
|
-
#
|
46
|
-
# # Same as above, but passing parameters too
|
47
|
-
# Person.script(
|
48
|
-
# after: ["After script", "the param"],
|
49
|
-
# prerequest: ["Prereq script", "the param"],
|
50
|
-
# presort: o ["Presort script", "the param"]
|
51
|
-
# ).find_some
|
52
|
-
#
|
53
|
-
# Person.script(nil).find_some # Disable script execution
|
54
|
-
# Person.script(false).find_some # Disable script execution
|
55
|
-
#
|
56
|
-
# @return [FmRest::Spyke::Relation] a new relation with the script
|
57
|
-
# options applied
|
58
|
-
def script(options)
|
59
|
-
with_clone do |r|
|
60
|
-
if options.eql?(false) || options.eql?(nil)
|
61
|
-
r.script_params = {}
|
62
|
-
else
|
63
|
-
r.script_params = script_params.merge(FmRest::V1.convert_script_params(options))
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# @param value_or_hash [Integer, Hash] the limit value for this layout,
|
69
|
-
# or a hash with limits for the layout's portals
|
70
|
-
# @example
|
71
|
-
# Person.limit(10) # Set layout limit
|
72
|
-
# Person.limit(children: 10) # Set portal limit
|
73
|
-
# @return [FmRest::Spyke::Relation] a new relation with the limits
|
74
|
-
# applied
|
75
|
-
def limit(value_or_hash)
|
76
|
-
with_clone do |r|
|
77
|
-
if value_or_hash.respond_to?(:each)
|
78
|
-
r.set_portal_params(value_or_hash, :limit)
|
79
|
-
else
|
80
|
-
r.limit_value = value_or_hash
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# @param value_or_hash [Integer, Hash] the offset value for this layout,
|
86
|
-
# or a hash with offsets for the layout's portals
|
87
|
-
# @example
|
88
|
-
# Person.offset(10) # Set layout offset
|
89
|
-
# Person.offset(children: 10) # Set portal offset
|
90
|
-
# @return [FmRest::Spyke::Relation] a new relation with the offsets
|
91
|
-
# applied
|
92
|
-
def offset(value_or_hash)
|
93
|
-
with_clone do |r|
|
94
|
-
if value_or_hash.respond_to?(:each)
|
95
|
-
r.set_portal_params(value_or_hash, :offset)
|
96
|
-
else
|
97
|
-
r.offset_value = value_or_hash
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Allows sort params given in either hash format (using FM Data API's
|
103
|
-
# format), or as a symbol, in which case the of the attribute must match
|
104
|
-
# a known mapped attribute, optionally suffixed with `!` or `__desc` to
|
105
|
-
# signify it should use descending order.
|
106
|
-
#
|
107
|
-
# @param args [Array<Symbol, Hash>] the names of attributes to sort by with
|
108
|
-
# optional `!` or `__desc` suffix, or a hash of options as expected by
|
109
|
-
# the FM Data API
|
110
|
-
# @example
|
111
|
-
# Person.sort(:first_name, :age!)
|
112
|
-
# Person.sort(:first_name, :age__desc)
|
113
|
-
# Person.sort(:first_name, :age__descend)
|
114
|
-
# Person.sort({ fieldName: "FirstName" }, { fieldName: "Age", sortOrder: "descend" })
|
115
|
-
# @return [FmRest::Spyke::Relation] a new relation with the sort options
|
116
|
-
# applied
|
117
|
-
def sort(*args)
|
118
|
-
with_clone do |r|
|
119
|
-
r.sort_params = args.flatten.map { |s| normalize_sort_param(s) }
|
120
|
-
end
|
121
|
-
end
|
122
|
-
alias order sort
|
123
|
-
|
124
|
-
# Sets the portals to include with each record in the response.
|
125
|
-
#
|
126
|
-
# @param args [Array<Symbol, String>, true, false] the names of portals to
|
127
|
-
# include, or `false` to request no portals
|
128
|
-
# @example
|
129
|
-
# Person.portal(:relatives, :pets)
|
130
|
-
# Person.portal(false) # Disables portals
|
131
|
-
# Person.portal(true) # Enables portals (includes all)
|
132
|
-
# @return [FmRest::Spyke::Relation] a new relation with the portal
|
133
|
-
# options applied
|
134
|
-
def portal(*args)
|
135
|
-
raise ArgumentError, "Call `portal' with at least one argument" if args.empty?
|
136
|
-
|
137
|
-
with_clone do |r|
|
138
|
-
if args.length == 1 && args.first.eql?(true) || args.first.eql?(false)
|
139
|
-
r.included_portals = args.first ? nil : []
|
140
|
-
else
|
141
|
-
r.included_portals ||= []
|
142
|
-
r.included_portals += args.flatten.map { |p| normalize_portal_param(p) }
|
143
|
-
r.included_portals.uniq!
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
alias includes portal
|
148
|
-
alias portals portal
|
149
|
-
|
150
|
-
# Same as calling `portal(true)`
|
151
|
-
#
|
152
|
-
# @return (see #portal)
|
153
|
-
def with_all_portals
|
154
|
-
portal(true)
|
155
|
-
end
|
156
|
-
|
157
|
-
# Same as calling `portal(false)`
|
158
|
-
#
|
159
|
-
# @return (see #portal)
|
160
|
-
def without_portals
|
161
|
-
portal(false)
|
162
|
-
end
|
163
|
-
|
164
|
-
def query(*params)
|
165
|
-
with_clone do |r|
|
166
|
-
r.query_params += params.flatten.map { |p| normalize_query_params(p) }
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def omit(params)
|
171
|
-
query(params.merge(omit: true))
|
172
|
-
end
|
173
|
-
|
174
|
-
# @return [Boolean] whether a query was set on this relation
|
175
|
-
def has_query?
|
176
|
-
query_params.present?
|
177
|
-
end
|
178
|
-
|
179
|
-
# Finds a single instance of the model by forcing limit = 1, or simply
|
180
|
-
# fetching the record by id if the primary key was set
|
181
|
-
#
|
182
|
-
# @return [FmRest::Spyke::Base]
|
183
|
-
def find_one
|
184
|
-
@find_one ||=
|
185
|
-
if primary_key_set?
|
186
|
-
without_collection_params { super }
|
187
|
-
else
|
188
|
-
klass.new_collection_from_result(limit(1).fetch).first
|
189
|
-
end
|
190
|
-
rescue ::Spyke::ConnectionError => error
|
191
|
-
fallback_or_reraise(error, default: nil)
|
192
|
-
end
|
193
|
-
alias_method :first, :find_one
|
194
|
-
alias_method :any, :find_one
|
195
|
-
|
196
|
-
# Yields each batch of records that was found by the find options.
|
197
|
-
#
|
198
|
-
# NOTE: By its nature, batch processing is subject to race conditions if
|
199
|
-
# other processes are modifying the database
|
200
|
-
#
|
201
|
-
# @param batch_size [Integer] Specifies the size of the batch.
|
202
|
-
# @return [Enumerator] if called without a block.
|
203
|
-
def find_in_batches(batch_size: 1000)
|
204
|
-
unless block_given?
|
205
|
-
return to_enum(:find_in_batches, batch_size: batch_size) do
|
206
|
-
total = limit(1).find_some.metadata.data_info.found_count
|
207
|
-
(total - 1).div(batch_size) + 1
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
offset = 1 # DAPI offset is 1-based
|
212
|
-
|
213
|
-
loop do
|
214
|
-
relation = offset(offset).limit(batch_size)
|
215
|
-
|
216
|
-
records = relation.find_some
|
217
|
-
|
218
|
-
yield records if records.length > 0
|
219
|
-
|
220
|
-
break if records.length < batch_size
|
221
|
-
|
222
|
-
# Save one iteration if the total is a multiple of batch_size
|
223
|
-
if found_count = records.metadata.data_info && records.metadata.data_info.found_count
|
224
|
-
break if found_count == (offset - 1) + batch_size
|
225
|
-
end
|
226
|
-
|
227
|
-
offset += batch_size
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
# Looping through a collection of records from the database (using the
|
232
|
-
# #all method, for example) is very inefficient since it will fetch and
|
233
|
-
# instantiate all the objects at once.
|
234
|
-
#
|
235
|
-
# In that case, batch processing methods allow you to work with the
|
236
|
-
# records in batches, thereby greatly reducing memory consumption and be
|
237
|
-
# lighter on the Data API server.
|
238
|
-
#
|
239
|
-
# The find_each method uses #find_in_batches with a batch size of 1000
|
240
|
-
# (or as specified by the :batch_size option).
|
241
|
-
#
|
242
|
-
# NOTE: By its nature, batch processing is subject to race conditions if
|
243
|
-
# other processes are modifying the database
|
244
|
-
#
|
245
|
-
# @param (see #find_in_batches)
|
246
|
-
# @example
|
247
|
-
# Person.find_each do |person|
|
248
|
-
# person.greet
|
249
|
-
# end
|
250
|
-
#
|
251
|
-
# Person.query(name: "==Mitch").find_each do |person|
|
252
|
-
# person.say_hi
|
253
|
-
# end
|
254
|
-
# @return (see #find_in_batches)
|
255
|
-
def find_each(batch_size: 1000)
|
256
|
-
unless block_given?
|
257
|
-
return to_enum(:find_each, batch_size: batch_size) do
|
258
|
-
limit(1).find_some.metadata.data_info.found_count
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
find_in_batches(batch_size: batch_size) do |records|
|
263
|
-
records.each { |r| yield r }
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
protected
|
268
|
-
|
269
|
-
def set_portal_params(params_hash, param)
|
270
|
-
# Copy portal_params so we're not modifying the same hash as the parent
|
271
|
-
# scope
|
272
|
-
self.portal_params = portal_params.dup
|
273
|
-
|
274
|
-
params_hash.each do |portal_name, value|
|
275
|
-
# TODO: Use a hash like { portal_name: { param: value } } instead so
|
276
|
-
# we can intelligently avoid including portal params for excluded
|
277
|
-
# portals
|
278
|
-
key = "#{param}.#{normalize_portal_param(portal_name)}"
|
279
|
-
|
280
|
-
# Delete key if value is falsy
|
281
|
-
if !value && portal_params.has_key?(key)
|
282
|
-
portal_params.delete(key)
|
283
|
-
else
|
284
|
-
self.portal_params[key] = value
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
private
|
290
|
-
|
291
|
-
def normalize_sort_param(param)
|
292
|
-
if param.kind_of?(Symbol) || param.kind_of?(String)
|
293
|
-
_, attr, descend = param.to_s.match(SORT_PARAM_MATCHER).to_a
|
294
|
-
|
295
|
-
unless field_name = klass.mapped_attributes[attr]
|
296
|
-
raise ArgumentError, "Unknown attribute `#{attr}' given to sort as #{param.inspect}. If you want to use a custom sort pass a hash in the Data API format"
|
297
|
-
end
|
298
|
-
|
299
|
-
hash = { fieldName: field_name }
|
300
|
-
hash[:sortOrder] = "descend" if descend
|
301
|
-
return hash
|
302
|
-
end
|
303
|
-
|
304
|
-
# TODO: Sanitize sort hash param for FM Data API conformity?
|
305
|
-
param
|
306
|
-
end
|
307
|
-
|
308
|
-
def normalize_portal_param(param)
|
309
|
-
if param.kind_of?(Symbol)
|
310
|
-
portal_key, = klass.portal_options.find { |_, opts| opts[:name].to_s == param.to_s }
|
311
|
-
|
312
|
-
unless portal_key
|
313
|
-
raise ArgumentError, "Unknown portal #{param.inspect}. If you want to include a portal not defined in the model pass it as a string instead"
|
314
|
-
end
|
315
|
-
|
316
|
-
return portal_key
|
317
|
-
end
|
318
|
-
|
319
|
-
param
|
320
|
-
end
|
321
|
-
|
322
|
-
def normalize_query_params(params)
|
323
|
-
params.each_with_object({}) do |(k, v), normalized|
|
324
|
-
if k == :omit || k == "omit"
|
325
|
-
# FM Data API wants omit values as strings, e.g. "true" or "false"
|
326
|
-
# rather than true/false
|
327
|
-
normalized["omit"] = v.to_s
|
328
|
-
next
|
329
|
-
end
|
330
|
-
|
331
|
-
# TODO: Raise ArgumentError if an attribute given as symbol isn't defiend
|
332
|
-
if k.kind_of?(Symbol) && klass.mapped_attributes.has_key?(k)
|
333
|
-
normalized[klass.mapped_attributes[k].to_s] = v
|
334
|
-
else
|
335
|
-
normalized[k.to_s] = v
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def primary_key_set?
|
341
|
-
params[klass.primary_key].present?
|
342
|
-
end
|
343
|
-
|
344
|
-
def without_collection_params
|
345
|
-
orig_values = limit_value, offset_value, sort_params, query_params
|
346
|
-
self.limit_value = self.offset_value = self.sort_params = self.query_params = nil
|
347
|
-
yield
|
348
|
-
ensure
|
349
|
-
self.limit_value, self.offset_value, self.sort_params, self.query_params = orig_values
|
350
|
-
end
|
351
|
-
|
352
|
-
def with_clone
|
353
|
-
clone.tap do |relation|
|
354
|
-
yield relation
|
355
|
-
end
|
356
|
-
end
|
357
|
-
end
|
358
|
-
end
|
359
|
-
end
|
@@ -1,273 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "ostruct"
|
5
|
-
|
6
|
-
module FmRest
|
7
|
-
module Spyke
|
8
|
-
# Metadata class to be passed to Spyke::Collection#metadata
|
9
|
-
class Metadata < Struct.new(:messages, :script, :data_info)
|
10
|
-
alias_method :scripts, :script
|
11
|
-
end
|
12
|
-
|
13
|
-
class DataInfo < OpenStruct
|
14
|
-
def total_record_count; totalRecordCount; end
|
15
|
-
def found_count; foundCount; end
|
16
|
-
def returned_count; returnedCount; end
|
17
|
-
end
|
18
|
-
|
19
|
-
# Response Faraday middleware for converting FM API's response JSON into
|
20
|
-
# Spyke's expected format
|
21
|
-
class SpykeFormatter < ::Faraday::Response::Middleware
|
22
|
-
SINGLE_RECORD_RE = %r(/records/\d+\z).freeze
|
23
|
-
MULTIPLE_RECORDS_RE = %r(/records\z).freeze
|
24
|
-
CONTAINER_RE = %r(/records/\d+/containers/[^/]+/\d+\z).freeze
|
25
|
-
FIND_RECORDS_RE = %r(/_find\b).freeze
|
26
|
-
SCRIPT_REQUEST_RE = %r(/script/[^/]+\z).freeze
|
27
|
-
|
28
|
-
VALIDATION_ERROR_RANGE = 500..599
|
29
|
-
|
30
|
-
# @param app [#call]
|
31
|
-
# @param model [Class<FmRest::Spyke::Base>]
|
32
|
-
def initialize(app, model)
|
33
|
-
super(app)
|
34
|
-
@model = model
|
35
|
-
end
|
36
|
-
|
37
|
-
# @param env [Faraday::Env]
|
38
|
-
def on_complete(env)
|
39
|
-
return unless env.body.is_a?(Hash)
|
40
|
-
|
41
|
-
json = env.body
|
42
|
-
|
43
|
-
case
|
44
|
-
when single_record_request?(env)
|
45
|
-
env.body = prepare_single_record(json)
|
46
|
-
when multiple_records_request?(env), find_request?(env)
|
47
|
-
env.body = prepare_collection(json)
|
48
|
-
when create_request?(env), update_request?(env), delete_request?(env), container_upload_request?(env)
|
49
|
-
env.body = prepare_save_response(json)
|
50
|
-
when execute_script_request?(env)
|
51
|
-
env.body = build_base_hash(json)
|
52
|
-
else
|
53
|
-
# Attempt to parse unknown requests too
|
54
|
-
env.body = build_base_hash(json)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
# @param json [Hash]
|
61
|
-
# @return [Hash] the response in Spyke format
|
62
|
-
def prepare_save_response(json)
|
63
|
-
response = json[:response]
|
64
|
-
|
65
|
-
data = {}
|
66
|
-
data[:mod_id] = response[:modId] if response[:modId]
|
67
|
-
data[:id] = response[:recordId].to_i if response[:recordId]
|
68
|
-
|
69
|
-
build_base_hash(json, true).merge!(data: data)
|
70
|
-
end
|
71
|
-
|
72
|
-
# (see #prepare_save_response)
|
73
|
-
def prepare_single_record(json)
|
74
|
-
data =
|
75
|
-
json[:response][:data] &&
|
76
|
-
prepare_record_data(json[:response][:data].first)
|
77
|
-
|
78
|
-
build_base_hash(json).merge!(data: data)
|
79
|
-
end
|
80
|
-
|
81
|
-
# (see #prepare_save_response)
|
82
|
-
def prepare_collection(json)
|
83
|
-
data =
|
84
|
-
json[:response][:data] &&
|
85
|
-
json[:response][:data].map { |record_data| prepare_record_data(record_data) }
|
86
|
-
|
87
|
-
build_base_hash(json).merge!(data: data)
|
88
|
-
end
|
89
|
-
|
90
|
-
# @param json [Hash]
|
91
|
-
# @param include_errors [Boolean]
|
92
|
-
# @return [FmRest::Spyke::Metadata] the skeleton structure for a
|
93
|
-
# Spyke-formatted response
|
94
|
-
def build_base_hash(json, include_errors = false)
|
95
|
-
{
|
96
|
-
metadata: Metadata.new(
|
97
|
-
prepare_messages(json),
|
98
|
-
prepare_script_results(json),
|
99
|
-
prepare_data_info(json)
|
100
|
-
).freeze,
|
101
|
-
errors: include_errors ? prepare_errors(json) : {}
|
102
|
-
}
|
103
|
-
end
|
104
|
-
|
105
|
-
# @param json [Hash]
|
106
|
-
# @return [Array<OpenStruct>] the skeleton structure for a
|
107
|
-
# Spyke-formatted response
|
108
|
-
def prepare_messages(json)
|
109
|
-
return [] unless json[:messages]
|
110
|
-
json[:messages].map { |m| OpenStruct.new(m).freeze }.freeze
|
111
|
-
end
|
112
|
-
|
113
|
-
# @param json [Hash]
|
114
|
-
# @return [OpenStruct] the script(s) execution results for Spyke metadata
|
115
|
-
# format
|
116
|
-
def prepare_script_results(json)
|
117
|
-
results = {}
|
118
|
-
|
119
|
-
[:prerequest, :presort].each do |s|
|
120
|
-
if json[:response][:"scriptError.#{s}"]
|
121
|
-
results[s] = OpenStruct.new(
|
122
|
-
result: json[:response][:"scriptResult.#{s}"],
|
123
|
-
error: json[:response][:"scriptError.#{s}"]
|
124
|
-
).freeze
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
if json[:response][:scriptError]
|
129
|
-
results[:after] = OpenStruct.new(
|
130
|
-
result: json[:response][:scriptResult],
|
131
|
-
error: json[:response][:scriptError]
|
132
|
-
).freeze
|
133
|
-
end
|
134
|
-
|
135
|
-
results.present? ? OpenStruct.new(results).freeze : nil
|
136
|
-
end
|
137
|
-
|
138
|
-
# @param json [Hash]
|
139
|
-
# @return [OpenStruct] the script(s) execution results for
|
140
|
-
# Spyke metadata format
|
141
|
-
def prepare_data_info(json)
|
142
|
-
data_info = json[:response] && json[:response][:dataInfo]
|
143
|
-
|
144
|
-
return nil unless data_info.present?
|
145
|
-
|
146
|
-
DataInfo.new(data_info).freeze
|
147
|
-
end
|
148
|
-
|
149
|
-
# @param json [Hash]
|
150
|
-
# @return [Hash] the errors hash in Spyke format
|
151
|
-
def prepare_errors(json)
|
152
|
-
# Code 0 means "No Error"
|
153
|
-
# https://fmhelp.filemaker.com/help/17/fmp/en/index.html#page/FMP_Help/error-codes.html
|
154
|
-
return {} if json[:messages][0][:code].to_i == 0
|
155
|
-
|
156
|
-
json[:messages].each_with_object(base: []) do |message, hash|
|
157
|
-
# Only include validation errors
|
158
|
-
next unless VALIDATION_ERROR_RANGE.include?(message[:code].to_i)
|
159
|
-
|
160
|
-
hash[:base] << "#{message[:message]} (#{message[:code]})"
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# `json_data` is expected in this format:
|
165
|
-
#
|
166
|
-
# {
|
167
|
-
# "fieldData": {
|
168
|
-
# "fieldName1" : "fieldValue1",
|
169
|
-
# "fieldName2" : "fieldValue2",
|
170
|
-
# ...
|
171
|
-
# },
|
172
|
-
# "portalData": {
|
173
|
-
# "portal1" : [
|
174
|
-
# { <portalRecord1> },
|
175
|
-
# { <portalRecord2> },
|
176
|
-
# ...
|
177
|
-
# ],
|
178
|
-
# "portal2" : [
|
179
|
-
# { <portalRecord1> },
|
180
|
-
# { <portalRecord2> },
|
181
|
-
# ...
|
182
|
-
# ]
|
183
|
-
# },
|
184
|
-
# "modId": <Id_for_last_modification>,
|
185
|
-
# "recordId": <Unique_internal_ID_for_this_record>
|
186
|
-
# }
|
187
|
-
#
|
188
|
-
# @param json_data [Hash]
|
189
|
-
# @return [Hash] the record data in Spyke format
|
190
|
-
def prepare_record_data(json_data)
|
191
|
-
out = { id: json_data[:recordId].to_i, mod_id: json_data[:modId] }
|
192
|
-
out.merge!(json_data[:fieldData])
|
193
|
-
out.merge!(prepare_portal_data(json_data[:portalData])) if json_data[:portalData]
|
194
|
-
out
|
195
|
-
end
|
196
|
-
|
197
|
-
# Extracts `recordId` and strips the `"PortalName::"` field prefix for each
|
198
|
-
# portal
|
199
|
-
#
|
200
|
-
# Sample `json_portal_data`:
|
201
|
-
#
|
202
|
-
# "portalData": {
|
203
|
-
# "Orders":[
|
204
|
-
# { "Orders::DeliveryDate": "3/7/2017", "recordId": "23" }
|
205
|
-
# ]
|
206
|
-
# }
|
207
|
-
#
|
208
|
-
# @param json_portal_data [Hash]
|
209
|
-
# @return [Hash] the portal data in Spyke format
|
210
|
-
def prepare_portal_data(json_portal_data)
|
211
|
-
json_portal_data.each_with_object({}) do |(portal_name, portal_records), out|
|
212
|
-
portal_options = @model.portal_options[portal_name.to_s] || {}
|
213
|
-
|
214
|
-
out[portal_name] =
|
215
|
-
portal_records.map do |portal_fields|
|
216
|
-
attributes = { id: portal_fields[:recordId].to_i }
|
217
|
-
attributes[:mod_id] = portal_fields[:modId] if portal_fields[:modId]
|
218
|
-
|
219
|
-
prefix = portal_options[:attribute_prefix] || portal_name
|
220
|
-
prefix_matcher = /\A#{prefix}::/
|
221
|
-
|
222
|
-
portal_fields.each do |k, v|
|
223
|
-
next if :recordId == k || :modId == k
|
224
|
-
attributes[k.to_s.gsub(prefix_matcher, "").to_sym] = v
|
225
|
-
end
|
226
|
-
|
227
|
-
attributes
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
# @param env [Faraday::Env]
|
233
|
-
# @return [Boolean]
|
234
|
-
def single_record_request?(env)
|
235
|
-
env.method == :get && env.url.path.match(SINGLE_RECORD_RE)
|
236
|
-
end
|
237
|
-
|
238
|
-
# (see #single_record_request?)
|
239
|
-
def multiple_records_request?(env)
|
240
|
-
env.method == :get && env.url.path.match(MULTIPLE_RECORDS_RE)
|
241
|
-
end
|
242
|
-
|
243
|
-
# (see #single_record_request?)
|
244
|
-
def find_request?(env)
|
245
|
-
env.method == :post && env.url.path.match(FIND_RECORDS_RE)
|
246
|
-
end
|
247
|
-
|
248
|
-
# (see #single_record_request?)
|
249
|
-
def update_request?(env)
|
250
|
-
env.method == :patch && env.url.path.match(SINGLE_RECORD_RE)
|
251
|
-
end
|
252
|
-
|
253
|
-
# (see #single_record_request?)
|
254
|
-
def create_request?(env)
|
255
|
-
env.method == :post && env.url.path.match(MULTIPLE_RECORDS_RE)
|
256
|
-
end
|
257
|
-
|
258
|
-
# (see #single_record_request?)
|
259
|
-
def container_upload_request?(env)
|
260
|
-
env.method == :post && env.url.path.match(CONTAINER_RE)
|
261
|
-
end
|
262
|
-
|
263
|
-
# (see #single_record_request?)
|
264
|
-
def delete_request?(env)
|
265
|
-
env.method == :delete && env.url.path.match(SINGLE_RECORD_RE)
|
266
|
-
end
|
267
|
-
|
268
|
-
def execute_script_request?(env)
|
269
|
-
env.method == :get && env.url.path.match(SCRIPT_REQUEST_RE)
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|