swaggard 4.0.3 → 4.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cd440c54fb4a517bedb134570c0dd76ed67ef1ef1947d0bcd89dc72f70d89a8
4
- data.tar.gz: cdc795955a2d5d3d89e570657da280fa6fb4873a5bd562f086def5871c035354
3
+ metadata.gz: 28d2106f9e0d61087b74c97d99c4e3e6c848b9bee4afa7fb6a828db0466cb134
4
+ data.tar.gz: 668252d84bbdc87a23ed4191cc5baae2c2c7dfbe2b06cb794d8a7fe947cbf70c
5
5
  SHA512:
6
- metadata.gz: e0789daceebcc72d62aca37584b4a2163bc1d5de00d20e22d990a8556797492d29fda8ac85acdbacbfde76b8c985047db02b656e0c91686ac386be7b4c9f088f
7
- data.tar.gz: d860cfcc26f4c3a6c241fb5409ed3077c8330023b171b6c8a729e300056166ee58afd94020f43598d271b488c12ec1192265cbcc8ac2d2ed8bbd62677b3be959
6
+ metadata.gz: fbc1a171bbb0b3eaf2d285e4eebb9e3b2d76196745865afb727de157be519be9604d12af0745c9d2a7d73a5b52f0916230e98372bbc94af67159552cf72302dc
7
+ data.tar.gz: a648d45fc2cffc78502e548ccafcfbc2ff4ece0bccee7a75b3d4827cffab98d1672304bca5d63bcd1b94f1f0dc4d7a8cd373faea5adf61ca356f4f553928493c
data/README.md CHANGED
@@ -139,7 +139,7 @@ Add YARD comments to provide richer documentation:
139
139
 
140
140
  - `@tag name` — Group this controller under `name`. Defaults to the controller path if `ignore_untagged_controllers` is false.
141
141
  - `@query_parameter [type] name` — Query string parameter.
142
- - `@body_parameter [type] name` — Request body property. Generates a `requestBody` in the OpenAPI output.
142
+ - `@body_parameter [type] name` — Request body property. Generates a `requestBody` in the OpenAPI output. Use `[type]!` to mark the property as required and `name(deprecated)` to mark it as deprecated.
143
143
  - `@form_parameter [type] name` — Form data parameter (`application/x-www-form-urlencoded`).
144
144
  - `@parameter_list` — Enum-style query parameter list.
145
145
  - `@response_class type` — Response schema type. Supports `Array<Type>` for array responses.
@@ -162,7 +162,7 @@ To document all controllers including those without a `@tag`:
162
162
 
163
163
  ### Models
164
164
 
165
- - `@attr [type] name` — Model attribute.
165
+ - `@attr [type] name` — Model attribute. Use `!name` to mark the attribute as required and `name(deprecated)` to mark it as deprecated.
166
166
  - `@ignore_inherited` — Do not inherit properties from parent class.
167
167
 
168
168
 
@@ -11,10 +11,11 @@ module Swaggard
11
11
  options, description = options_and_description.match(/\A(\[.*\])?(.*)\Z/).captures
12
12
  options = options ? options.gsub(/\[?\]?\s?/, '').split(',') : []
13
13
  description = description.strip
14
+ deprecated = name.gsub!(/\(deprecated\)\z/, '')
14
15
  required = name.gsub!(/^!/, '')
15
16
  type = Parsers::Type.run(yard_object.types.first)
16
17
 
17
- Swaggard::Swagger::Property.new(name, type, description, required.present?, options)
18
+ Swaggard::Swagger::Property.new(name, type, description, required.present?, options, deprecated.present?)
18
19
  end
19
20
  end
20
21
  end
@@ -5,13 +5,14 @@ module Swaggard
5
5
  attr_reader :id
6
6
  attr_writer :description, :title, :ignore_inherited
7
7
 
8
- def initialize(id, ancestors: [])
8
+ def initialize(id, ancestors: [], collection: false)
9
9
  @id = id
10
10
  @title = ''
11
11
  @properties = []
12
12
  @description = ''
13
13
  @ancestors = ancestors
14
14
  @ignore_inherited = false
15
+ @collection = collection
15
16
  end
16
17
 
17
18
  def add_property(property)
@@ -41,19 +42,31 @@ module Swaggard
41
42
  end
42
43
 
43
44
  def to_doc(definitions)
44
- {}.tap do |doc|
45
- doc['title'] = @title if @title.present?
46
- doc['type'] = 'object'
45
+ all_properties = properties(definitions)
46
+ properties_hash = Hash[all_properties.map { |property| [property.id, property.to_doc] }]
47
+ required_properties = all_properties.select(&:required?).map(&:id)
47
48
 
48
- doc['description'] = @description if @description.present?
49
+ if @collection
50
+ items = { 'type' => 'object', 'properties' => properties_hash }
51
+ items['required'] = required_properties if required_properties.any?
49
52
 
50
- all_properties = properties(definitions)
53
+ {}.tap do |doc|
54
+ doc['title'] = @title if @title.present?
55
+ doc['type'] = 'array'
56
+ doc['description'] = @description if @description.present?
57
+ doc['items'] = items
58
+ end
59
+ else
60
+ {}.tap do |doc|
61
+ doc['title'] = @title if @title.present?
62
+ doc['type'] = 'object'
51
63
 
52
- doc['properties'] = Hash[all_properties.map { |property| [property.id, property.to_doc] }]
53
- required_properties = all_properties.select(&:required?).map(&:id)
54
- doc['required'] = required_properties if required_properties.any?
55
- end
64
+ doc['description'] = @description if @description.present?
56
65
 
66
+ doc['properties'] = properties_hash
67
+ doc['required'] = required_properties if required_properties.any?
68
+ end
69
+ end
57
70
  end
58
71
 
59
72
  end
@@ -72,15 +72,17 @@ module Swaggard
72
72
  elsif @options.present?
73
73
  result['enum'] = @options
74
74
  end
75
+ result['deprecated'] = true if @deprecated
75
76
  result
76
77
  end
77
78
 
78
79
  # Example: [Array] status Filter by status. (e.g. status[]=1&status[]=2&status[]=3)
79
80
  # Example: [Array] status(required) Filter by status. (e.g. status[]=1&status[]=2&status[]=3)
80
81
  # Example: [Integer] media[media_type_id] ID of the desired media type.
82
+ # Example: [Boolean] uses_tobacco(deprecated) Whether the patient uses tobacco.
81
83
  def parse(string)
82
84
  string.gsub!("\n", ' ')
83
- data_type, required, name, options_and_description = string.match(/\A\[(\S*)\](!)?\s*([\w\[\]]*)\s*(.*)\Z/).captures
85
+ data_type, required, name, deprecated, options_and_description = string.match(/\A\[(\S*)\](!)?\s*([\w\[\]]*)(\(deprecated\))?\s*(.*)\Z/).captures
84
86
  options, description = options_and_description.match(/\A(\[.*\])?(.*)\Z/).captures
85
87
  options = options ? options.gsub(/\[?\]?\s?/, '').split(',') : []
86
88
 
@@ -89,6 +91,7 @@ module Swaggard
89
91
  @type = Parsers::Type.run(data_type)
90
92
  @required = required
91
93
  @options = options
94
+ @deprecated = deprecated.present?
92
95
  end
93
96
  end
94
97
  end
@@ -5,22 +5,28 @@ module Swaggard
5
5
  class Property
6
6
  attr_reader :id, :type, :description
7
7
 
8
- def initialize(name, type, description = '', required = false, options = [])
8
+ def initialize(name, type, description = '', required = false, options = [], deprecated = false)
9
9
  @id = name
10
10
  @type = type
11
11
  @description = description
12
12
  @required = required
13
13
  @options = options
14
+ @deprecated = deprecated
14
15
  end
15
16
 
16
17
  def required?
17
18
  @required
18
19
  end
19
20
 
21
+ def deprecated?
22
+ @deprecated
23
+ end
24
+
20
25
  def to_doc
21
26
  result = @type.to_doc
22
27
  result['description'] = @description if @description.present?
23
28
  result['enum'] = @options if @options.present?
29
+ result['deprecated'] = true if @deprecated
24
30
  result
25
31
  end
26
32
  end
@@ -1,3 +1,3 @@
1
1
  module Swaggard
2
- VERSION = '4.0.3'
2
+ VERSION = '4.1.0'
3
3
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Swaggard::Parsers::Property do
4
+ def yard_tag(name, text: 'A description', types: ['string'])
5
+ YARD::Tags::Tag.new('attr', text, types, name)
6
+ end
7
+
8
+ describe '.run' do
9
+ it 'parses a plain attribute' do
10
+ property = described_class.run(yard_tag('name'))
11
+
12
+ expect(property.id).to eq('name')
13
+ expect(property).not_to be_required
14
+ expect(property).not_to be_deprecated
15
+ end
16
+
17
+ it 'parses the required marker' do
18
+ property = described_class.run(yard_tag('!name'))
19
+
20
+ expect(property.id).to eq('name')
21
+ expect(property).to be_required
22
+ end
23
+
24
+ it 'parses the deprecated marker' do
25
+ property = described_class.run(yard_tag('uses_tobacco(deprecated)', types: ['boolean']))
26
+
27
+ expect(property.id).to eq('uses_tobacco')
28
+ expect(property).to be_deprecated
29
+ expect(property.to_doc).to include('deprecated' => true)
30
+ end
31
+
32
+ it 'combines the required and deprecated markers' do
33
+ property = described_class.run(yard_tag('!name(deprecated)'))
34
+
35
+ expect(property.id).to eq('name')
36
+ expect(property).to be_required
37
+ expect(property).to be_deprecated
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Swaggard::Swagger::Definition do
4
+ let(:property) { Swaggard::Swagger::Property.new('name', Swaggard::Swagger::Type.new(:string)) }
5
+
6
+ describe '#to_doc' do
7
+ context 'when collection is false (default)' do
8
+ subject(:definition) { described_class.new('Foo') }
9
+
10
+ before { definition.add_property(property) }
11
+
12
+ it 'emits a type: object schema with inline properties' do
13
+ expect(definition.to_doc({})).to eq(
14
+ 'type' => 'object',
15
+ 'properties' => { 'name' => { 'type' => 'string' } }
16
+ )
17
+ end
18
+ end
19
+
20
+ context 'when collection is true' do
21
+ subject(:definition) { described_class.new('Foo_all', collection: true) }
22
+
23
+ before { definition.add_property(property) }
24
+
25
+ it 'emits a type: array schema with properties wrapped under items' do
26
+ expect(definition.to_doc({})).to eq(
27
+ 'type' => 'array',
28
+ 'items' => {
29
+ 'type' => 'object',
30
+ 'properties' => { 'name' => { 'type' => 'string' } }
31
+ }
32
+ )
33
+ end
34
+
35
+ context 'with required properties' do
36
+ let(:property) do
37
+ Swaggard::Swagger::Property.new('name', Swaggard::Swagger::Type.new(:string), '', true)
38
+ end
39
+
40
+ it 'places the required array inside items' do
41
+ doc = definition.to_doc({})
42
+
43
+ expect(doc['required']).to be_nil
44
+ expect(doc['items']['required']).to eq(['name'])
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Swaggard::Swagger::Parameters::Body do
4
+ subject(:body) { described_class.new('PetsController.create') }
5
+
6
+ def property_doc(name)
7
+ body.definition.to_doc({})['properties'][name]
8
+ end
9
+
10
+ describe '#add_property' do
11
+ it 'parses type, name and description' do
12
+ body.add_property('[String] name The name of the pet')
13
+
14
+ expect(property_doc('name')).to eq('type' => 'string', 'description' => 'The name of the pet')
15
+ end
16
+
17
+ it 'parses the required marker' do
18
+ body.add_property('[String]! name The name of the pet')
19
+
20
+ expect(body.definition.to_doc({})['required']).to eq(['name'])
21
+ end
22
+
23
+ it 'parses the deprecated marker' do
24
+ body.add_property('[Boolean] uses_tobacco(deprecated) Use lifestyle instead.')
25
+
26
+ expect(property_doc('uses_tobacco')).to eq(
27
+ 'type' => 'boolean',
28
+ 'description' => 'Use lifestyle instead.',
29
+ 'deprecated' => true
30
+ )
31
+ end
32
+
33
+ it 'combines the required and deprecated markers' do
34
+ body.add_property('[String]! name(deprecated) The name of the pet')
35
+
36
+ expect(property_doc('name')['deprecated']).to be(true)
37
+ expect(body.definition.to_doc({})['required']).to eq(['name'])
38
+ end
39
+
40
+ it 'keeps enum options working alongside the deprecated marker' do
41
+ body.add_property('[String] status(deprecated) [active,inactive] The status')
42
+
43
+ expect(property_doc('status')).to eq(
44
+ 'type' => 'string',
45
+ 'description' => ' The status',
46
+ 'enum' => %w[active inactive],
47
+ 'deprecated' => true
48
+ )
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Swaggard::Swagger::Property do
4
+ describe '#to_doc' do
5
+ let(:type) { Swaggard::Swagger::Type.new(:string) }
6
+
7
+ it 'emits the type only by default' do
8
+ property = described_class.new('name', type)
9
+
10
+ expect(property.to_doc).to eq('type' => 'string')
11
+ end
12
+
13
+ it 'emits the description when present' do
14
+ property = described_class.new('name', type, 'The full name')
15
+
16
+ expect(property.to_doc).to eq('type' => 'string', 'description' => 'The full name')
17
+ end
18
+
19
+ it 'emits deprecated when the property is deprecated' do
20
+ property = described_class.new('name', type, '', false, [], true)
21
+
22
+ expect(property.to_doc).to eq('type' => 'string', 'deprecated' => true)
23
+ expect(property).to be_deprecated
24
+ end
25
+
26
+ it 'does not emit deprecated when the property is not deprecated' do
27
+ property = described_class.new('name', type)
28
+
29
+ expect(property.to_doc).not_to have_key('deprecated')
30
+ expect(property).not_to be_deprecated
31
+ end
32
+ end
33
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swaggard
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.3
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Gomez
@@ -162,6 +162,10 @@ files:
162
162
  - spec/integration/openapi_spec.rb
163
163
  - spec/integration/swaggard_spec.rb
164
164
  - spec/spec_helper.rb
165
+ - spec/swaggard/parsers/property_spec.rb
166
+ - spec/swaggard/swagger/definition_spec.rb
167
+ - spec/swaggard/swagger/parameters/body_spec.rb
168
+ - spec/swaggard/swagger/property_spec.rb
165
169
  homepage: https://github.com/adrian-gomez/swaggard
166
170
  licenses:
167
171
  - MIT
@@ -199,3 +203,7 @@ test_files:
199
203
  - spec/integration/openapi_spec.rb
200
204
  - spec/integration/swaggard_spec.rb
201
205
  - spec/spec_helper.rb
206
+ - spec/swaggard/parsers/property_spec.rb
207
+ - spec/swaggard/swagger/definition_spec.rb
208
+ - spec/swaggard/swagger/parameters/body_spec.rb
209
+ - spec/swaggard/swagger/property_spec.rb