grape-jsonapi 1.0.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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +65 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +15 -0
  5. data/CHANGELOG.md +52 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +219 -0
  8. data/LICENSE +21 -0
  9. data/README.md +88 -0
  10. data/Rakefile +3 -0
  11. data/grape-jsonapi.gemspec +29 -0
  12. data/lib/grape_jsonapi.rb +11 -0
  13. data/lib/grape_jsonapi/deprecated/formatter.rb +25 -0
  14. data/lib/grape_jsonapi/deprecated/parser.rb +21 -0
  15. data/lib/grape_jsonapi/endpoint_extension.rb +12 -0
  16. data/lib/grape_jsonapi/formatter.rb +92 -0
  17. data/lib/grape_jsonapi/parser.rb +195 -0
  18. data/lib/grape_jsonapi/version.rb +7 -0
  19. data/spec/lib/grape_jsonapi/deprecated.rb/formatter_spec.rb +7 -0
  20. data/spec/lib/grape_jsonapi/deprecated.rb/parser_spec.rb +7 -0
  21. data/spec/lib/grape_jsonapi/formatter_spec.rb +121 -0
  22. data/spec/lib/grape_jsonapi/parser_spec.rb +214 -0
  23. data/spec/lib/grape_jsonapi/version_spec.rb +5 -0
  24. data/spec/spec_helper.rb +6 -0
  25. data/spec/support/models/blog_post.rb +17 -0
  26. data/spec/support/models/db_record.rb +19 -0
  27. data/spec/support/models/foo.rb +28 -0
  28. data/spec/support/models/user.rb +27 -0
  29. data/spec/support/models/user_admin.rb +31 -0
  30. data/spec/support/serializers/another_blog_post_serializer.rb +9 -0
  31. data/spec/support/serializers/another_user_serializer.rb +7 -0
  32. data/spec/support/serializers/blog_post_serializer.rb +9 -0
  33. data/spec/support/serializers/db_record_serializer.rb +18 -0
  34. data/spec/support/serializers/foo_serializer.rb +23 -0
  35. data/spec/support/serializers/user_serializer.rb +9 -0
  36. metadata +165 -0
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GrapeSwagger::Jsonapi::Parser do
4
+ let(:model) { BlogPostSerializer }
5
+ let(:endpoint) { '/' }
6
+
7
+ describe 'attr_readers' do
8
+ subject { described_class.new(model, endpoint) }
9
+
10
+ it { expect(subject.model).to eq model }
11
+ it { expect(subject.endpoint).to eq endpoint }
12
+ end
13
+
14
+ describe 'instance methods' do
15
+ describe '#call' do
16
+ subject { described_class.new(model, endpoint).call }
17
+
18
+ it 'return a hash defining the schema' do
19
+ expect(subject).to eq({
20
+ data: {
21
+ type: :object,
22
+ properties: {
23
+ id: { type: :integer },
24
+ type: { type: :string },
25
+ attributes: {
26
+ type: :object,
27
+ properties: {
28
+ title: { type: :string },
29
+ body: { type: :string }
30
+ }
31
+ },
32
+ relationships: {
33
+ type: :object,
34
+ properties: {
35
+ user: {
36
+ type: :object,
37
+ properties: {
38
+ data: {
39
+ type: :object,
40
+ properties: {
41
+ id: { type: :integer },
42
+ type: { type: :string }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ },
50
+ example: {
51
+ id: 1,
52
+ type: :blog_post,
53
+ attributes: {
54
+ title: 'Example string',
55
+ body: 'Example string'
56
+ },
57
+ relationships: {
58
+ user: {
59
+ data: {
60
+ id: 1,
61
+ type: :user
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ })
68
+ end
69
+
70
+ context 'when the serializer contains sensitive information' do
71
+ let(:model) { UserSerializer } # contains :password attribute
72
+
73
+ it 'return a hash defining the schema filtering the sensitive attributes' do
74
+ expect(subject).to eq({
75
+ data: {
76
+ type: :object,
77
+ properties: {
78
+ id: { type: :integer },
79
+ type: { type: :string },
80
+ attributes: {
81
+ type: :object,
82
+ properties: {
83
+ first_name: { type: :string },
84
+ last_name: { type: :string },
85
+ email: { type: :string }
86
+ # password: { type: :string }, FILTERED
87
+ }
88
+ },
89
+ relationships: {
90
+ type: :object,
91
+ properties: {
92
+ blog_posts: {
93
+ type: :object,
94
+ properties: {
95
+ data: {
96
+ type: :array,
97
+ items: {
98
+ type: :object,
99
+ properties: {
100
+ id: { type: :integer },
101
+ type: { type: :string }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ },
110
+ example: {
111
+ id: 1,
112
+ type: :user,
113
+ attributes: {
114
+ first_name: 'Example string',
115
+ last_name: 'Example string',
116
+ email: 'Example string'
117
+ # password: "Example string", FILTERED
118
+ },
119
+ relationships: {
120
+ blog_posts: {
121
+ data: [{ id: 1, type: :blog_post }]
122
+ }
123
+ }
124
+ }
125
+ }
126
+ })
127
+ end
128
+ end
129
+
130
+ context 'when schema has an association with :key different than association name' do
131
+ let(:model) { FooSerializer }
132
+
133
+ it 'includes associations as defined by :key attributes' do
134
+ expect(subject[:data][:properties][:relationships][:properties]).to include(:foo_bar, :foo_fizz, :foo_buzzes)
135
+ end
136
+ end
137
+
138
+ context 'when serializer has additional schema specified' do
139
+ let(:model) { FooSerializer }
140
+
141
+ it 'is deep-merged into the returned schema' do
142
+ expect(subject[:data][:example][:attributes]).to include(xyz: 'foobar')
143
+ end
144
+ end
145
+
146
+ context 'when serializer has DB-backed model' do
147
+ let(:model) { DbRecordSerializer }
148
+
149
+ before { allow(SecureRandom).to receive(:uuid).and_return 'fakeuuid' }
150
+
151
+ it 'contains examples for corresponding data types' do
152
+ expect(subject[:data][:example][:attributes]).to include(
153
+ string_attribute: be_a(String),
154
+ uuid_attribute: 'fakeuuid',
155
+ integer_attribute: be_a(Integer),
156
+ text_attribute: be_a(String),
157
+ datetime_attribute: satisfy { |val| Time.parse(val).is_a? Time },
158
+ date_attribute: satisfy { |val| Date.parse(val).is_a? Date },
159
+ boolean_attribute: be_a(TrueClass).or(be_a(FalseClass)),
160
+ array_attribute: be_a(Array)
161
+ )
162
+ end
163
+ end
164
+
165
+ context 'when the serializer doesn\'t have any attributes' do
166
+ let(:model) { AnotherBlogPostSerializer } # no attributes
167
+
168
+ it 'return a hash defining the schema with empty attributes' do
169
+ expect(subject).to eq({
170
+ data: {
171
+ type: :object,
172
+ properties: {
173
+ id: { type: :integer },
174
+ type: { type: :string },
175
+ attributes: {
176
+ type: :object,
177
+ properties: {}
178
+ },
179
+ relationships: {
180
+ type: :object,
181
+ properties: {
182
+ user: {
183
+ properties: {
184
+ data: {
185
+ properties: {
186
+ id: { type: :integer },
187
+ type: { type: :string }
188
+ },
189
+ type: :object
190
+ }
191
+ },
192
+ type: :object
193
+ }
194
+ }
195
+ }
196
+ },
197
+ example: {
198
+ id: 1,
199
+ type: :blog_post,
200
+ attributes: {
201
+ },
202
+ relationships: {
203
+ user: {
204
+ data: { id: 1, type: :user }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ })
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Grape::Jsonapi::VERSION do
4
+ it { is_expected.to eq '1.0.0'.freeze }
5
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+ require 'grape_jsonapi'
5
+
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BlogPost
4
+ include ActiveModel::Serialization
5
+
6
+ attr_accessor :id, :title, :body
7
+
8
+ def initialize(params = {})
9
+ params.each do |k, v|
10
+ instance_variable_set("@#{k}", v) unless v.nil?
11
+ end
12
+ end
13
+
14
+ def user_id
15
+ nil
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ require 'active_record'
3
+
4
+ class DbRecord < ActiveRecord::Base
5
+ def self.columns
6
+ [
7
+ # adhering to the ActiveRecord::ConnectionAdapters::Column contract
8
+ OpenStruct.new(name: 'id', sql_type: 'integer', type: :integer),
9
+ OpenStruct.new(name: 'string_attribute', sql_type: 'character varying(255)', type: :string),
10
+ OpenStruct.new(name: 'uuid_attribute', sql_type: 'uuid', type: :uuid),
11
+ OpenStruct.new(name: 'integer_attribute', sql_type: 'integer', type: :integer),
12
+ OpenStruct.new(name: 'text_attribute', sql_type: 'text', type: :text),
13
+ OpenStruct.new(name: 'datetime_attribute', sql_type: 'timestamp without time zone', type: :datetime),
14
+ OpenStruct.new(name: 'date_attribute', sql_type: 'date', type: :date),
15
+ OpenStruct.new(name: 'boolean_attribute', sql_type: 'boolean', type: :boolean),
16
+ OpenStruct.new(name: 'array_attribute', sql_type: 'integer[]', type: :array)
17
+ ]
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Foo
4
+ include ActiveModel::Serialization
5
+
6
+ attr_accessor :id, :bar_id
7
+
8
+ # belongs_to :bar
9
+ # has_one :fizz
10
+ # has_many :buzzes
11
+
12
+ def initialize(params = {})
13
+ params.each do |k, v|
14
+ instance_variable_set("@#{k}", v) unless v.nil?
15
+ end
16
+ end
17
+
18
+ def attributes
19
+ {
20
+ 'id' => nil,
21
+ 'bar_id' => nil
22
+ }
23
+ end
24
+
25
+ def buzz_ids
26
+ []
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class User
4
+ include ActiveModel::Serialization
5
+
6
+ attr_accessor :id, :first_name, :last_name, :password, :email
7
+
8
+ def initialize(params = {})
9
+ params.each do |k, v|
10
+ instance_variable_set("@#{k}", v) unless v.nil?
11
+ end
12
+ end
13
+
14
+ def attributes
15
+ {
16
+ 'id' => nil,
17
+ 'first_name' => nil,
18
+ 'last_name' => nil,
19
+ 'password' => nil,
20
+ 'email' => nil
21
+ }
22
+ end
23
+
24
+ def blog_post_ids
25
+ []
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UserAdmin
4
+ include ActiveModel::Serialization
5
+
6
+ attr_accessor :id, :first_name, :last_name, :password, :email
7
+
8
+ def initialize(params = {})
9
+ params.each do |k, v|
10
+ instance_variable_set("@#{k}", v) unless v.nil?
11
+ end
12
+ end
13
+
14
+ def attributes
15
+ {
16
+ 'id' => nil,
17
+ 'first_name' => nil,
18
+ 'last_name' => nil,
19
+ 'password' => nil,
20
+ 'email' => nil
21
+ }
22
+ end
23
+
24
+ def blog_post_ids
25
+ []
26
+ end
27
+
28
+ def model_name
29
+ ActiveModel::Name.new(User)
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AnotherBlogPostSerializer
4
+ include JSONAPI::Serializer
5
+
6
+ set_type :blog_post
7
+
8
+ belongs_to :user
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AnotherUserSerializer
4
+ include JSONAPI::Serializer
5
+
6
+ attributes :email
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BlogPostSerializer
4
+ include JSONAPI::Serializer
5
+
6
+ belongs_to :user
7
+
8
+ attributes :title, :body
9
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record'
4
+
5
+ class DbRecordSerializer
6
+ include JSONAPI::Serializer
7
+
8
+ attributes(
9
+ :string_attribute,
10
+ :uuid_attribute,
11
+ :integer_attribute,
12
+ :text_attribute,
13
+ :datetime_attribute,
14
+ :date_attribute,
15
+ :boolean_attribute,
16
+ :array_attribute
17
+ )
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FooSerializer
4
+ include JSONAPI::Serializer
5
+
6
+ belongs_to :bar, key: :foo_bar
7
+ has_one :fizz, key: :foo_fizz
8
+ has_many :buzzes, key: :foo_buzzes
9
+
10
+ attribute :xyz
11
+
12
+ def self.additional_schema
13
+ {
14
+ data: {
15
+ example: {
16
+ attributes: {
17
+ xyz: 'foobar'
18
+ }
19
+ }
20
+ }
21
+ }
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UserSerializer
4
+ include JSONAPI::Serializer
5
+
6
+ has_many :blog_posts
7
+
8
+ attributes :first_name, :last_name, :email
9
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape-jsonapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Emmanuel Cousin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: grape
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jsonapi-serializer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 4.2.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 4.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Provides a Formatter for the Grape API DSL to emit objects serialized
84
+ with jsonapi-serializer.
85
+ email:
86
+ - emmanuel_cousin@hotmail.fr
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".circleci/config.yml"
92
+ - ".rspec"
93
+ - ".rubocop.yml"
94
+ - CHANGELOG.md
95
+ - Gemfile
96
+ - Gemfile.lock
97
+ - LICENSE
98
+ - README.md
99
+ - Rakefile
100
+ - grape-jsonapi.gemspec
101
+ - lib/grape_jsonapi.rb
102
+ - lib/grape_jsonapi/deprecated/formatter.rb
103
+ - lib/grape_jsonapi/deprecated/parser.rb
104
+ - lib/grape_jsonapi/endpoint_extension.rb
105
+ - lib/grape_jsonapi/formatter.rb
106
+ - lib/grape_jsonapi/parser.rb
107
+ - lib/grape_jsonapi/version.rb
108
+ - spec/lib/grape_jsonapi/deprecated.rb/formatter_spec.rb
109
+ - spec/lib/grape_jsonapi/deprecated.rb/parser_spec.rb
110
+ - spec/lib/grape_jsonapi/formatter_spec.rb
111
+ - spec/lib/grape_jsonapi/parser_spec.rb
112
+ - spec/lib/grape_jsonapi/version_spec.rb
113
+ - spec/spec_helper.rb
114
+ - spec/support/models/blog_post.rb
115
+ - spec/support/models/db_record.rb
116
+ - spec/support/models/foo.rb
117
+ - spec/support/models/user.rb
118
+ - spec/support/models/user_admin.rb
119
+ - spec/support/serializers/another_blog_post_serializer.rb
120
+ - spec/support/serializers/another_user_serializer.rb
121
+ - spec/support/serializers/blog_post_serializer.rb
122
+ - spec/support/serializers/db_record_serializer.rb
123
+ - spec/support/serializers/foo_serializer.rb
124
+ - spec/support/serializers/user_serializer.rb
125
+ homepage: https://github.com/EmCousin/grape-jsonapi
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 2.6.0
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubygems_version: 3.0.3
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Use grape-jsonapi in grape
148
+ test_files:
149
+ - spec/lib/grape_jsonapi/deprecated.rb/formatter_spec.rb
150
+ - spec/lib/grape_jsonapi/deprecated.rb/parser_spec.rb
151
+ - spec/lib/grape_jsonapi/formatter_spec.rb
152
+ - spec/lib/grape_jsonapi/parser_spec.rb
153
+ - spec/lib/grape_jsonapi/version_spec.rb
154
+ - spec/spec_helper.rb
155
+ - spec/support/models/blog_post.rb
156
+ - spec/support/models/db_record.rb
157
+ - spec/support/models/foo.rb
158
+ - spec/support/models/user.rb
159
+ - spec/support/models/user_admin.rb
160
+ - spec/support/serializers/another_blog_post_serializer.rb
161
+ - spec/support/serializers/another_user_serializer.rb
162
+ - spec/support/serializers/blog_post_serializer.rb
163
+ - spec/support/serializers/db_record_serializer.rb
164
+ - spec/support/serializers/foo_serializer.rb
165
+ - spec/support/serializers/user_serializer.rb