Skip to content
This repository has been archived by the owner on Aug 6, 2023. It is now read-only.

Commit

Permalink
Allow optional relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
maddiesch committed Nov 13, 2018
1 parent d9f9dbb commit b8b8b26
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 8 deletions.
1 change: 1 addition & 0 deletions lib/onsi/error_responder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def notify_unhandled_exception(exception)

def error_metadata(error)
return nil unless Rails.configuration.consider_all_requests_local

{
exception: {
'_note' => '`exception` will be removed if Rails.configuration.consider_all_requests_local is false',
Expand Down
2 changes: 2 additions & 0 deletions lib/onsi/includes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def load_included
included.each do |name|
fetcher = fetch_methods[name]
next if fetcher.nil?

results = fetcher.call
root[name] = results
end
Expand All @@ -35,6 +36,7 @@ def add_fetch_method(name, &block)
if block.nil?
raise ArgumentError, "Must specify a block for fetch_#{name}"
end

fetch_methods[name.to_sym] = block
end

Expand Down
34 changes: 30 additions & 4 deletions lib/onsi/params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,52 @@ def parse_json(body, attributes = [], relationships = [])

def permit_attributes(data, attributes)
return {} if Array(attributes).empty?

data.require(:attributes).permit(*attributes)
end

def permit_relationships(data, relationships)
return {} if Array(relationships).empty?

rels = data.require(:relationships)
{}.tap do |obj|
relationships.each do |name|
next if rels[name].nil?
resource = rels.require(name).require(:data)
optional, true_name = parse_relationship_name(name)
next unless rels.key?(true_name)

resource = fetch_relationship(rels, optional, true_name)
case resource
when nil
obj["#{true_name}_id".to_sym] = nil
when Array
ids = resource.map { |r| parse_relationship(r).last }
obj["#{name.to_s.singularize}_ids".to_sym] = ids
obj["#{true_name.to_s.singularize}_ids".to_sym] = ids
else
_type, id = parse_relationship(resource)
obj["#{name}_id".to_sym] = id
obj["#{true_name}_id".to_sym] = id
end
end
end
end

def fetch_relationship(rels, optional, name)
if optional
payload = rels.require(name).permit!.to_h
if payload[:data].is_a?(Array)
return []
end

nil
else
rels.require(name).require(:data)
end
end

def parse_relationship_name(name)
name = name.to_s
[name.start_with?('?'), name.gsub(/\A\?/, '')]
end

def parse_relationship(data)
[
data.require(:type),
Expand Down Expand Up @@ -108,6 +132,7 @@ def require(key)
if value.nil?
raise MissingReqiredAttribute.new("Missing attribute #{key}", key)
end

value
end

Expand Down Expand Up @@ -180,6 +205,7 @@ def default_attributes
raw_attrs = attributes.to_h.symbolize_keys
defaults.each_with_object({}) do |(key, value), object|
next if raw_attrs.key?(key)

if value.respond_to?(:call)
object[key] = value.call
else
Expand Down
3 changes: 3 additions & 0 deletions lib/onsi/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,21 @@ def type
def append_relationships(root)
relationships = generate_relationships
return unless relationships.any?

root[RELATIONSHIPS_KEY] = relationships
end

def append_meta(root)
meta = generate_metadata
return unless meta.any?

root[META_KEY] = meta
end

def append_includes(root)
includes = generate_includes
return unless includes.any?

root[RELATIONSHIPS_KEY] ||= {}
root[RELATIONSHIPS_KEY].merge!(includes)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/onsi/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Onsi
VERSION = '0.7.0'.freeze
VERSION = '0.8.0'.freeze
end
6 changes: 3 additions & 3 deletions onsi.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ require 'onsi/version'
Gem::Specification.new do |spec|
spec.name = 'onsi'
spec.version = Onsi::VERSION
spec.authors = ['Skylar Schipper']
spec.email = ['me@skylarsch.com']
spec.authors = ['Maddie Schipper']
spec.email = ['me@maddiesch.com']

spec.summary = 'Format JSON API Responses'
spec.description = 'Format JSON API responses and parse inbound requests.'
spec.homepage = 'https://github.com/skylarsch/onsi'
spec.homepage = 'https://github.com/maddiesch/onsi'
spec.license = 'MIT'

spec.files = `git ls-files -z`.split("\x0").reject do |f|
Expand Down
90 changes: 90 additions & 0 deletions spec/onsi/params_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,96 @@

it { expect(subject.relationships).to eq(person_id: '7', access_token_ids: %w[1 2]) }
end

context 'given an optional relationship and nil data' do
let(:params) do
ActionController::Parameters.new(
data: {
type: 'person',
attributes: {
name: 'Skylar',
foo: 'Bar'
},
relationships: {
person: {
data: nil
},
access_tokens: {
data: [
{ type: 'access_token', id: '1' },
{ type: 'access_token', id: '2' }
]
}
}
}
)
end

subject { described_class.parse(params, %w[name], %w[?person access_tokens]) }

it { expect { subject }.to_not raise_error }

it { expect(subject.relationships).to eq(person_id: nil, access_token_ids: %w[1 2]) }
end

context 'given an optional relationship and empty array' do
let(:params) do
ActionController::Parameters.new(
data: {
type: 'person',
attributes: {
name: 'Skylar',
foo: 'Bar'
},
relationships: {
person: {
data: {
type: 'person',
id: '7'
}
},
access_tokens: {
data: []
}
}
}
)
end

subject { described_class.parse(params, [:name], %w[person ?access_tokens]) }

it { expect { subject }.to_not raise_error }

it { expect(subject.relationships).to eq(person_id: '7', access_token_ids: []) }
end

context 'given an expected key not in the body' do
let(:params) do
ActionController::Parameters.new(
data: {
type: 'person',
attributes: {
name: 'Skylar'
},
relationships: {
person: {
data: nil
},
access_tokens: {
data: [
{ type: 'access_token', id: '1' },
{ type: 'access_token', id: '2' }
]
}
}
}
)
end

subject { described_class.parse(params, %w[name foo], %w[?person access_tokens]) }

it { expect { subject }.to_not raise_error }
end
end

describe '.parse_json' do
Expand Down

0 comments on commit b8b8b26

Please sign in to comment.