class FmRest::Spyke::SpykeFormatter

Response Faraday middleware for converting FM API's response JSON into Spyke's expected format

Constants

CONTAINER_RE
FIND_RECORDS_RE
MULTIPLE_RECORDS_RE
SCRIPT_REQUEST_RE
SINGLE_RECORD_RE
VALIDATION_ERROR_RANGE

Public Class Methods

new(app, model) click to toggle source

@param app [#call] @param model [Class<FmRest::Spyke::Base>]

Calls superclass method
# File lib/fmrest/spyke/spyke_formatter.rb, line 32
def initialize(app, model)
  super(app)
  @model = model
end

Public Instance Methods

on_complete(env) click to toggle source

@param env [Faraday::Env]

# File lib/fmrest/spyke/spyke_formatter.rb, line 38
def on_complete(env)
  return unless env.body.is_a?(Hash)

  json = env.body

  case
  when single_record_request?(env)
    env.body = prepare_single_record(json)
  when multiple_records_request?(env), find_request?(env)
    env.body = prepare_collection(json)
  when create_request?(env), update_request?(env), delete_request?(env), container_upload_request?(env)
    env.body = prepare_save_response(json)
  when execute_script_request?(env)
    env.body = build_base_hash(json)
  else
    # Attempt to parse unknown requests too
    env.body = build_base_hash(json)
  end
end

Private Instance Methods

build_base_hash(json, include_errors = false) click to toggle source

@param json [Hash] @param include_errors [Boolean] @return [FmRest::Spyke::Metadata] the skeleton structure for a

Spyke-formatted response
# File lib/fmrest/spyke/spyke_formatter.rb, line 95
def build_base_hash(json, include_errors = false)
  {
    metadata: Metadata.new(
      prepare_messages(json),
      prepare_script_results(json),
      prepare_data_info(json)
    ).freeze,
    errors: include_errors ? prepare_errors(json) : {}
  }
end
container_upload_request?(env) click to toggle source

(see single_record_request?)

# File lib/fmrest/spyke/spyke_formatter.rb, line 260
def container_upload_request?(env)
  env.method == :post && env.url.path.match(CONTAINER_RE)
end
create_request?(env) click to toggle source

(see single_record_request?)

# File lib/fmrest/spyke/spyke_formatter.rb, line 255
def create_request?(env)
  env.method == :post && env.url.path.match(MULTIPLE_RECORDS_RE)
end
delete_request?(env) click to toggle source

(see single_record_request?)

# File lib/fmrest/spyke/spyke_formatter.rb, line 265
def delete_request?(env)
  env.method == :delete && env.url.path.match(SINGLE_RECORD_RE)
end
execute_script_request?(env) click to toggle source
# File lib/fmrest/spyke/spyke_formatter.rb, line 269
def execute_script_request?(env)
  env.method == :get && env.url.path.match(SCRIPT_REQUEST_RE)
end
find_request?(env) click to toggle source

(see single_record_request?)

# File lib/fmrest/spyke/spyke_formatter.rb, line 245
def find_request?(env)
  env.method == :post && env.url.path.match(FIND_RECORDS_RE)
end
multiple_records_request?(env) click to toggle source

(see single_record_request?)

# File lib/fmrest/spyke/spyke_formatter.rb, line 240
def multiple_records_request?(env)
  env.method == :get && env.url.path.match(MULTIPLE_RECORDS_RE)
end
prepare_collection(json) click to toggle source

(see prepare_save_response)

# File lib/fmrest/spyke/spyke_formatter.rb, line 83
def prepare_collection(json)
  data =
    json[:response][:data] &&
    json[:response][:data].map { |record_data| prepare_record_data(record_data) }

  build_base_hash(json).merge!(data: data)
end
prepare_data_info(json) click to toggle source

@param json [Hash] @return [OpenStruct] the script(s) execution results for

Spyke metadata format
# File lib/fmrest/spyke/spyke_formatter.rb, line 142
def prepare_data_info(json)
  data_info = json[:response] && json[:response][:dataInfo]

  return nil unless data_info.present?

  DataInfo.new(data_info).freeze
end
prepare_errors(json) click to toggle source

@param json [Hash] @return [Hash] the errors hash in Spyke format

# File lib/fmrest/spyke/spyke_formatter.rb, line 152
def prepare_errors(json)
  # Code 0 means "No Error"
  # https://fmhelp.filemaker.com/help/17/fmp/en/index.html#page/FMP_Help/error-codes.html
  return {} if json[:messages][0][:code].to_i == 0

  json[:messages].each_with_object(base: []) do |message, hash|
    # Only include validation errors
    next unless VALIDATION_ERROR_RANGE.include?(message[:code].to_i)

    hash[:base] << "#{message[:message]} (#{message[:code]})"
  end
end
prepare_messages(json) click to toggle source

@param json [Hash] @return [Array<OpenStruct>] the skeleton structure for a

Spyke-formatted response
# File lib/fmrest/spyke/spyke_formatter.rb, line 109
def prepare_messages(json)
  return [] unless json[:messages]
  json[:messages].map { |m| OpenStruct.new(m).freeze }.freeze
end
prepare_portal_data(json_portal_data) click to toggle source

Extracts `recordId` and strips the `“PortalName::”` field prefix for each portal

Sample `json_portal_data`:

"portalData": {
  "Orders":[
    { "Orders::DeliveryDate": "3/7/2017", "recordId": "23" }
  ]
}

@param json_portal_data [Hash] @return [Hash] the portal data in Spyke format

# File lib/fmrest/spyke/spyke_formatter.rb, line 211
def prepare_portal_data(json_portal_data)
  json_portal_data.each_with_object({}) do |(portal_name, portal_records), out|
    portal_options = @model.portal_options[portal_name.to_s] || {}

    out[portal_name] =
      portal_records.map do |portal_fields|
        attributes = { __record_id: portal_fields[:recordId] }
        attributes[:__mod_id] = portal_fields[:modId] if portal_fields[:modId]

        prefix = portal_options[:attribute_prefix] || portal_name
        prefix_matcher = /\A#{prefix}::/

        portal_fields.each do |k, v|
          next if :recordId == k || :modId == k
          attributes[k.to_s.gsub(prefix_matcher, "").to_sym] = v
        end

        attributes
      end
  end
end
prepare_record_data(json_data) click to toggle source

`json_data` is expected in this format:

{
  "fieldData": {
    "fieldName1" : "fieldValue1",
    "fieldName2" : "fieldValue2",
    ...
  },
  "portalData": {
    "portal1" : [
      { <portalRecord1> },
      { <portalRecord2> },
      ...
    ],
    "portal2" : [
      { <portalRecord1> },
      { <portalRecord2> },
      ...
    ]
  },
  "modId": <Id_for_last_modification>,
  "recordId": <Unique_internal_ID_for_this_record>
}

@param json_data [Hash] @return [Hash] the record data in Spyke format

# File lib/fmrest/spyke/spyke_formatter.rb, line 191
def prepare_record_data(json_data)
  out = { __record_id: json_data[:recordId], __mod_id: json_data[:modId] }
  out.merge!(json_data[:fieldData])
  out.merge!(prepare_portal_data(json_data[:portalData])) if json_data[:portalData]
  out
end
prepare_save_response(json) click to toggle source

@param json [Hash] @return [Hash] the response in Spyke format

# File lib/fmrest/spyke/spyke_formatter.rb, line 62
def prepare_save_response(json)
  response = json[:response]

  data = {}
  data[:__mod_id] = response[:modId] if response[:modId]
  data[:__record_id] = response[:recordId] if response[:recordId]
  data[:__new_portal_record_info] = response[:newPortalRecordInfo] if response[:newPortalRecordInfo]

  build_base_hash(json, true).merge!(data: data)
end
prepare_script_results(json) click to toggle source

@param json [Hash] @return [OpenStruct] the script(s) execution results for Spyke metadata

format
# File lib/fmrest/spyke/spyke_formatter.rb, line 117
def prepare_script_results(json)
  results = {}

  [:prerequest, :presort].each do |s|
    if json[:response][:"scriptError.#{s}"]
      results[s] = OpenStruct.new(
        result: json[:response][:"scriptResult.#{s}"],
        error:  json[:response][:"scriptError.#{s}"]
      ).freeze
    end
  end

  if json[:response][:scriptError]
    results[:after] = OpenStruct.new(
      result: json[:response][:scriptResult],
      error:  json[:response][:scriptError]
    ).freeze
  end

  results.present? ? OpenStruct.new(results).freeze : nil
end
prepare_single_record(json) click to toggle source

(see prepare_save_response)

# File lib/fmrest/spyke/spyke_formatter.rb, line 74
def prepare_single_record(json)
  data =
    json[:response][:data] &&
    prepare_record_data(json[:response][:data].first)

  build_base_hash(json).merge!(data: data)
end
single_record_request?(env) click to toggle source

@param env [Faraday::Env] @return [Boolean]

# File lib/fmrest/spyke/spyke_formatter.rb, line 235
def single_record_request?(env)
  env.method == :get && env.url.path.match(SINGLE_RECORD_RE)
end
update_request?(env) click to toggle source

(see single_record_request?)

# File lib/fmrest/spyke/spyke_formatter.rb, line 250
def update_request?(env)
  env.method == :patch && env.url.path.match(SINGLE_RECORD_RE)
end