jsonapi_swagger_helpers 0.1.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b9b3f968a4f5d28cdde96e82960634eb620f152
4
- data.tar.gz: a1a488145afdab452e203eca3301f02fb9c03e04
3
+ metadata.gz: 9a9dd06a7c8afca90ba9b3b340598a61ee7473d4
4
+ data.tar.gz: a4e56e637f81f4da21a6e784c9a4d2dae0b85953
5
5
  SHA512:
6
- metadata.gz: 85b70ab34c7a2e0dcdb0aab4aea2d9d62cdcd598772f5eb186837a9e3ab79d169024de4721fa0b4893b14e30472ec544d2e7ede0620225f88628a28703448a58
7
- data.tar.gz: 0b33c9e6fad54818f4fcf28a8bea33692d6a51a204f11e6caa2b6b5238133ce9d3f91ccfb946d023a4d50c505afed588f44f4ed1b4d4e269b60d83474e71d171
6
+ metadata.gz: 002f847d7f43fa3433932b72fab3e1efba37aafa120d2ba7332b9df78c9792a7e9075d5e30a6f56592d69c49a8ab5a213a03ad55463cce5db5040720d2896fd1
7
+ data.tar.gz: edc488adacd76b2281ad138410e37e3150fb380b889550ec171f8e0e90207e5dd917c81fb94f40335189d790f7a50f648f9865d517a68969e0f29841699e54de
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ swagger.json
11
+ npm-debug.log
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'swagger-blocks', '~> 1.3'
22
+ # TODO: above 0.4.2
23
+ spec.add_dependency 'jsonapi_spec_helpers', ['< 1']
22
24
 
23
25
  spec.add_development_dependency "bundler", "~> 1.12"
24
26
  spec.add_development_dependency "rake", "~> 10.0"
@@ -0,0 +1,10 @@
1
+ class JsonapiSwaggerHelpers::Configuration
2
+ def type_mapping
3
+ @type_mapping ||= {
4
+ string: [String],
5
+ integer: [Integer, Bignum],
6
+ float: [Float],
7
+ boolean: [TrueClass, FalseClass]
8
+ }
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ class JsonapiSwaggerHelpers::CreateAction
2
+ include JsonapiSwaggerHelpers::Writeable
3
+
4
+ def action_name
5
+ :create
6
+ end
7
+
8
+ def generate
9
+ _self = self
10
+
11
+ define_schema
12
+ @node.operation :post do
13
+ key :description, _self.description
14
+ key :operationId, _self.operation_id
15
+ key :tags, _self.all_tags
16
+
17
+ parameter do
18
+ key :name, :payload
19
+ key :in, :body
20
+
21
+ schema do
22
+ key :'$ref', :"#{_self.strong_resource.name}_create"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ class JsonapiSwaggerHelpers::DestroyAction
2
+ include JsonapiSwaggerHelpers::Writeable
3
+
4
+ def action_name
5
+ :destroy
6
+ end
7
+
8
+ def generate
9
+ _self = self
10
+
11
+ @node.operation :delete do
12
+ key :description, _self.description
13
+ key :operationId, _self.operation_id
14
+ key :tags, _self.tags
15
+
16
+ _self.util.id_in_url(self)
17
+ end
18
+ end
19
+ end
@@ -1,13 +1,32 @@
1
1
  module JsonapiSwaggerHelpers
2
2
  module DocsControllerMixin
3
3
  def self.included(klass)
4
+ # Save our controller so we can execute Swagger::Blocks code against it
5
+ JsonapiSwaggerHelpers.docs_controller = klass
6
+
7
+ # Add glue code
4
8
  klass.send(:include, Swagger::Blocks)
5
- klass.extend(ResourceMixin)
9
+ klass.extend(ResourceMixin) # jsonapi_resource DSL
10
+ klass.extend(ClassMethods) # for predefining payloads
11
+
12
+ # Predefine swagger definitions for later reference
13
+ # * spec payloads define outputs
14
+ # * strong resources define inputs
15
+ klass.register_payload_definitions!
16
+ end
17
+
18
+ module ClassMethods
19
+ def register_payload_definitions!
20
+ JsonapiSpecHelpers.load_payloads!
21
+ JsonapiSpecHelpers::Payload.registry.each_pair do |payload_name, payload|
22
+ JsonapiSwaggerHelpers::PayloadDefinition.new(payload).generate
23
+ end
24
+ end
6
25
  end
7
26
 
27
+ # Give Swagger::Blocks what it wants
8
28
  def index
9
- klasses = [self.class, JsonapiSwaggerHelpers::StrongResourceMixin::Schemas]
10
- render json: Swagger::Blocks.build_root_json(klasses)
29
+ render json: Swagger::Blocks.build_root_json([self.class])
11
30
  end
12
31
  end
13
32
  end
@@ -0,0 +1,45 @@
1
+ class JsonapiSwaggerHelpers::IndexAction
2
+ include JsonapiSwaggerHelpers::Readable
3
+
4
+ def action_name
5
+ :index
6
+ end
7
+
8
+ def generate
9
+ _self = self
10
+
11
+ @node.operation :get do
12
+ key :description, _self.full_description
13
+ key :operationId, _self.operation_id
14
+ key :tags, _self.all_tags
15
+
16
+ _self.util.jsonapi_sorting(self)
17
+ _self.util.jsonapi_pagination(self)
18
+
19
+ _self.util.each_filter(_self.resource) do |filter_label|
20
+ _self.util.jsonapi_filter(self, filter_label)
21
+ end
22
+
23
+ _self.each_stat do |stat_name, calculations|
24
+ _self.util.jsonapi_stat(self, stat_name, calculations)
25
+ end
26
+
27
+ _self.util.jsonapi_fields(self, _self.jsonapi_type)
28
+
29
+ if _self.has_extra_fields?
30
+ _self.util.jsonapi_extra_fields(self, _self.resource)
31
+ end
32
+
33
+ if _self.has_sideloads?
34
+ _self.util.jsonapi_includes(self)
35
+
36
+ _self.each_association do |association_name, association_resource|
37
+ _self.util.each_filter(association_resource, association_name) do |filter_label|
38
+ _self.util.jsonapi_filter(self, filter_label)
39
+ _self.util.jsonapi_fields(self, association_resource.config[:type])
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,57 @@
1
+ class JsonapiSwaggerHelpers::PayloadDefinition
2
+ attr_reader :payload
3
+
4
+ # Given a spec payload like:
5
+ #
6
+ # key(:name, String)
7
+ #
8
+ # Return the corresponding swagger type, ie :string
9
+ # If a key has multiple types, we'll pick the first swagger type that matches:
10
+ #
11
+ # key(:total, [String, Integer]) => :string
12
+ def self.swagger_type_for(payload_name, attribute, type)
13
+ types = Array(type)
14
+ return :string if types.empty?
15
+
16
+ type_mapping.each_pair do |swagger_type, klasses|
17
+ if types.any? { |t| klasses.include?(t) }
18
+ return swagger_type
19
+ end
20
+ end
21
+
22
+ raise JsonapiSwaggerHelpers::Errors::TypeNotFound
23
+ .new(payload_name, attribute)
24
+ end
25
+
26
+ def self.type_mapping
27
+ JsonapiSwaggerHelpers.config.type_mapping
28
+ end
29
+
30
+ def initialize(payload)
31
+ @payload = payload
32
+ end
33
+
34
+ def context
35
+ JsonapiSwaggerHelpers.docs_controller
36
+ end
37
+
38
+ def jsonapi_type
39
+ payload.type
40
+ end
41
+
42
+ def generate
43
+ _self = self
44
+
45
+ context.send(:swagger_schema, payload.name) do
46
+ payload = _self.payload
47
+
48
+ payload.keys.each_pair do |attribute, config|
49
+ property attribute do
50
+ type = _self.class.swagger_type_for(payload.name, attribute, config[:type])
51
+ key :type, type
52
+ key :description, config[:description]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,83 @@
1
+ module JsonapiSwaggerHelpers::Readable
2
+ def self.included(klass)
3
+ klass.class_eval do
4
+ attr_reader :node,
5
+ :controller,
6
+ :resource,
7
+ :description,
8
+ :tags
9
+ end
10
+ end
11
+
12
+ def initialize(node, controller, description: nil, tags: [])
13
+ @node = node
14
+ @controller = controller
15
+ @resource = controller._jsonapi_compliable
16
+ @description = description || default_description
17
+ @tags = tags
18
+ end
19
+
20
+ def default_description
21
+ "#{action_name.capitalize} Action"
22
+ end
23
+
24
+ def operation_id
25
+ "#{controller.name.gsub('::', '-')}-#{action_name}"
26
+ end
27
+
28
+ def util
29
+ JsonapiSwaggerHelpers::Util
30
+ end
31
+
32
+ def include_directive
33
+ util.include_directive_for(controller, action_name)
34
+ end
35
+
36
+ def has_sideloads?
37
+ include_directive.keys.length > 0
38
+ end
39
+
40
+ def has_extra_fields?
41
+ resource.config[:extra_fields].keys.length > 1
42
+ end
43
+
44
+ def full_description
45
+ "#{description}<br /><br />#{util.sideload_label(include_directive)}"
46
+ end
47
+
48
+ def all_tags
49
+ tags + payload_tags
50
+ end
51
+
52
+ def payload_tags
53
+ util.payload_tags_for(resource, include_directive.to_hash)
54
+ end
55
+
56
+ def operation_id
57
+ "#{controller.name.gsub('::', '-')}-#{action_name}"
58
+ end
59
+
60
+ def each_stat
61
+ resource.config[:stats].each_pair do |stat_name, opts|
62
+ calculations = opts.calculations.keys - [:keys]
63
+ calculations = calculations.join(', ')
64
+
65
+ yield stat_name, calculations
66
+ end
67
+ end
68
+
69
+ def each_association
70
+ resource_map = util.all_resources(resource, include_directive)
71
+ resource_map.each_pair do |association_name, association_resource|
72
+ yield association_name, association_resource
73
+ end
74
+ end
75
+
76
+ def jsonapi_type
77
+ resource.config[:type]
78
+ end
79
+
80
+ def generate
81
+ raise 'override me'
82
+ end
83
+ end
@@ -1,76 +1,58 @@
1
1
  module JsonapiSwaggerHelpers
2
2
  module ResourceMixin
3
3
 
4
- def jsonapi_resource(base_path, tags: [], descriptions: {}, only: [], except: [])
5
- actions = [:index, :show, :create, :update, :destroy]
6
-
7
- unless only.empty?
8
- actions.select! { |a| only.include?(a) }
9
- end
10
-
11
- unless except.empty?
12
- actions.reject! { |a| except.include?(a) }
13
- end
4
+ def jsonapi_resource(base_path,
5
+ tags: [],
6
+ descriptions: {},
7
+ only: [],
8
+ except: [])
9
+ actions = [:index, :show, :create, :update, :destroy]
10
+ actions.select! { |a| only.include?(a) } unless only.empty?
11
+ actions.reject! { |a| except.include?(a) } unless except.empty?
14
12
 
15
13
  prefix = @swagger_root_node.data[:basePath]
16
14
  full_path = [prefix, base_path].join('/').gsub('//', '/')
17
- controller = controller_for(full_path)
15
+ controller = JsonapiSwaggerHelpers::Util.controller_for(full_path)
18
16
 
17
+ ctx = self
19
18
  if [:create, :index].any? { |a| actions.include?(a) }
20
19
  swagger_path base_path do
21
- if actions.include?(:index)
22
- operation :get do
23
- key :tags, tags
24
- key :description, descriptions[:index]
25
- jsonapi_index(controller)
26
- end
20
+ if actions.include?(:index) && controller.action_methods.include?('index')
21
+ index_action = JsonapiSwaggerHelpers::IndexAction.new \
22
+ self, controller, tags: tags, description: descriptions[:index]
23
+ index_action.generate
27
24
  end
28
25
 
29
- if actions.include?(:create)
30
- operation :post do
31
- key :tags, tags
32
- key :description, descriptions[:create]
33
- strong_resource(controller, :create)
34
- end
26
+ if actions.include?(:create) && controller.action_methods.include?('create')
27
+ create_action = JsonapiSwaggerHelpers::CreateAction.new \
28
+ self, controller, tags: tags, description: descriptions[:create]
29
+ create_action.generate
35
30
  end
36
31
  end
37
32
  end
38
33
 
39
34
  if [:show, :update, :destroy].any? { |a| actions.include?(a) }
35
+ ctx = self
40
36
  swagger_path "#{base_path}/{id}" do
41
- if actions.include?(:show)
42
- operation :get do
43
- key :tags, tags
44
- key :description, descriptions[:show]
45
- jsonapi_show(controller)
46
- end
37
+ if actions.include?(:show) && controller.action_methods.include?('show')
38
+ show_action = JsonapiSwaggerHelpers::ShowAction.new \
39
+ self, controller, tags: tags, description: descriptions[:show]
40
+ show_action.generate
47
41
  end
48
42
 
49
- if actions.include?(:update)
50
- operation :put do
51
- key :tags, tags
52
- key :description, descriptions[:update]
53
- id_in_url
54
- strong_resource(controller, :update)
55
- end
43
+ if actions.include?(:update) && controller.action_methods.include?('update')
44
+ update_action = JsonapiSwaggerHelpers::UpdateAction.new \
45
+ self, controller, tags: tags, description: descriptions[:update]
46
+ update_action.generate
56
47
  end
57
48
 
58
- if actions.include?(:destroy)
59
- operation :delete do
60
- key :tags, tags
61
- key :description, descriptions[:destroy]
62
- id_in_url
63
- end
49
+ if actions.include?(:destroy) && controller.action_methods.include?('destroy')
50
+ destroy_action = JsonapiSwaggerHelpers::DestroyAction.new \
51
+ self, controller, tags: tags, description: descriptions[:destroy]
52
+ destroy_action.generate
64
53
  end
65
54
  end
66
55
  end
67
56
  end
68
-
69
- def controller_for(path)
70
- path = path.sub('{id}', '1')
71
- route = Rails.application.routes.recognize_path(path)
72
- "#{route[:controller]}_controller".classify.constantize
73
- end
74
-
75
57
  end
76
58
  end
@@ -0,0 +1,43 @@
1
+ class JsonapiSwaggerHelpers::ShowAction
2
+ include JsonapiSwaggerHelpers::Readable
3
+
4
+ def action_name
5
+ :show
6
+ end
7
+
8
+ def generate
9
+ _self = self
10
+
11
+ @node.operation :get do
12
+ key :description, _self.full_description
13
+ key :operationId, _self.operation_id
14
+ key :tags, _self.all_tags
15
+
16
+ _self.util.id_in_url(self)
17
+ _self.util.jsonapi_fields(self, _self.jsonapi_type)
18
+
19
+ if _self.has_extra_fields?
20
+ _self.util.jsonapi_extra_fields(self, _self.resource)
21
+ end
22
+
23
+ _self.each_stat do |stat_name, calculations|
24
+ _self.util.jsonapi_stat(self, stat_name, calculations)
25
+ end
26
+
27
+ if _self.has_sideloads?
28
+ _self.util.jsonapi_includes(self)
29
+
30
+ _self.each_association do |association_name, association_resource|
31
+ _self.util.each_filter(association_resource, association_name) do |filter_label|
32
+ _self.util.jsonapi_filter(self, filter_label)
33
+ _self.util.jsonapi_fields(self, association_resource.config[:type])
34
+
35
+ if association_resource.config[:extra_fields].keys.length > 0
36
+ _self.util.jsonapi_extra_fields(self, association_resource)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ class JsonapiSwaggerHelpers::UpdateAction
2
+ include JsonapiSwaggerHelpers::Writeable
3
+
4
+ def action_name
5
+ :update
6
+ end
7
+
8
+ def generate
9
+ _self = self
10
+
11
+ define_schema
12
+ @node.operation :put do
13
+ key :description, _self.description
14
+ key :operationId, _self.operation_id
15
+ key :tags, _self.all_tags
16
+
17
+ _self.util.id_in_url(self)
18
+
19
+ parameter do
20
+ key :name, :payload
21
+ key :in, :body
22
+
23
+ schema do
24
+ key :'$ref', :"#{_self.strong_resource.name}_update"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,165 @@
1
+ module JsonapiSwaggerHelpers::Util
2
+ def self.controller_for(path)
3
+ path = path.sub('{id}', '1')
4
+ route = Rails.application.routes.recognize_path(path)
5
+ "#{route[:controller]}_controller".classify.constantize
6
+ end
7
+
8
+ def self.sideload_label(include_directive)
9
+ sideloads = include_directive.to_string.split(",").sort.join(",<br/>")
10
+
11
+ <<-HTML
12
+ <label>
13
+ Possible sideloads:
14
+ <span class="possible-sideloads">#{sideloads}</span>
15
+ </label>
16
+ HTML
17
+ end
18
+
19
+ def self.each_filter(resource, association_name = nil)
20
+ resource.config[:filters].each_pair do |filter_name, opts|
21
+ if association_name
22
+ yield "filter[#{association_name}][#{filter_name}]"
23
+ else
24
+ yield "filter[#{filter_name}]"
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.all_resources(resource, include_directive, memo = {})
30
+ resource.sideloading.sideloads.each_pair do |name, sideload|
31
+ next if memo[name] || !include_directive.key?(name)
32
+
33
+ memo[name] = sideload.resource.class
34
+ all_resources(sideload.resource.class, include_directive[name], memo)
35
+ end
36
+ memo
37
+ end
38
+
39
+ def self.include_directive_for(controller, action)
40
+ resource_class = controller._jsonapi_compliable
41
+ includes = resource_class.sideloading.to_hash[:base]
42
+ whitelist = resource_class.config[:sideload_whitelist]
43
+
44
+ if whitelist && whitelist[action]
45
+ includes = JsonapiCompliable::Util::IncludeParams
46
+ .scrub(includes, whitelist[action])
47
+ end
48
+
49
+ JSONAPI::IncludeDirective.new(includes)
50
+ end
51
+
52
+ def self.payloads_for(resource, include_hash)
53
+ [].tap do |payloads|
54
+ payloads << JsonapiSpecHelpers::Payload.by_type(resource.config[:type])
55
+
56
+ include_hash.each_pair do |name, nested|
57
+ sideload = resource.sideloading.sideloads[name]
58
+
59
+ if sideload.polymorphic?
60
+ sideload.polymorphic_groups.each_pair do |type, sl|
61
+ payloads << payloads_for(sl.resource_class, nested)
62
+ end
63
+ else
64
+ sideload_resource = sideload.resource_class
65
+ payloads << payloads_for(sideload_resource, nested)
66
+ end
67
+ end
68
+ end.flatten.uniq(&:name)
69
+ end
70
+
71
+ def self.payload_tags_for(resource, include_hash)
72
+ payloads_for(resource, include_hash).map { |p| "payload-#{p.name}" }
73
+ end
74
+
75
+ def self.jsonapi_filter(node, label)
76
+ node.parameter do
77
+ key :description, '<a href="http://jsonapi.org/format/#fetching-filtering">JSONAPI Filter</a>'
78
+ key :name, label
79
+ key :in, :query
80
+ key :type, :string
81
+ key :required, false
82
+ end
83
+ end
84
+
85
+ def self.jsonapi_sorting(node)
86
+ node.parameter do
87
+ key :description, '<a href="http://jsonapi.org/format/#fetching-sorting">JSONAPI Sorting</a>'
88
+ key :name, :sort
89
+ key :in, :query
90
+ key :type, :string
91
+ key :required, false
92
+ end
93
+ end
94
+
95
+ def self.jsonapi_pagination(node)
96
+ node.parameter do
97
+ key :description, '<a href="http://jsonapi.org/format/#fetching-pagination">JSONAPI Page Size</a>'
98
+ key :name, "page[size]"
99
+ key :in, :query
100
+ key :type, :string
101
+ key :required, false
102
+ end
103
+
104
+ node.parameter do
105
+ key :description, '<a href="http://jsonapi.org/format/#fetching-pagination">JSONAPI Page Number</a>'
106
+ key :name, "page[number]"
107
+ key :in, :query
108
+ key :type, :string
109
+ key :required, false
110
+ end
111
+ end
112
+
113
+ def self.jsonapi_stat(node, name, calculations)
114
+ node.parameter do
115
+ key :name, "stats[#{name}]"
116
+ key :description, "<a href=\"https://jsonapi-suite.github.io/jsonapi_suite/how-to-return-statistics\">JSONAPI Stats</a><br /><b>Possible Calculations:</b> #{calculations}"
117
+ key :in, :query
118
+ key :type, :string
119
+ key :required, false
120
+ end
121
+ end
122
+
123
+ def self.jsonapi_includes(node)
124
+ node.parameter do
125
+ key :description, '<a href="http://jsonapi.org/format/#fetching-includes">JSONAPI Includes</a>'
126
+ key :name, :include
127
+ key :in, :query
128
+ key :type, :string
129
+ key :required, false
130
+ end
131
+ end
132
+
133
+ def self.jsonapi_fields(node, jsonapi_type)
134
+ node.parameter do
135
+ key :description, '<a href="http://jsonapi.org/format/#fetching-sparse-fieldsets">JSONAPI Sparse Fieldset</a>'
136
+ key :name, "fields[#{jsonapi_type}]"
137
+ key :in, :query
138
+ key :type, :string
139
+ key :required, false
140
+ end
141
+ end
142
+
143
+ def self.jsonapi_extra_fields(node, resource)
144
+ jsonapi_type = resource.config[:type]
145
+
146
+ extra_field_names = resource.config[:extra_fields].keys.join(',')
147
+ node.parameter do
148
+ key :description, "<a href=\"https://jsonapi-suite.github.io/jsonapi_suite/how-to-conditionally-render-fields\">JSONAPI Extra Fields</a><br /><b>Possible Fields:</b> #{extra_field_names}"
149
+ key :name, "extra_fields[#{jsonapi_type}]"
150
+ key :in, :query
151
+ key :type, :string
152
+ key :required, false
153
+ end
154
+ end
155
+
156
+ def self.id_in_url(node)
157
+ node.parameter do
158
+ key :name, :id
159
+ key :in, :path
160
+ key :type, :string
161
+ key :required, true
162
+ key :description, 'record id'
163
+ end
164
+ end
165
+ end
@@ -1,3 +1,3 @@
1
1
  module JsonapiSwaggerHelpers
2
- VERSION = "0.1.5"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,89 @@
1
+ module JsonapiSwaggerHelpers::Writeable
2
+ def self.included(klass)
3
+ klass.class_eval do
4
+ attr_reader :node,
5
+ :controller,
6
+ :resource,
7
+ :description,
8
+ :tags
9
+ end
10
+ end
11
+
12
+ def initialize(node, controller, description: nil, tags: [])
13
+ @node = node
14
+ @controller = controller
15
+ @resource = controller._jsonapi_compliable
16
+ @description = description || default_description
17
+ @tags = tags
18
+ end
19
+
20
+ def util
21
+ JsonapiSwaggerHelpers::Util
22
+ end
23
+
24
+ def action_name
25
+ raise 'override me'
26
+ end
27
+
28
+ def default_description
29
+ "#{action_name.to_s.capitalize} Action"
30
+ end
31
+
32
+ def operation_id
33
+ "#{controller.name.gsub('::', '-')}-#{action_name}"
34
+ end
35
+
36
+ def all_tags
37
+ tags + payload_tags
38
+ end
39
+
40
+ def payload_tags
41
+ tags = [:"payload-#{strong_resource.name}_#{action_name}"]
42
+
43
+ strong_resource.relations.each_pair do |relation_name, relation_config|
44
+ tags << :"payload-#{strong_resource.name}_#{relation_name}_#{action_name}"
45
+ end
46
+
47
+ tags
48
+ end
49
+
50
+ def context
51
+ JsonapiSwaggerHelpers.docs_controller
52
+ end
53
+
54
+ def strong_resource
55
+ controller._strong_resources[action_name]
56
+ end
57
+
58
+ def define_schema
59
+ _self = self
60
+ context.send(:swagger_schema, :"#{strong_resource.name}_#{action_name}") do
61
+ _self.strong_resource.attributes.each_pair do |attribute, config|
62
+ property attribute do
63
+ key :type, config[:type] # TODO - swagger type?
64
+ end
65
+ end
66
+ end
67
+
68
+ _self.strong_resource.relations.each_pair do |relation_name, relation_config|
69
+ context.send(:swagger_schema, :"#{strong_resource.name}_#{relation_name}_#{action_name}") do
70
+ relation_config[:resource].attributes.each_pair do |attribute, config|
71
+ property attribute do
72
+ key :type, config[:type] # TODO - swagger type?
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def generate
80
+ _self = self
81
+
82
+ define_schema
83
+ @node.operation :post do
84
+ key :description, _self.description
85
+ key :operationId, _self.operation_id
86
+ key :tags, _self.all_tags
87
+ end
88
+ end
89
+ end
@@ -1,13 +1,26 @@
1
1
  require 'swagger/blocks'
2
- require "jsonapi_swagger_helpers/version"
3
- require "jsonapi_swagger_helpers/schema_helpers"
4
- require "jsonapi_swagger_helpers/strong_resource_mixin"
2
+ require "jsonapi_spec_helpers"
3
+ require "jsonapi_swagger_helpers/configuration"
4
+ require "jsonapi_swagger_helpers/payload_definition"
5
+ require "jsonapi_swagger_helpers/util"
6
+ require "jsonapi_swagger_helpers/readable"
7
+ require "jsonapi_swagger_helpers/writeable"
8
+ require "jsonapi_swagger_helpers/index_action"
9
+ require "jsonapi_swagger_helpers/show_action"
10
+ require "jsonapi_swagger_helpers/create_action"
11
+ require "jsonapi_swagger_helpers/update_action"
12
+ require "jsonapi_swagger_helpers/destroy_action"
13
+
5
14
  require "jsonapi_swagger_helpers/resource_mixin"
6
15
  require "jsonapi_swagger_helpers/docs_controller_mixin"
7
16
 
8
17
  module JsonapiSwaggerHelpers
9
- def self.prepended(klass)
10
- klass.send(:include, StrongResourceMixin)
18
+ def self.configure
19
+ yield config
20
+ end
21
+
22
+ def self.config
23
+ @config ||= Configuration.new
11
24
  end
12
25
 
13
26
  def self.docs_controller
@@ -17,125 +30,6 @@ module JsonapiSwaggerHelpers
17
30
  def self.docs_controller=(controller)
18
31
  @docs_controller = controller
19
32
  end
20
-
21
- def jsonapi_link
22
- "<br/><p><a href='http://jsonapi.org'>JSONAPI-compliant</a> endpoint.</p><br />"
23
- end
24
-
25
- def validation_messages(messages)
26
- string = "<p><b>Validations:</b><ul>"
27
- messages.each do |message|
28
- string << "<li>#{message}</li>"
29
- end
30
- string << "</ul></p>"
31
- end
32
-
33
- def jsonapi_index(controller)
34
- jsonapi_includes(controller, :index)
35
- jsonapi_filters(controller)
36
- jsonapi_stats(controller)
37
- jsonapi_pagination
38
- jsonapi_sorting
39
- end
40
-
41
- def jsonapi_show(controller)
42
- id_in_url
43
- jsonapi_includes(controller, :show)
44
- end
45
-
46
- def id_in_url
47
- parameter do
48
- key :name, :id
49
- key :in, :path
50
- key :type, :string
51
- key :required, true
52
- key :description, 'record id'
53
- end
54
- end
55
-
56
- def jsonapi_filters(controller)
57
- filter_names = []
58
- controller._jsonapi_compliable.filters.each_pair do |name, opts|
59
- filter_names << name
60
- end
61
-
62
- filter_names.each do |filter_name|
63
- parameter do
64
- key :name, "filter[#{filter_name}]"
65
- key :in, :query
66
- key :type, :string
67
- key :required, false
68
- key :description, "<a href='http://jsonapi.org/format/#fetching-filtering'>JSONAPI filter</a>"
69
-
70
- items do
71
- key :model, :string
72
- end
73
- end
74
- end
75
- end
76
-
77
- def jsonapi_stats(controller)
78
- controller._jsonapi_compliable.stats.each_pair do |stat_name, opts|
79
- calculations = opts.calculations.keys - [:keys]
80
- calculations = calculations.join('<br/>')
81
- parameter do
82
- key :name, "stats[#{stat_name}]"
83
- key :in, :query
84
- key :type, :string
85
- key :required, false
86
- key :description, "<a href='http://jsonapi.org/format/#document-meta'>JSONAPI meta data</a><br/> #{calculations}"
87
-
88
- items do
89
- key :model, :string
90
- end
91
- end
92
- end
93
- end
94
-
95
- def jsonapi_pagination
96
- parameter do
97
- key :name, "page[size]"
98
- key :in, :query
99
- key :type, :string
100
- key :required, false
101
- key :description, "<a href='http://jsonapi.org/format/#fetching-pagination'>JSONAPI page size</a>"
102
- end
103
-
104
- parameter do
105
- key :name, "page[number]"
106
- key :in, :query
107
- key :type, :string
108
- key :required, false
109
- key :description, "<a href='http://jsonapi.org/format/#fetching-pagination'>JSONAPI page number</a>"
110
- end
111
- end
112
-
113
- def jsonapi_sorting
114
- parameter do
115
- key :name, :sort
116
- key :in, :query
117
- key :type, :string
118
- key :required, false
119
- key :description, "<a href='http://jsonapi.org/format/#fetching-sorting'>JSONAPI sort</a>"
120
- end
121
- end
122
-
123
- def jsonapi_includes(controller, action)
124
- includes = controller._jsonapi_compliable.sideloads[:whitelist]
125
-
126
- if includes && includes[action]
127
- directive = includes[action]
128
- includes = directive.to_string.split(",").sort.join(",<br/>")
129
-
130
- parameter do
131
- key :name, :include
132
- key :in, :query
133
- key :type, :string
134
- key :required, false
135
- key :description, "<a href='http://jsonapi.org/format/#fetching-includes'>JSONAPI includes</a>: <br/> #{includes}"
136
- end
137
- end
138
- end
139
33
  end
140
34
 
141
35
  Swagger::Blocks::OperationNode.prepend(JsonapiSwaggerHelpers)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi_swagger_helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-26 00:00:00.000000000 Z
11
+ date: 2017-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: swagger-blocks
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jsonapi_spec_helpers
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "<"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -84,11 +98,19 @@ files:
84
98
  - bin/setup
85
99
  - jsonapi_swagger_helpers.gemspec
86
100
  - lib/jsonapi_swagger_helpers.rb
101
+ - lib/jsonapi_swagger_helpers/configuration.rb
102
+ - lib/jsonapi_swagger_helpers/create_action.rb
103
+ - lib/jsonapi_swagger_helpers/destroy_action.rb
87
104
  - lib/jsonapi_swagger_helpers/docs_controller_mixin.rb
105
+ - lib/jsonapi_swagger_helpers/index_action.rb
106
+ - lib/jsonapi_swagger_helpers/payload_definition.rb
107
+ - lib/jsonapi_swagger_helpers/readable.rb
88
108
  - lib/jsonapi_swagger_helpers/resource_mixin.rb
89
- - lib/jsonapi_swagger_helpers/schema_helpers.rb
90
- - lib/jsonapi_swagger_helpers/strong_resource_mixin.rb
109
+ - lib/jsonapi_swagger_helpers/show_action.rb
110
+ - lib/jsonapi_swagger_helpers/update_action.rb
111
+ - lib/jsonapi_swagger_helpers/util.rb
91
112
  - lib/jsonapi_swagger_helpers/version.rb
113
+ - lib/jsonapi_swagger_helpers/writeable.rb
92
114
  homepage:
93
115
  licenses:
94
116
  - MIT
@@ -109,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
131
  version: '0'
110
132
  requirements: []
111
133
  rubyforge_project:
112
- rubygems_version: 2.5.1
134
+ rubygems_version: 2.6.11
113
135
  signing_key:
114
136
  specification_version: 4
115
137
  summary: Swagger helpers for jsonapi.org-compatible APIs
@@ -1,78 +0,0 @@
1
- module JsonapiSwaggerHelpers
2
- class SchemaHelpers
3
- def self.attributes_schema
4
- lambda do |attributes|
5
- property :attributes do
6
- key :type, :object
7
-
8
- attributes.each_pair do |name, attr_type|
9
- property name do
10
- key :name, name
11
- key :type, attr_type
12
- end
13
- end
14
- end
15
- end
16
- end
17
-
18
- def self.relationships_schema
19
- lambda do |relationships|
20
- property :relationships do
21
- key :type, :object
22
-
23
- relationships.each_pair do |relation_name, opts|
24
- property relation_name do
25
- property :data do
26
- if opts[:array]
27
- key :type, :array
28
- items do
29
- if opts[:id]
30
- property :id do
31
- key :type, :string
32
- end
33
- end
34
-
35
- property :type do
36
- key :type, :string
37
- key :required, true
38
- key :enum, [opts[:jsonapi_type]]
39
- end
40
-
41
- if opts[:attributes]
42
- instance_exec(opts[:attributes], &SchemaHelpers.attributes_schema)
43
- end
44
-
45
- if opts[:relationships]
46
- instance_exec(opts[:relationships], &SchemaHelpers.relationships_schema)
47
- end
48
- end
49
- else
50
- if opts[:id]
51
- property :id do
52
- key :type, :string
53
- end
54
- end
55
-
56
- property :type do
57
- key :type, :string
58
- key :required, true
59
- key :enum, [opts[:jsonapi_type]]
60
- end
61
-
62
- if opts[:attributes]
63
- instance_exec(opts[:attributes], &SchemaHelpers.attributes_schema)
64
- end
65
-
66
- if opts[:relationships]
67
- instance_exec(opts[:relationships], &SchemaHelpers.relationships_schema)
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end
76
-
77
- end
78
- end
@@ -1,120 +0,0 @@
1
- module JsonapiSwaggerHelpers
2
- module StrongResourceMixin
3
-
4
- class Schemas
5
- include Swagger::Blocks
6
-
7
- def self.schema(name, &blk)
8
- swagger_schema(name, &blk)
9
- end
10
- end
11
-
12
- def strong_resource(controller, action)
13
- update = action == :update
14
- resource = controller._strong_resources[action]
15
- opts = { jsonapi_type: resource.jsonapi_type }
16
-
17
- if (attributes = strong_resource_attributes(resource, update)).present?
18
- opts[:attributes] = attributes
19
- end
20
-
21
- if (relationships = strong_resource_relationships(resource, update)).present?
22
- opts[:relationships] = relationships
23
- end
24
-
25
- jsonapi_payload(SecureRandom.uuid.to_sym, opts)
26
- end
27
-
28
- def strong_resource_attributes(resource, is_update = false)
29
- attributes = {}
30
- resource.attributes.each_pair do |name, opts|
31
- type = StrongResources.config.strong_params[opts[:type]][:swagger]
32
- attributes[name] = type
33
-
34
- if is_update
35
- if resource.destroy?
36
- attributes[:_destroy] = :boolean
37
- end
38
-
39
- if resource.delete?
40
- attributes[:_delete] = :boolean
41
- end
42
- end
43
- end
44
-
45
- attributes = attributes.slice(*resource.only) if !!resource.only
46
- attributes = attributes.except(*resource.except) if !!resource.except
47
- attributes
48
- end
49
-
50
- def strong_resource_relationships(resource, is_update = false)
51
- {}.tap do |relations|
52
- resource.relations.each_pair do |relation_name, opts|
53
- resource = opts[:resource]
54
-
55
- payload = {
56
- jsonapi_type: resource.jsonapi_type,
57
- id: true,
58
- array: resource.has_many?
59
- }
60
-
61
- if (attributes = strong_resource_attributes(resource, is_update)).present?
62
- payload[:attributes] = attributes
63
- end
64
-
65
- if (relationships = strong_resource_relationships(resource, is_update)).present?
66
- payload[:relationships] = relationships
67
- end
68
-
69
- relations[relation_name] = payload
70
- end
71
- end
72
- end
73
-
74
- def jsonapi_payload(schema_name, payload)
75
- jsonapi_input_schema(schema_name, payload)
76
-
77
- parameter do
78
- key :name, :payload
79
- key :in, :body
80
-
81
- schema do
82
- key :'$ref', schema_name
83
- end
84
- end
85
- end
86
-
87
- def jsonapi_input_schema(schema_name,
88
- id: false,
89
- jsonapi_type:,
90
- attributes: nil,
91
- relationships: nil)
92
- Schemas.schema(schema_name) do
93
- property :data do
94
- key :type, :object
95
-
96
- property :type do
97
- key :type, :string
98
- key :required, true
99
- key :enum, [jsonapi_type]
100
- end
101
-
102
- if id
103
- property :id do
104
- key :type, :string
105
- end
106
- end
107
-
108
- if attributes
109
- instance_exec(attributes, &SchemaHelpers.attributes_schema)
110
- end
111
-
112
- if relationships
113
- instance_exec(relationships, &SchemaHelpers.relationships_schema)
114
- end
115
- end
116
- end
117
- end
118
-
119
- end
120
- end