grape-jsonapi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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