Skip to content

Commit 3d70c8b

Browse files
Make entity fields required by default
1 parent df415ab commit 3d70c8b

File tree

3 files changed

+82
-41
lines changed

3 files changed

+82
-41
lines changed

.rubocop_todo.yml

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This configuration was generated by
22
# `rubocop --auto-gen-config`
3-
# on 2024-09-09 22:35:29 UTC using RuboCop version 1.66.1.
3+
# on 2025-05-08 20:42:22 UTC using RuboCop version 1.75.5.
44
# The point is for the user to remove these configuration records
55
# one by one as the offenses are removed from the code base.
66
# Note that changes in the inspected code, or installation of new
@@ -44,7 +44,7 @@ Metrics/AbcSize:
4444
# Offense count: 2
4545
# Configuration parameters: CountComments, CountAsOne.
4646
Metrics/ClassLength:
47-
Max: 112
47+
Max: 117
4848

4949
# Offense count: 2
5050
# Configuration parameters: AllowedMethods, AllowedPatterns.
@@ -64,7 +64,7 @@ Metrics/PerceivedComplexity:
6464
# Offense count: 5
6565
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
6666
# SupportedStyles: snake_case, normalcase, non_integer
67-
# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
67+
# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
6868
Naming/VariableNumber:
6969
Exclude:
7070
- 'spec/grape-swagger/entities/response_model_spec.rb'
@@ -100,9 +100,9 @@ RSpec/DescribeClass:
100100
# Offense count: 4
101101
# Configuration parameters: CountAsOne.
102102
RSpec/ExampleLength:
103-
Max: 187
103+
Max: 211
104104

105-
# Offense count: 24
105+
# Offense count: 26
106106
RSpec/LeakyConstantDeclaration:
107107
Exclude:
108108
- 'spec/grape-swagger/entities/response_model_spec.rb'
@@ -118,14 +118,14 @@ RSpec/MultipleDescribes:
118118
RSpec/MultipleExpectations:
119119
Max: 11
120120

121-
# Offense count: 20
121+
# Offense count: 21
122122
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
123123
# SupportedStyles: always, named_only
124124
RSpec/NamedSubject:
125125
Exclude:
126126
- 'spec/grape-swagger/entities/response_model_spec.rb'
127127

128-
# Offense count: 39
128+
# Offense count: 40
129129
# Configuration parameters: AllowedGroups.
130130
RSpec/NestedGroups:
131131
Max: 5

lib/grape-swagger/entity/parser.rb

+9-3
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,15 @@ def parse_nested(entity_name, entity_options, parent_model = nil)
135135
end
136136

137137
def required_params(params)
138-
params.select { |_, options| options.fetch(:documentation, {}).fetch(:required, false) }
139-
.map { |(key, options)| [options.fetch(:as, key), options] }
140-
.map(&:first)
138+
params.each_with_object(Set.new) do |(key, options), accum|
139+
required = if options.fetch(:documentation, {}).key?(:required)
140+
options.dig(:documentation, :required)
141+
else
142+
!options.key?(:if) && !options.key?(:unless)
143+
end
144+
145+
accum.add(options.fetch(:as, key)) if required
146+
end.to_a
141147
end
142148

143149
def with_required(hash, required)

spec/grape-swagger/entities/response_model_spec.rb

+66-31
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def app
4444
'properties' => {
4545
'code' => { 'type' => 'string', 'description' => 'Error code' },
4646
'message' => { 'type' => 'string', 'description' => 'Error message' }
47-
}
47+
},
48+
'required' => %w[code message]
4849
)
4950

5051
expect(subject['definitions'].keys).to include 'ThisApi_Entities_Something'
@@ -66,24 +67,32 @@ def app
6667
'code' => { 'type' => 'string', 'description' => 'Error code' },
6768
'message' => { 'type' => 'string', 'description' => 'Error message' },
6869
'attr' => { 'type' => 'string', 'description' => 'Attribute' } },
69-
'required' => ['attr']
70+
'required' => %w[text colors hidden_attr created_at kind kind2 kind3 tags relation
71+
attr code message]
7072
)
7173

7274
expect(subject['definitions'].keys).to include 'ThisApi_Entities_Kind'
7375
expect(subject['definitions']['ThisApi_Entities_Kind']).to eq(
74-
'type' => 'object', 'properties' => { 'title' => { 'type' => 'string', 'description' => 'Title of the kind.' },
75-
'content' => { 'description' => 'Content', 'type' => 'string',
76-
'x-some' => 'stuff' } }
76+
'type' => 'object',
77+
'properties' => {
78+
'title' => { 'type' => 'string', 'description' => 'Title of the kind.' },
79+
'content' => { 'type' => 'string', 'description' => 'Content', 'x-some' => 'stuff' }
80+
},
81+
'required' => %w[title content]
7782
)
7883

7984
expect(subject['definitions'].keys).to include 'ThisApi_Entities_Relation'
8085
expect(subject['definitions']['ThisApi_Entities_Relation']).to eq(
81-
'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } }
86+
'type' => 'object',
87+
'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } },
88+
'required' => %w[name]
8289
)
8390

8491
expect(subject['definitions'].keys).to include 'ThisApi_Entities_Tag'
8592
expect(subject['definitions']['ThisApi_Entities_Tag']).to eq(
86-
'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } }
93+
'type' => 'object',
94+
'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } },
95+
'required' => %w[name]
8796
)
8897
end
8998
end
@@ -134,8 +143,10 @@ class Nested < Grape::Entity
134143
end
135144
expose :nested_required do
136145
expose :some1, documentation: { required: true, desc: 'Required some 1' }
137-
expose :attr, as: :some2, documentation: { required: true, desc: 'Required some 2' }
138-
expose :some3, documentation: { desc: 'Optional some 3' }
146+
expose :attr, as: :some2, documentation: { desc: 'Required some 2' }
147+
expose :some3, documentation: { required: false, desc: 'Optional some 3' }
148+
expose :some4, if: -> { true }, documentation: { desc: 'Optional some 4' }
149+
expose :some5, unless: -> { true }, documentation: { desc: 'Optional some 5' }
139150
end
140151

141152
expose :nested_array, documentation: { type: 'Array', desc: 'Nested array' } do
@@ -235,7 +246,8 @@ def app
235246
'uuid' => { 'type' => 'string', 'format' => 'own', 'description' => 'customer uuid',
236247
'example' => 'e3008fba-d53d-4bcc-a6ae-adc56dff8020' },
237248
'color' => { 'type' => 'string', 'enum' => %w[red blue], 'description' => 'Color' }
238-
}
249+
},
250+
'required' => %w[guid uuid color]
239251
)
240252
expect(subject['TheseApi_Entities_Kind']).to eql(
241253
'type' => 'object',
@@ -244,30 +256,37 @@ def app
244256
'readOnly' => true },
245257
'title' => { 'type' => 'string', 'description' => 'Title of the kind.', 'readOnly' => false },
246258
'type' => { 'type' => 'string', 'description' => 'Type of the kind.', 'readOnly' => true }
247-
}
259+
},
260+
'required' => %w[id title type]
248261
)
249262
expect(subject['TheseApi_Entities_Tag']).to eql(
250-
'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name',
251-
'example' => 'random_tag' } }
263+
'type' => 'object',
264+
'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name', 'example' => 'random_tag' } },
265+
'required' => %w[name]
252266
)
253267
expect(subject['TheseApi_Entities_Relation']).to eql(
254-
'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } }
268+
'type' => 'object',
269+
'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } },
270+
'required' => %w[name]
255271
)
256272
expect(subject['TheseApi_Entities_Nested']).to eq(
273+
'type' => 'object',
257274
'properties' => {
258275
'nested' => {
259276
'type' => 'object',
260277
'properties' => {
261278
'some1' => { 'type' => 'string', 'description' => 'Nested some 1' },
262279
'some2' => { 'type' => 'string', 'description' => 'Nested some 2' }
263280
},
264-
'description' => 'Nested entity'
281+
'description' => 'Nested entity',
282+
'required' => %w[some1 some2]
265283
},
266284
'aliased' => {
267285
'type' => 'object',
268286
'properties' => {
269287
'some1' => { 'type' => 'string', 'description' => 'Alias some 1' }
270-
}
288+
},
289+
'required' => %w[some1]
271290
},
272291
'deep_nested' => {
273292
'type' => 'object',
@@ -277,17 +296,21 @@ def app
277296
'properties' => {
278297
'level_2' => { 'type' => 'string', 'description' => 'Level 2' }
279298
},
280-
'description' => 'More deepest nested entity'
299+
'description' => 'More deepest nested entity',
300+
'required' => %w[level_2]
281301
}
282302
},
283-
'description' => 'Deep nested entity'
303+
'description' => 'Deep nested entity',
304+
'required' => %w[level_1]
284305
},
285306
'nested_required' => {
286307
'type' => 'object',
287308
'properties' => {
288309
'some1' => { 'type' => 'string', 'description' => 'Required some 1' },
289310
'some2' => { 'type' => 'string', 'description' => 'Required some 2' },
290-
'some3' => { 'type' => 'string', 'description' => 'Optional some 3' }
311+
'some3' => { 'type' => 'string', 'description' => 'Optional some 3' },
312+
'some4' => { 'type' => 'string', 'description' => 'Optional some 4' },
313+
'some5' => { 'type' => 'string', 'description' => 'Optional some 5' }
291314
},
292315
'required' => %w[some1 some2]
293316
},
@@ -298,14 +321,16 @@ def app
298321
'properties' => {
299322
'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'Collection element id' },
300323
'name' => { 'type' => 'string', 'description' => 'Collection element name' }
301-
}
324+
},
325+
'required' => %w[id name]
302326
},
303327
'description' => 'Nested array'
304328
}
305329
},
306-
'type' => 'object'
330+
'required' => %w[nested aliased deep_nested nested_required nested_array]
307331
)
308332
expect(subject['TheseApi_Entities_NestedChild']).to eq(
333+
'type' => 'object',
309334
'properties' => {
310335
'nested' => {
311336
'type' => 'object',
@@ -314,14 +339,16 @@ def app
314339
'some2' => { 'type' => 'string', 'description' => 'Nested some 2' },
315340
'some3' => { 'type' => 'string', 'description' => 'Nested some 3' }
316341
},
317-
'description' => 'Nested entity'
342+
'description' => 'Nested entity',
343+
'required' => %w[some1 some2 some3]
318344
},
319345
'aliased' => {
320346
'type' => 'object',
321347
'properties' => {
322348
'some1' => { 'type' => 'string', 'description' => 'Alias some 1' },
323349
'some2' => { 'type' => 'string', 'description' => 'Alias some 2' }
324-
}
350+
},
351+
'required' => %w[some1 some2]
325352
},
326353
'deep_nested' => {
327354
'type' => 'object',
@@ -337,20 +364,25 @@ def app
337364
'description' => 'Level 3'
338365
}
339366
},
340-
'description' => 'Level 2'
367+
'description' => 'Level 2',
368+
'required' => %w[level_3]
341369
}
342370
},
343-
'description' => 'More deepest nested entity'
371+
'description' => 'More deepest nested entity',
372+
'required' => %w[level_2]
344373
}
345374
},
346-
'description' => 'Deep nested entity'
375+
'description' => 'Deep nested entity',
376+
'required' => %w[level_1]
347377
},
348378
'nested_required' => {
349379
'type' => 'object',
350380
'properties' => {
351381
'some1' => { 'type' => 'string', 'description' => 'Required some 1' },
352382
'some2' => { 'type' => 'string', 'description' => 'Required some 2' },
353-
'some3' => { 'type' => 'string', 'description' => 'Optional some 3' }
383+
'some3' => { 'type' => 'string', 'description' => 'Optional some 3' },
384+
'some4' => { 'type' => 'string', 'description' => 'Optional some 4' },
385+
'some5' => { 'type' => 'string', 'description' => 'Optional some 5' }
354386
},
355387
'required' => %w[some1 some2]
356388
},
@@ -362,12 +394,13 @@ def app
362394
'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'Collection element id' },
363395
'name' => { 'type' => 'string', 'description' => 'Collection element name' },
364396
'category' => { 'type' => 'string', 'description' => 'Collection element category' }
365-
}
397+
},
398+
'required' => %w[id name category]
366399
},
367400
'description' => 'Nested array'
368401
}
369402
},
370-
'type' => 'object'
403+
'required' => %w[nested aliased deep_nested nested_required nested_array]
371404
)
372405
expect(subject['TheseApi_Entities_Polymorphic']).to eql(
373406
'type' => 'object',
@@ -380,14 +413,15 @@ def app
380413
)
381414

382415
expect(subject['TheseApi_Entities_MixedType']).to eql(
416+
'type' => 'object',
383417
'properties' => {
384418
'tags' => {
385419
'description' => 'Tags',
386420
'items' => { '$ref' => '#/definitions/TheseApi_Entities_TagType' },
387421
'type' => 'array'
388422
}
389423
},
390-
'type' => 'object'
424+
'required' => %w[tags]
391425
)
392426

393427
expect(subject['TheseApi_Entities_SomeEntity']).to eql(
@@ -414,7 +448,8 @@ def app
414448
},
415449
'attr' => { 'type' => 'string', 'description' => 'Attribute' }
416450
},
417-
'required' => %w[attr],
451+
'required' => %w[text kind kind2 kind3 tags relation values nested nested_child
452+
polymorphic mixed attr code message],
418453
'description' => 'TheseApi_Entities_SomeEntity model'
419454
)
420455
end

0 commit comments

Comments
 (0)