swagui 0.1.2 → 0.2.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
  SHA1:
3
- metadata.gz: 0aeb4e82e294ab2acef6f0f905a5d5190240193d
4
- data.tar.gz: 1078ce9042532e87e78ab297122723f8cc77dac8
3
+ metadata.gz: 2ece6a30df668aa10a6f7414e3acb9b533336385
4
+ data.tar.gz: 812dce9d3d9fb20f887928ea8a3205173905412e
5
5
  SHA512:
6
- metadata.gz: 79b53723736615a736208b9773ef6262f612dbd9c61ed860e64ed87c5683e88de13fb3ce06996ec9e4a8175283e48359c323e95f204d137985f54b836f29b237
7
- data.tar.gz: 4ff8496c3a6834ebabf898fe2c217a0cc6041eec362282ee6bf8af5b862ece69c561fdf23356001a257a40833b3ce0506f49d91cd8d26f0203c00166ab39ca67
6
+ metadata.gz: ac9745d126ccea6472f6adb5ac06d879ce0a0d276b6331c7f3a67a473a5f74deac6d6d6302aee290ca842cffb6dc163f3d572d22dca38a376275adcc1e7f843b
7
+ data.tar.gz: 208d92bf9221669eac16e35ef8bb9b268614091e31f828bef6023c4f33b9a58d69f561a9c2a1f7577c95cd4dd490b5a7cc8c58f7379d6f7c87ee48ca9151cd5b
data/Gemfile CHANGED
@@ -2,3 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in swagui.gemspec
4
4
  gemspec
5
+
6
+ gem 'minitest'
7
+ gem 'guard-jruby-minitest', '0.1.8'
8
+ gem 'terminal-notifier-guard'
9
+ gem 'pry'
10
+ gem 'rack-test'
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ interactor :simple
5
+ guard 'jruby-minitest', :spec_paths => ["test"], :all_on_start => false do
6
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
7
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/test_#{m[2]}.rb" }
8
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
9
+ end
10
+
data/README.md CHANGED
@@ -35,6 +35,65 @@ end
35
35
 
36
36
  You will be able to access the [swagger-ui] loaded with your api documentation at `/doc` (your `url` setting) when the server starts.
37
37
 
38
+ ### doc file format
39
+
40
+ `Swagui` supports three types of doc format
41
+
42
+ 1. JSON documents with no extension ([original swagger doc format]), such as `api-docs` and `account`.
43
+ 2. JSON documents with `.json` extension, such as `api-docs.json` and `account.json`.
44
+ 3. YAML documents. This the recommended approach as it is naturally more concise than json and also tries to be more opinionated in the following ways:
45
+ 1. if not provided in `api-docs.yml`, the list of apis under `apis` is automatically populated by the names of all the .yml files in the same directory.
46
+ 2. the `basePath` attribute of each doc, used for `try it out` feature, if unprovided, is automatically provided based on the host application. this assumes the doc is hosted as part of the application.
47
+ 3. to circumvent all the rather complex and tedious syntax for `models`, schema attributes under `apis/operations/parameters` (for request body json schema) and `apis/operations/responseMessages` (for response body json schema), and it will be used to automatically populate the `models` under root.
48
+
49
+ With this approach, swagger api docs are now a lot more concise and readible, let alone having dynamic `basePath`.
50
+
51
+ #### sample api-docs.yml
52
+ ```yaml
53
+ ---
54
+ info:
55
+ title: "a sojourner's api"
56
+ description: "Ut? Amet, et eros nascetur parturient diam scelerisque, egestas, pulvinar sit cum, rhoncus eros vel urna aliquam massa! Turpis purus auctor proin aliquam nunc, nec proin vel enim est, scelerisque! Ac vel integer proin sed in."
57
+ contact: "sojourner@world.net"
58
+ apiVersion: "1.0.0"
59
+ swaggerVersion: "1.2"
60
+ authorizations: {}
61
+
62
+ ```
63
+ #### sample account.yml
64
+ ```yaml
65
+ ---
66
+ produces:
67
+ - application/json
68
+ apis:
69
+ -
70
+ path: /account
71
+ description: create or update a account
72
+ operations:
73
+ -
74
+ method: POST
75
+ summary: create or update an account
76
+ nickname: PostAccount
77
+ parameters:
78
+ -
79
+ name: body
80
+ required: true
81
+ schema: test/doc/schemas/account_post_request_body.schema.json
82
+ paramType: body
83
+ responseMessages:
84
+ -
85
+ code: 200
86
+ message: account creation success
87
+ schema: test/doc/schemas/account_post_creation_success.schema.json
88
+ -
89
+ code: 400
90
+ message: account creation failure
91
+ schema: test/doc/schemas/account_post_creation_failure.schema.json
92
+
93
+ ```
94
+
95
+
96
+
38
97
  ### Command-line utility
39
98
 
40
99
  Sometimes, you only want to see the documentation without starting the entire application. All you need to do is to run the following command in your documentation directory:
@@ -48,7 +107,7 @@ optionally, you can specify the directory name as a command line argument:
48
107
  You will then able to view the [swagger-ui] loaded with your api documentation at `http://localhost:9292`
49
108
 
50
109
  [swagger-ui]: https://github.com/wordnik/swagger-ui
51
-
110
+ [original swagger doc format]: https://github.com/wordnik/swagger-spec/blob/master/fixtures/v1.2/helloworld/static/api-docs
52
111
  ## Contributing
53
112
 
54
113
  1. Fork it ( https://github.com/jackxxu/swagui/fork )
@@ -56,3 +115,4 @@ You will then able to view the [swagger-ui] loaded with your api documentation a
56
115
  3. Commit your changes (`git commit -am 'Add some feature'`)
57
116
  4. Push to the branch (`git push origin my-new-feature`)
58
117
  5. Create a new Pull Request
118
+
data/Rakefile CHANGED
@@ -1,2 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
@@ -1,4 +1,16 @@
1
- require "swagui/version"
2
- require "swagui/app"
3
- require "swagui/asset_handler"
4
- require "swagui/swagger_doc_handler"
1
+ require 'swagui/version'
2
+ require 'swagui/app'
3
+ require 'swagui/asset_handler'
4
+ require 'swagui/swagger_doc_handler'
5
+ require 'swagui/yaml_doc_handler'
6
+ require 'swagui/json_schema_parser'
7
+ require 'swagui/json_schema'
8
+
9
+
10
+ module Swagui
11
+
12
+ def self.file_full_path(relative_path)
13
+ File.expand_path(relative_path, Dir.pwd)
14
+ end
15
+
16
+ end
@@ -4,6 +4,9 @@ module Swagui
4
4
  def initialize(path)
5
5
  @url_regex = Regexp.union(Regexp.new("^\/swagger-ui"), Regexp.new("^#{path}\/?$"))
6
6
  swagger_ui_dir = File.expand_path('../../swagger-ui', File.dirname(__FILE__))
7
+
8
+ raise "swagger ui assets directory #{swagger_ui_dir} does not exist" unless File.directory?(swagger_ui_dir)
9
+
7
10
  @asset_file_server = Rack::File.new(swagger_ui_dir)
8
11
  end
9
12
 
@@ -0,0 +1,38 @@
1
+ module Swagui
2
+ class JsonSchema
3
+
4
+ attr_reader :name
5
+
6
+ def initialize(schema_hash, name)
7
+ @name = name
8
+ @schema_hash = schema_hash
9
+ @nested_objects = []
10
+
11
+ @schema_hash['properties'].each do |pname, pattributes|
12
+ if pattributes['type'] == 'object'
13
+ nested_object_name = "#{@name}-#{pname}"
14
+ @schema_hash['properties'][pname] = {'$ref' => nested_object_name }
15
+ @nested_objects << JsonSchema.new(pattributes, nested_object_name)
16
+ end
17
+ end
18
+ end
19
+
20
+ def properties
21
+ @schema_hash['properties']
22
+ end
23
+
24
+ def models
25
+ all_objects.map do |schema|
26
+ {
27
+ 'id' => schema.name,
28
+ 'properties' => schema.properties
29
+ }
30
+ end
31
+ end
32
+
33
+ def all_objects
34
+ ([self] + @nested_objects.map {|x| x.all_objects}).flatten
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ module Swagui
2
+ class JsonSchemaParser
3
+
4
+ # returns a hash of interlinked models
5
+ # the first one is the root
6
+ def self.parse(file, prefix = "")
7
+ schema_json = JSON.load(File.open(file).read)
8
+ JsonSchema.new(schema_json, prefix)
9
+ end
10
+ end
11
+ end
@@ -2,8 +2,11 @@ module Swagui
2
2
  class SwaggerDocHandler
3
3
  def initialize(path, url)
4
4
  @url_regex = Regexp.new("^#{url}")
5
- app_doc_dir = File.expand_path(path || url, Dir.pwd)
6
- @app_file_server = Rack::File.new(app_doc_dir)
5
+ app_doc_dir = Swagui.file_full_path(path || url)
6
+
7
+ raise "swagger api doc directory #{app_doc_dir} does not exist" unless File.directory?(app_doc_dir)
8
+
9
+ @app_file_server = YAMLDocHandler.new(Rack::File.new(app_doc_dir))
7
10
  end
8
11
 
9
12
  def handles?(env)
@@ -13,26 +16,12 @@ module Swagui
13
16
  def call(env)
14
17
  path = env["PATH_INFO"].gsub(@url_regex, '') # root path renders index.html
15
18
 
16
- response = first_valid_file_response(path) || [404, {"Content-Type"=>"application/json"}, '']
17
-
18
- if response[2].path.end_with?('.yml') # yml response needs to be re=processed.
19
- body = ''
20
- response[2].each {|f| body = YAML::load(f).to_json}
21
- response[2] = [body]
22
- response[1].merge!("Content-Length"=> body.size.to_s)
23
- end
19
+ first_valid_file_response = ['', '.json', '.yml'].map do |ext|
20
+ @app_file_server.call(env.merge('PATH_INFO' => "#{path}#{ext}", 'REQUEST_METHOD' => 'GET'))
21
+ end.find {|res| res[0] == 200 }
24
22
 
25
- response[1].merge!("Content-Type"=>"application/json") # response is always json content
26
-
27
- response
23
+ first_valid_file_response || [404, {"Content-Type"=>"application/json"}, '']
28
24
  end
29
25
 
30
- private
31
-
32
- def first_valid_file_response(path)
33
- ['', '.json', '.yml'].map do |ext|
34
- @app_file_server.call('PATH_INFO' => "#{path}#{ext}", 'REQUEST_METHOD' => 'GET')
35
- end.find {|res| res[0] == 200 }
36
- end
37
26
  end
38
27
  end
@@ -1,3 +1,3 @@
1
1
  module Swagui
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,81 @@
1
+ require 'yaml'
2
+
3
+ module Swagui
4
+ class YAMLDocHandler
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ @app.call(env).tap do |response|
12
+
13
+ response[1].merge!("Content-Type"=>"application/json") # response is always json content
14
+
15
+ if yaml_response?(response) # yml response needs to be re=processed.
16
+ body = []
17
+ response[2].each do |f|
18
+ body << YAML::load(f).tap do |response_hash|
19
+ process_schemas(response_hash)
20
+ process_api_docs_api_listing(response_hash, response[2].path )
21
+ process_base_path(response_hash, response[2].path, env)
22
+ end.to_json
23
+ end
24
+
25
+ response[2] = body
26
+
27
+ response[1].merge!("Content-Length"=> body.first.size.to_s)
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def yaml_response?(response)
35
+ response[2].respond_to?(:path) && response[2].path.end_with?('.yml')
36
+ end
37
+
38
+ def process_schemas(response_hash)
39
+ (response_hash['apis'] || []).each do |api_hash|
40
+ (api_hash['operations'] || []).each do |operations_hash|
41
+ (operations_hash['parameters'] || []).each do |parameters_hash|
42
+ if schema_file = parameters_hash.delete('schema')
43
+ schema_file_path = Swagui.file_full_path(schema_file)
44
+ schema = JsonSchemaParser.parse(schema_file_path, "#{operations_hash['nickname']}-Request")
45
+ schema.models.each {|m| (response_hash['models'] ||= {})[m['id']] = m }
46
+ parameters_hash.merge!('type' => schema.name)
47
+ end
48
+ end
49
+ operations_hash['responseMessages'].each do |response_messages_hash|
50
+ if schema_file = response_messages_hash.delete('schema')
51
+ schema_file_path = Swagui.file_full_path(schema_file)
52
+ schema = JsonSchemaParser.parse(schema_file_path, "#{operations_hash['nickname']}-Response-#{response_messages_hash['message'].gsub(' ', '-')}")
53
+ schema.models.each {|m| (response_hash['models'] ||= {})[m['id']] = m }
54
+ response_messages_hash.merge!('responseModel' => schema.name)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def process_api_docs_api_listing(response_hash, doc_path)
62
+ if doc_path.end_with?('api-docs.yml') && response_hash['models'].nil? # requesting the root
63
+ dir_path = File.dirname(doc_path)
64
+ files = Dir["#{File.dirname(doc_path)}/*.yml"].map {|x| x.gsub(dir_path, '').gsub('.yml', '')}
65
+ files.each do |file|
66
+ unless file == '/api-docs'
67
+ (response_hash['apis'] ||= []) << {'path' => "/..#{file}"}
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def process_base_path(response_hash, doc_path, env)
74
+ if !doc_path.end_with?('api-docs.yml') && response_hash['basePath'].nil?
75
+ request = Rack::Request.new(env)
76
+ response_hash['basePath'] = (request.base_url + request.script_name)
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,15 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ Bundler.require(:default, :test)
4
+
5
+ require 'swagui'
6
+ require 'rack/lobster'
7
+ require 'json'
8
+ class Minitest::Test
9
+ def mounted_app
10
+ Rack::Builder.new do
11
+ use Swagui::App, url: '/doc', path: 'test/doc'
12
+ run Rack::Lobster.new
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class TestJsonSchema < Minitest::Test
4
+
5
+ def setup
6
+ sampole_schema_file = File.expand_path('doc/schemas/account_post_request_body.schema.json', File.dirname(__FILE__))
7
+ @schema_json = JSON.load(File.open(sampole_schema_file).read)
8
+ end
9
+
10
+ def test_objects_method_returns_array
11
+ schema = Swagui::JsonSchema.new(@schema_json, 'PostAccount')
12
+ assert_equal schema.all_objects.size, 2
13
+ end
14
+
15
+ def test_models_methods_return_array_of_hash
16
+ schema = Swagui::JsonSchema.new(@schema_json, 'PostAccount')
17
+ assert_equal schema.models.size, 2
18
+ assert_equal schema.models.map {|x| x['id']}, ['PostAccount', 'PostAccount-contact']
19
+ end
20
+
21
+ end
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class TestJsonSchemaParser < Minitest::Test
4
+
5
+ def test_parse_file
6
+ file_path = File.expand_path('test/doc/schemas/account_post_request_body.schema.json', Dir.pwd)
7
+ result = Swagui::JsonSchemaParser.parse(file_path, 'PostAccount')
8
+ assert_equal result.class, Swagui::JsonSchema
9
+ assert_equal result.name, 'PostAccount'
10
+ assert_equal result.models.size, 2
11
+ assert_equal result.models.first.class, Hash
12
+ end
13
+
14
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class TestSwaggerDocHandler < Minitest::Test
4
+
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ Rack::Lint.new(mounted_app)
9
+ end
10
+
11
+ def test_loading_api_docs_root
12
+ get '/doc/api-docs'
13
+ assert last_response.ok?
14
+ response_json = JSON.parse(last_response.body)
15
+ assert_equal response_json['apis'].size, 1
16
+ assert_equal response_json['apis'].first['path'], '/../account'
17
+ end
18
+
19
+ def test_loading_individual_api_doc
20
+ get '/doc/account'
21
+ assert last_response.ok?
22
+ response_json = JSON.parse(last_response.body)
23
+ assert_equal response_json['apis'].size, 1
24
+ assert response_json['models'].size > 1
25
+ assert_equal response_json['models'].keys.first, 'PostAccount-Request'
26
+ refute response_json['basePath'].nil?
27
+ end
28
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swagui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jack Xu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-30 00:00:00.000000000 Z
11
+ date: 2014-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -62,6 +62,7 @@ extra_rdoc_files: []
62
62
  files:
63
63
  - .gitignore
64
64
  - Gemfile
65
+ - Guardfile
65
66
  - LICENSE.txt
66
67
  - README.md
67
68
  - Rakefile
@@ -69,8 +70,11 @@ files:
69
70
  - lib/swagui.rb
70
71
  - lib/swagui/app.rb
71
72
  - lib/swagui/asset_handler.rb
73
+ - lib/swagui/json_schema.rb
74
+ - lib/swagui/json_schema_parser.rb
72
75
  - lib/swagui/swagger_doc_handler.rb
73
76
  - lib/swagui/version.rb
77
+ - lib/swagui/yaml_doc_handler.rb
74
78
  - swagger-ui/css/reset.css
75
79
  - swagger-ui/css/screen.css
76
80
  - swagger-ui/images/explorer_icons.png
@@ -95,6 +99,10 @@ files:
95
99
  - swagger-ui/swagger-ui.js
96
100
  - swagger-ui/swagger-ui.min.js
97
101
  - swagui.gemspec
102
+ - test/test_helper.rb
103
+ - test/test_json_schema.rb
104
+ - test/test_json_schema_parser.rb
105
+ - test/test_swagger_doc_handler.rb
98
106
  homepage: https://github.com/jackxxu/swagui
99
107
  licenses:
100
108
  - MIT
@@ -119,4 +127,8 @@ rubygems_version: 2.2.2
119
127
  signing_key:
120
128
  specification_version: 4
121
129
  summary: A rack-based swagger-ui middleware and commandline utility.
122
- test_files: []
130
+ test_files:
131
+ - test/test_helper.rb
132
+ - test/test_json_schema.rb
133
+ - test/test_json_schema_parser.rb
134
+ - test/test_swagger_doc_handler.rb