apipierails3 0.0.1
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 +17 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +27 -0
- data/APACHE-LICENSE-2.0 +202 -0
- data/CHANGELOG.md +469 -0
- data/Gemfile +1 -0
- data/Gemfile.rails32 +6 -0
- data/Gemfile.rails41 +6 -0
- data/Gemfile.rails42 +11 -0
- data/Gemfile.rails50 +6 -0
- data/Gemfile.rails51 +7 -0
- data/MIT-LICENSE +20 -0
- data/NOTICE +4 -0
- data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
- data/README.rst +1874 -0
- data/Rakefile +13 -0
- data/apipierails3.gemspec +27 -0
- data/app/controllers/apipie/apipies_controller.rb +199 -0
- data/app/helpers/apipie_helper.rb +10 -0
- data/app/public/apipie/javascripts/apipie.js +6 -0
- data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
- data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
- data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
- data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
- data/app/public/apipie/stylesheets/application.css +7 -0
- data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
- data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
- data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
- data/app/views/apipie/apipies/_disqus.html.erb +13 -0
- data/app/views/apipie/apipies/_errors.html.erb +23 -0
- data/app/views/apipie/apipies/_headers.html.erb +26 -0
- data/app/views/apipie/apipies/_languages.erb +6 -0
- data/app/views/apipie/apipies/_metadata.erb +1 -0
- data/app/views/apipie/apipies/_method_detail.erb +61 -0
- data/app/views/apipie/apipies/_params.html.erb +42 -0
- data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
- data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
- data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
- data/app/views/apipie/apipies/getting_started.html.erb +6 -0
- data/app/views/apipie/apipies/index.html.erb +56 -0
- data/app/views/apipie/apipies/method.html.erb +41 -0
- data/app/views/apipie/apipies/plain.html.erb +77 -0
- data/app/views/apipie/apipies/resource.html.erb +80 -0
- data/app/views/apipie/apipies/static.html.erb +103 -0
- data/app/views/layouts/apipie/apipie.html.erb +27 -0
- data/config/locales/de.yml +28 -0
- data/config/locales/en.yml +32 -0
- data/config/locales/es.yml +28 -0
- data/config/locales/fr.yml +31 -0
- data/config/locales/it.yml +31 -0
- data/config/locales/ja.yml +31 -0
- data/config/locales/pl.yml +28 -0
- data/config/locales/pt-BR.yml +28 -0
- data/config/locales/ru.yml +28 -0
- data/config/locales/tr.yml +28 -0
- data/config/locales/zh-CN.yml +28 -0
- data/config/locales/zh-TW.yml +28 -0
- data/images/screenshot-1.png +0 -0
- data/images/screenshot-2.png +0 -0
- data/lib/apipie/apipie_module.rb +83 -0
- data/lib/apipie/application.rb +462 -0
- data/lib/apipie/configuration.rb +186 -0
- data/lib/apipie/dsl_definition.rb +607 -0
- data/lib/apipie/error_description.rb +44 -0
- data/lib/apipie/errors.rb +86 -0
- data/lib/apipie/extractor.rb +177 -0
- data/lib/apipie/extractor/collector.rb +117 -0
- data/lib/apipie/extractor/recorder.rb +166 -0
- data/lib/apipie/extractor/writer.rb +454 -0
- data/lib/apipie/helpers.rb +73 -0
- data/lib/apipie/markup.rb +48 -0
- data/lib/apipie/method_description.rb +273 -0
- data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
- data/lib/apipie/param_description.rb +280 -0
- data/lib/apipie/railtie.rb +9 -0
- data/lib/apipie/resource_description.rb +124 -0
- data/lib/apipie/response_description.rb +131 -0
- data/lib/apipie/response_description_adapter.rb +200 -0
- data/lib/apipie/routes_formatter.rb +33 -0
- data/lib/apipie/routing.rb +16 -0
- data/lib/apipie/rspec/response_validation_helper.rb +192 -0
- data/lib/apipie/see_description.rb +39 -0
- data/lib/apipie/static_dispatcher.rb +69 -0
- data/lib/apipie/swagger_generator.rb +707 -0
- data/lib/apipie/tag_list_description.rb +11 -0
- data/lib/apipie/validator.rb +526 -0
- data/lib/apipie/version.rb +3 -0
- data/lib/apipierails3.rb +25 -0
- data/lib/generators/apipie/install/README +6 -0
- data/lib/generators/apipie/install/install_generator.rb +25 -0
- data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
- data/lib/generators/apipie/views_generator.rb +11 -0
- data/lib/tasks/apipie.rake +345 -0
- data/rel-eng/packages/.readme +3 -0
- data/rel-eng/packages/rubygem-apipie-rails +1 -0
- data/rel-eng/tito.props +5 -0
- data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
- data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
- data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
- data/spec/controllers/apipies_controller_spec.rb +273 -0
- data/spec/controllers/concerns_controller_spec.rb +42 -0
- data/spec/controllers/extended_controller_spec.rb +11 -0
- data/spec/controllers/users_controller_spec.rb +740 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
- data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
- data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
- data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
- data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
- data/spec/dummy/app/controllers/application_controller.rb +18 -0
- data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
- data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
- data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
- data/spec/dummy/app/controllers/extended_controller.rb +14 -0
- data/spec/dummy/app/controllers/files_controller.rb +5 -0
- data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
- data/spec/dummy/app/controllers/pets_controller.rb +408 -0
- data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
- data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
- data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
- data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
- data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
- data/spec/dummy/app/controllers/users_controller.rb +297 -0
- data/spec/dummy/app/views/layouts/application.html.erb +21 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +49 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +21 -0
- data/spec/dummy/config/environment.rb +8 -0
- data/spec/dummy/config/environments/development.rb +28 -0
- data/spec/dummy/config/environments/production.rb +52 -0
- data/spec/dummy/config/environments/test.rb +38 -0
- data/spec/dummy/config/initializers/apipie.rb +110 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +8 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +51 -0
- data/spec/dummy/db/.gitkeep +0 -0
- data/spec/dummy/doc/apipie_examples.json +1 -0
- data/spec/dummy/doc/users/desc_from_file.md +1 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/lib/application_spec.rb +49 -0
- data/spec/lib/extractor/extractor_spec.rb +9 -0
- data/spec/lib/extractor/middleware_spec.rb +44 -0
- data/spec/lib/extractor/writer_spec.rb +110 -0
- data/spec/lib/file_handler_spec.rb +18 -0
- data/spec/lib/method_description_spec.rb +98 -0
- data/spec/lib/param_description_spec.rb +345 -0
- data/spec/lib/param_group_spec.rb +60 -0
- data/spec/lib/rake_spec.rb +71 -0
- data/spec/lib/resource_description_spec.rb +48 -0
- data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
- data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
- data/spec/lib/swagger/response_validation_spec.rb +104 -0
- data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
- data/spec/lib/validator_spec.rb +113 -0
- data/spec/lib/validators/array_validator_spec.rb +85 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/support/rake.rb +21 -0
- metadata +415 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Apipie
|
|
2
|
+
module Helpers
|
|
3
|
+
def markup_to_html(text)
|
|
4
|
+
return "" if text.nil?
|
|
5
|
+
if Apipie.configuration.markup.respond_to? :to_html
|
|
6
|
+
Apipie.configuration.markup.to_html(text.strip_heredoc)
|
|
7
|
+
else
|
|
8
|
+
text.strip_heredoc
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_accessor :url_prefix
|
|
13
|
+
|
|
14
|
+
def request_script_name
|
|
15
|
+
Thread.current[:apipie_req_script_name] || ""
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def request_script_name=(script_name)
|
|
19
|
+
Thread.current[:apipie_req_script_name] = script_name
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def full_url(path)
|
|
23
|
+
unless @url_prefix
|
|
24
|
+
@url_prefix = ""
|
|
25
|
+
@url_prefix << request_script_name
|
|
26
|
+
@url_prefix << Apipie.configuration.doc_base_url
|
|
27
|
+
end
|
|
28
|
+
path = path.sub(/^\//,"")
|
|
29
|
+
ret = "#{@url_prefix}/#{path}"
|
|
30
|
+
ret.insert(0,"/") unless ret =~ /\A[.\/]/
|
|
31
|
+
ret.sub!(/\/*\Z/,"")
|
|
32
|
+
ret
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def include_javascripts
|
|
36
|
+
%w[ bundled/jquery.js
|
|
37
|
+
bundled/bootstrap-collapse.js
|
|
38
|
+
bundled/prettify.js
|
|
39
|
+
apipie.js ].map do |file|
|
|
40
|
+
"<script type='text/javascript' src='#{Apipie.full_url("javascripts/#{file}")}'></script>"
|
|
41
|
+
end.join("\n").html_safe
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def include_stylesheets
|
|
45
|
+
%w[ bundled/bootstrap.min.css
|
|
46
|
+
bundled/prettify.css
|
|
47
|
+
bundled/bootstrap-responsive.min.css ].map do |file|
|
|
48
|
+
"<link type='text/css' rel='stylesheet' href='#{Apipie.full_url("stylesheets/#{file}")}'/>"
|
|
49
|
+
end.join("\n").html_safe
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def label_class_for_error(err)
|
|
53
|
+
case err[:code]
|
|
54
|
+
when 200
|
|
55
|
+
'label label-info'
|
|
56
|
+
when 201
|
|
57
|
+
'label label-success'
|
|
58
|
+
when 204
|
|
59
|
+
'label label-info2'
|
|
60
|
+
when 401
|
|
61
|
+
'label label-warning'
|
|
62
|
+
when 403
|
|
63
|
+
'label label-warning2'
|
|
64
|
+
when 422
|
|
65
|
+
'label label-important'
|
|
66
|
+
when 404
|
|
67
|
+
'label label-inverse'
|
|
68
|
+
else
|
|
69
|
+
'label'
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Apipie
|
|
2
|
+
|
|
3
|
+
module Markup
|
|
4
|
+
|
|
5
|
+
class RDoc
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
require 'rdoc'
|
|
9
|
+
require 'rdoc/markup/to_html'
|
|
10
|
+
if Gem::Version.new(::RDoc::VERSION) < Gem::Version.new('4.0.0')
|
|
11
|
+
@rdoc ||= ::RDoc::Markup::ToHtml.new()
|
|
12
|
+
else
|
|
13
|
+
@rdoc ||= ::RDoc::Markup::ToHtml.new(::RDoc::Options.new)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_html(text)
|
|
18
|
+
@rdoc.convert(text)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Markdown
|
|
24
|
+
|
|
25
|
+
def initialize
|
|
26
|
+
require 'maruku'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_html(text)
|
|
30
|
+
Maruku.new(text).to_html
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Textile
|
|
36
|
+
|
|
37
|
+
def initialize
|
|
38
|
+
require 'RedCloth'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_html(text)
|
|
42
|
+
RedCloth.new(text).to_html
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
module Apipie
|
|
3
|
+
|
|
4
|
+
class MethodDescription
|
|
5
|
+
|
|
6
|
+
class Api
|
|
7
|
+
|
|
8
|
+
attr_accessor :short_description, :path, :http_method, :from_routes, :options, :returns
|
|
9
|
+
|
|
10
|
+
def initialize(method, path, desc, options)
|
|
11
|
+
@http_method = method.to_s
|
|
12
|
+
@path = path
|
|
13
|
+
@short_description = desc
|
|
14
|
+
@from_routes = options[:from_routes]
|
|
15
|
+
@options = options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :full_description, :method, :resource, :apis, :examples, :see, :formats, :metadata, :headers, :show
|
|
21
|
+
|
|
22
|
+
def initialize(method, resource, dsl_data)
|
|
23
|
+
@method = method.to_s
|
|
24
|
+
@resource = resource
|
|
25
|
+
@from_concern = dsl_data[:from_concern]
|
|
26
|
+
@apis = api_data(dsl_data).map do |mthd, path, desc, opts|
|
|
27
|
+
MethodDescription::Api.new(mthd, concern_subst(path), concern_subst(desc), opts)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
desc = dsl_data[:description] || ''
|
|
31
|
+
@full_description = Apipie.markup_to_html(desc)
|
|
32
|
+
|
|
33
|
+
@errors = dsl_data[:errors].map do |args|
|
|
34
|
+
Apipie::ErrorDescription.from_dsl_data(args)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@tag_list = dsl_data[:tag_list]
|
|
38
|
+
|
|
39
|
+
@returns = dsl_data[:returns].map do |code,args|
|
|
40
|
+
Apipie::ResponseDescription.from_dsl_data(self, code, args)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@see = dsl_data[:see].map do |args|
|
|
44
|
+
Apipie::SeeDescription.new(args)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@formats = dsl_data[:formats]
|
|
48
|
+
@examples = dsl_data[:examples]
|
|
49
|
+
@examples += load_recorded_examples
|
|
50
|
+
|
|
51
|
+
@metadata = dsl_data[:meta]
|
|
52
|
+
|
|
53
|
+
@params_ordered = dsl_data[:params].map do |args|
|
|
54
|
+
Apipie::ParamDescription.from_dsl_data(self, args)
|
|
55
|
+
end.reject{|p| p.response_only? }
|
|
56
|
+
|
|
57
|
+
@params_ordered = ParamDescription.unify(@params_ordered)
|
|
58
|
+
@headers = dsl_data[:headers]
|
|
59
|
+
|
|
60
|
+
@show = if dsl_data.has_key? :show
|
|
61
|
+
dsl_data[:show]
|
|
62
|
+
else
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def id
|
|
68
|
+
"#{resource._id}##{method}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def params
|
|
72
|
+
params_ordered.reduce(ActiveSupport::OrderedHash.new) { |h,p| h[p.name] = p; h }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def params_ordered_self
|
|
76
|
+
@params_ordered
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def params_ordered
|
|
80
|
+
all_params = []
|
|
81
|
+
parent = Apipie.get_resource_description(@resource.controller.superclass)
|
|
82
|
+
|
|
83
|
+
# get params from parent resource description
|
|
84
|
+
[parent, @resource].compact.each do |resource|
|
|
85
|
+
resource_params = resource._params_args.map do |args|
|
|
86
|
+
Apipie::ParamDescription.from_dsl_data(self, args)
|
|
87
|
+
end
|
|
88
|
+
merge_params(all_params, resource_params)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
merge_params(all_params, @params_ordered)
|
|
92
|
+
all_params.find_all(&:validator)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def returns_self
|
|
96
|
+
@returns
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def tag_list
|
|
100
|
+
all_tag_list = []
|
|
101
|
+
parent = Apipie.get_resource_description(@resource.controller.superclass)
|
|
102
|
+
|
|
103
|
+
# get tags from parent resource description
|
|
104
|
+
parent_tags = [parent, @resource].compact.flat_map { |resource| resource._tag_list_arg }
|
|
105
|
+
Apipie::TagListDescription.new((parent_tags + @tag_list).uniq.compact)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def returns
|
|
109
|
+
all_returns = []
|
|
110
|
+
parent = Apipie.get_resource_description(@resource.controller.superclass)
|
|
111
|
+
|
|
112
|
+
# get response descriptions from parent resource description
|
|
113
|
+
[parent, @resource].compact.each do |resource|
|
|
114
|
+
resource_returns = resource._returns_args.map do |code, args|
|
|
115
|
+
Apipie::ResponseDescription.from_dsl_data(self, code, args)
|
|
116
|
+
end
|
|
117
|
+
merge_returns(all_returns, resource_returns)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
merge_returns(all_returns, @returns)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def errors
|
|
124
|
+
return @merged_errors if @merged_errors
|
|
125
|
+
@merged_errors = []
|
|
126
|
+
if @resource
|
|
127
|
+
resource_errors = @resource._errors_args.map do |args|
|
|
128
|
+
Apipie::ErrorDescription.from_dsl_data(args)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# exclude overwritten parent errors
|
|
132
|
+
@merged_errors = resource_errors.find_all do |err|
|
|
133
|
+
!@errors.any? { |e| e.code == err.code }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
@merged_errors.concat(@errors)
|
|
137
|
+
return @merged_errors
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def version
|
|
141
|
+
resource._version
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def doc_url
|
|
145
|
+
crumbs = []
|
|
146
|
+
crumbs << @resource._version if Apipie.configuration.version_in_url
|
|
147
|
+
crumbs << @resource._id
|
|
148
|
+
crumbs << @method
|
|
149
|
+
Apipie.full_url crumbs.join('/')
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def create_api_url(api)
|
|
153
|
+
path = api.path
|
|
154
|
+
unless api.from_routes
|
|
155
|
+
path = "#{@resource._api_base_url}#{path}"
|
|
156
|
+
end
|
|
157
|
+
path = path[0..-2] if path[-1..-1] == '/'
|
|
158
|
+
return path
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def method_apis_to_json(lang = nil)
|
|
162
|
+
@apis.each.collect do |api|
|
|
163
|
+
{
|
|
164
|
+
:api_url => create_api_url(api),
|
|
165
|
+
:http_method => api.http_method.to_s,
|
|
166
|
+
:short_description => Apipie.app.translate(api.short_description, lang),
|
|
167
|
+
:deprecated => resource._deprecated || api.options[:deprecated]
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def see
|
|
173
|
+
@see
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def formats
|
|
177
|
+
@formats || @resource._formats
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def to_json(lang=nil)
|
|
181
|
+
{
|
|
182
|
+
:doc_url => doc_url,
|
|
183
|
+
:name => @method,
|
|
184
|
+
:apis => method_apis_to_json(lang),
|
|
185
|
+
:formats => formats,
|
|
186
|
+
:full_description => Apipie.app.translate(@full_description, lang),
|
|
187
|
+
:errors => errors.map(&:to_json),
|
|
188
|
+
:params => params_ordered.map{ |param| param.to_json(lang) }.flatten,
|
|
189
|
+
:returns => @returns.map{ |return_item| return_item.to_json(lang) }.flatten,
|
|
190
|
+
:examples => @examples,
|
|
191
|
+
:metadata => @metadata,
|
|
192
|
+
:see => see.map(&:to_json),
|
|
193
|
+
:headers => headers,
|
|
194
|
+
:show => @show
|
|
195
|
+
}
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# was the description defines in a module instead of directly in controller?
|
|
199
|
+
def from_concern?
|
|
200
|
+
@from_concern
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
private
|
|
204
|
+
|
|
205
|
+
def api_data(dsl_data)
|
|
206
|
+
ret = dsl_data[:api_args].dup
|
|
207
|
+
if dsl_data[:api_from_routes]
|
|
208
|
+
desc = dsl_data[:api_from_routes][:desc]
|
|
209
|
+
options = dsl_data[:api_from_routes][:options]
|
|
210
|
+
|
|
211
|
+
api_from_routes = Apipie.routes_for_action(resource.controller, method, {:desc => desc, :options => options}).map do |route_info|
|
|
212
|
+
[route_info[:verb],
|
|
213
|
+
route_info[:path],
|
|
214
|
+
route_info[:desc],
|
|
215
|
+
(route_info[:options] || {}).merge(:from_routes => true)]
|
|
216
|
+
end
|
|
217
|
+
ret.concat(api_from_routes)
|
|
218
|
+
end
|
|
219
|
+
ret
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def merge_params(params, new_params)
|
|
223
|
+
new_param_names = Set.new(new_params.map(&:name))
|
|
224
|
+
params.delete_if { |p| new_param_names.include?(p.name) }
|
|
225
|
+
params.concat(new_params)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def merge_returns(returns, new_returns)
|
|
229
|
+
new_return_codes = Set.new(new_returns.map(&:code))
|
|
230
|
+
returns.delete_if { |p| new_return_codes.include?(p.code) }
|
|
231
|
+
returns.concat(new_returns)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def load_recorded_examples
|
|
235
|
+
(Apipie.recorded_examples[id] || []).
|
|
236
|
+
find_all { |ex| ex["show_in_doc"].to_i > 0 }.
|
|
237
|
+
find_all { |ex| ex["versions"].nil? || ex["versions"].include?(self.version) }.
|
|
238
|
+
sort_by { |ex| ex["show_in_doc"] }.
|
|
239
|
+
map { |ex| format_example(ex.symbolize_keys) }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def format_example_data(data)
|
|
243
|
+
case data
|
|
244
|
+
when Array, Hash
|
|
245
|
+
JSON.pretty_generate(data).gsub(/: \[\s*\]/,": []").gsub(/\{\s*\}/,"{}")
|
|
246
|
+
else
|
|
247
|
+
data
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def format_example(ex)
|
|
252
|
+
example = ""
|
|
253
|
+
example << "// #{ex[:title]}\n" if ex[:title].present?
|
|
254
|
+
example << "#{ex[:verb]} #{ex[:path]}"
|
|
255
|
+
example << "?#{ex[:query]}" unless ex[:query].blank?
|
|
256
|
+
example << "\n" << format_example_data(ex[:request_data]).to_s if ex[:request_data]
|
|
257
|
+
example << "\n" << ex[:code].to_s
|
|
258
|
+
example << "\n" << format_example_data(ex[:response_data]).to_s if ex[:response_data]
|
|
259
|
+
example
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def concern_subst(string)
|
|
263
|
+
return if string.nil?
|
|
264
|
+
if from_concern?
|
|
265
|
+
resource.controller._apipie_perform_concern_subst(string)
|
|
266
|
+
else
|
|
267
|
+
string
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Middleware for rails app that adds checksum of JSON in the response headers
|
|
2
|
+
# which can help client to realize when JSON has changed
|
|
3
|
+
#
|
|
4
|
+
# Add the following to your application.rb
|
|
5
|
+
# require 'apipie/middleware/checksum_in_headers'
|
|
6
|
+
# # Add JSON checksum in headers for smarter caching
|
|
7
|
+
# config.middleware.use "Apipie::Middleware::ChecksumInHeaders"
|
|
8
|
+
#
|
|
9
|
+
# And in your apipie initializer allow checksum calculation
|
|
10
|
+
# Apipie.configuration.update_checksum = true
|
|
11
|
+
# and reload documentation
|
|
12
|
+
# Apipie.reload_documentation
|
|
13
|
+
#
|
|
14
|
+
# By default the header is added to requests on /api and /apipie only
|
|
15
|
+
# It can be changed with
|
|
16
|
+
# Apipie.configuration.checksum_path = ['/prefix/api']
|
|
17
|
+
# If set to nil the header is added always
|
|
18
|
+
|
|
19
|
+
module Apipie
|
|
20
|
+
module Middleware
|
|
21
|
+
class ChecksumInHeaders
|
|
22
|
+
def initialize(app)
|
|
23
|
+
@app = app
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call(env)
|
|
27
|
+
status, headers, body = @app.call(env)
|
|
28
|
+
if !Apipie.configuration.checksum_path || env['PATH_INFO'].start_with?(*Apipie.configuration.checksum_path)
|
|
29
|
+
headers.merge!( 'Apipie-Checksum' => Apipie.checksum )
|
|
30
|
+
end
|
|
31
|
+
return [status, headers, body]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
module Apipie
|
|
2
|
+
|
|
3
|
+
# method parameter description
|
|
4
|
+
#
|
|
5
|
+
# name - method name (show)
|
|
6
|
+
# desc - description
|
|
7
|
+
# required - boolean if required
|
|
8
|
+
# validator - Validator::BaseValidator subclass
|
|
9
|
+
class ParamDescription
|
|
10
|
+
|
|
11
|
+
attr_reader :method_description, :name, :desc, :allow_nil, :allow_blank, :validator, :options, :metadata, :show, :as, :validations, :response_only, :request_only
|
|
12
|
+
attr_reader :additional_properties, :is_array
|
|
13
|
+
attr_accessor :parent, :required
|
|
14
|
+
|
|
15
|
+
alias_method :response_only?, :response_only
|
|
16
|
+
alias_method :request_only?, :request_only
|
|
17
|
+
alias_method :is_array?, :is_array
|
|
18
|
+
|
|
19
|
+
def self.from_dsl_data(method_description, args)
|
|
20
|
+
param_name, validator, desc_or_options, options, block = args
|
|
21
|
+
Apipie::ParamDescription.new(method_description,
|
|
22
|
+
param_name,
|
|
23
|
+
validator,
|
|
24
|
+
desc_or_options,
|
|
25
|
+
options,
|
|
26
|
+
&block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
"ParamDescription: #{method_description.id}##{name}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ==(other)
|
|
34
|
+
return false unless self.class == other.class
|
|
35
|
+
if method_description == other.method_description && @options == other.options
|
|
36
|
+
true
|
|
37
|
+
else
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(method_description, name, validator, desc_or_options = nil, options = {}, &block)
|
|
43
|
+
|
|
44
|
+
if desc_or_options.is_a?(Hash)
|
|
45
|
+
options = options.merge(desc_or_options)
|
|
46
|
+
elsif desc_or_options.is_a?(String)
|
|
47
|
+
options[:desc] = desc_or_options
|
|
48
|
+
elsif !desc_or_options.nil?
|
|
49
|
+
raise ArgumentError.new("param description: expected description or options as 3rd parameter")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
options.symbolize_keys!
|
|
53
|
+
|
|
54
|
+
# we save options to know what was passed in DSL
|
|
55
|
+
@options = options
|
|
56
|
+
if @options[:param_group]
|
|
57
|
+
@from_concern = @options[:param_group][:from_concern]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
@method_description = method_description
|
|
61
|
+
@name = concern_subst(name)
|
|
62
|
+
@as = options[:as] || @name
|
|
63
|
+
@desc = preformat_text(@options[:desc])
|
|
64
|
+
|
|
65
|
+
@parent = @options[:parent]
|
|
66
|
+
@metadata = @options[:meta]
|
|
67
|
+
|
|
68
|
+
@required = is_required?
|
|
69
|
+
|
|
70
|
+
@response_only = (@options[:only_in] == :response)
|
|
71
|
+
@request_only = (@options[:only_in] == :request)
|
|
72
|
+
raise ArgumentError.new("'#{@options[:only_in]}' is not a valid value for :only_in") if (!@response_only && !@request_only) && @options[:only_in].present?
|
|
73
|
+
|
|
74
|
+
@show = if @options.has_key? :show
|
|
75
|
+
@options[:show]
|
|
76
|
+
else
|
|
77
|
+
true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@allow_nil = @options[:allow_nil] || false
|
|
81
|
+
@allow_blank = @options[:allow_blank] || false
|
|
82
|
+
|
|
83
|
+
action_awareness
|
|
84
|
+
|
|
85
|
+
if validator
|
|
86
|
+
if (validator != Hash) && (validator.is_a? Hash) && (validator[:array_of])
|
|
87
|
+
@is_array = true
|
|
88
|
+
rest_of_options = validator
|
|
89
|
+
validator = validator[:array_of]
|
|
90
|
+
options.merge!(rest_of_options.select{|k,v| k != :array_of })
|
|
91
|
+
raise "an ':array_of =>' validator is allowed exclusively on response-only fields" unless @response_only
|
|
92
|
+
end
|
|
93
|
+
@validator = Validator::BaseValidator.find(self, validator, @options, block)
|
|
94
|
+
raise "Validator for #{validator} not found." unless @validator
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
@validations = Array(options[:validations]).map {|v| concern_subst(Apipie.markup_to_html(v)) }
|
|
98
|
+
|
|
99
|
+
@additional_properties = @options[:additional_properties]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def from_concern?
|
|
103
|
+
method_description.from_concern? || @from_concern
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def normalized_value(value)
|
|
107
|
+
if value.is_a?(ActionController::Parameters) && !value.is_a?(Hash)
|
|
108
|
+
value.to_unsafe_hash
|
|
109
|
+
elsif value.is_a? Array
|
|
110
|
+
value.map { |v| normalized_value (v) }
|
|
111
|
+
else
|
|
112
|
+
value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def validate(value)
|
|
117
|
+
return true if @allow_nil && value.nil?
|
|
118
|
+
return true if @allow_blank && value.blank?
|
|
119
|
+
value = normalized_value(value)
|
|
120
|
+
if (!@allow_nil && value.nil?) || !@validator.valid?(value)
|
|
121
|
+
error = @validator.error
|
|
122
|
+
error = ParamError.new(error) unless error.is_a? StandardError
|
|
123
|
+
raise error
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def process_value(value)
|
|
128
|
+
value = normalized_value(value)
|
|
129
|
+
if @validator.respond_to?(:process_value)
|
|
130
|
+
@validator.process_value(value)
|
|
131
|
+
else
|
|
132
|
+
value
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def full_name
|
|
137
|
+
name_parts = parents_and_self.map{|p| p.name if p.show}.compact
|
|
138
|
+
return name.to_s if name_parts.blank?
|
|
139
|
+
return ([name_parts.first] + name_parts[1..-1].map { |n| "[#{n}]" }).join("")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# returns an array of all the parents: starting with the root parent
|
|
143
|
+
# ending with itself
|
|
144
|
+
def parents_and_self
|
|
145
|
+
ret = []
|
|
146
|
+
if self.parent
|
|
147
|
+
ret.concat(self.parent.parents_and_self)
|
|
148
|
+
end
|
|
149
|
+
ret << self
|
|
150
|
+
ret
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def to_json(lang = nil)
|
|
154
|
+
hash = { :name => name.to_s,
|
|
155
|
+
:full_name => full_name,
|
|
156
|
+
:description => preformat_text(Apipie.app.translate(@options[:desc], lang)),
|
|
157
|
+
:required => required,
|
|
158
|
+
:allow_nil => allow_nil,
|
|
159
|
+
:allow_blank => allow_blank,
|
|
160
|
+
:validator => validator.to_s,
|
|
161
|
+
:expected_type => validator.expected_type,
|
|
162
|
+
:metadata => metadata,
|
|
163
|
+
:show => show,
|
|
164
|
+
:validations => validations }
|
|
165
|
+
if sub_params = validator.params_ordered
|
|
166
|
+
hash[:params] = sub_params.map { |p| p.to_json(lang)}
|
|
167
|
+
end
|
|
168
|
+
hash
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def merge_with(other_param_desc)
|
|
172
|
+
if self.validator && other_param_desc.validator
|
|
173
|
+
self.validator.merge_with(other_param_desc.validator)
|
|
174
|
+
else
|
|
175
|
+
self.validator ||= other_param_desc.validator
|
|
176
|
+
end
|
|
177
|
+
self
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# merge param descriptions. Allows defining hash params on more places
|
|
181
|
+
# (e.g. in param_groups). For example:
|
|
182
|
+
#
|
|
183
|
+
# def_param_group :user do
|
|
184
|
+
# param :user, Hash do
|
|
185
|
+
# param :name, String
|
|
186
|
+
# end
|
|
187
|
+
# end
|
|
188
|
+
#
|
|
189
|
+
# param_group :user
|
|
190
|
+
# param :user, Hash do
|
|
191
|
+
# param :password, String
|
|
192
|
+
# end
|
|
193
|
+
def self.unify(params)
|
|
194
|
+
ordering = params.map(&:name)
|
|
195
|
+
params.group_by(&:name).map do |name, param_descs|
|
|
196
|
+
param_descs.reduce(&:merge_with)
|
|
197
|
+
end.sort_by { |param| ordering.index(param.name) }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def self.merge(target_params, source_params)
|
|
201
|
+
params_to_merge, params_to_add = source_params.partition do |source_param|
|
|
202
|
+
target_params.any? { |target_param| source_param.name == target_param.name }
|
|
203
|
+
end
|
|
204
|
+
unify(target_params + params_to_merge)
|
|
205
|
+
target_params.concat(params_to_add)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# action awareness is being inherited from ancestors (in terms of
|
|
209
|
+
# nested params)
|
|
210
|
+
def action_aware?
|
|
211
|
+
if @options.has_key?(:action_aware)
|
|
212
|
+
return @options[:action_aware]
|
|
213
|
+
elsif @parent
|
|
214
|
+
@parent.action_aware?
|
|
215
|
+
else
|
|
216
|
+
false
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def as_action
|
|
221
|
+
if @options[:param_group] && @options[:param_group][:options] &&
|
|
222
|
+
@options[:param_group][:options][:as]
|
|
223
|
+
@options[:param_group][:options][:as].to_s
|
|
224
|
+
elsif @parent
|
|
225
|
+
@parent.as_action
|
|
226
|
+
else
|
|
227
|
+
@method_description.method
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# makes modification that are based on the action that the param
|
|
232
|
+
# is defined for. Typical for required and allow_nil variations in
|
|
233
|
+
# crate/update actions.
|
|
234
|
+
def action_awareness
|
|
235
|
+
if action_aware?
|
|
236
|
+
if !@options.has_key?(:allow_nil)
|
|
237
|
+
if @required
|
|
238
|
+
@allow_nil = false
|
|
239
|
+
else
|
|
240
|
+
@allow_nil = true
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
if as_action != "create"
|
|
244
|
+
@required = false
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def concern_subst(string)
|
|
250
|
+
return string if string.nil? or !from_concern?
|
|
251
|
+
|
|
252
|
+
original = string
|
|
253
|
+
string = ":#{original}" if original.is_a? Symbol
|
|
254
|
+
|
|
255
|
+
replaced = method_description.resource.controller._apipie_perform_concern_subst(string)
|
|
256
|
+
|
|
257
|
+
return original if replaced == string
|
|
258
|
+
return replaced.to_sym if original.is_a? Symbol
|
|
259
|
+
return replaced
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def preformat_text(text)
|
|
263
|
+
concern_subst(Apipie.markup_to_html(text || ''))
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def is_required?
|
|
267
|
+
if @options.has_key?(:required)
|
|
268
|
+
if (@options[:required] == true) || (@options[:required] == false)
|
|
269
|
+
@options[:required]
|
|
270
|
+
else
|
|
271
|
+
Array(@options[:required]).include?(@method_description.method.to_sym)
|
|
272
|
+
end
|
|
273
|
+
else
|
|
274
|
+
Apipie.configuration.required_by_default?
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
end
|