From f8d993356f9d8fa062fe15aea7f24bbb6e830371 Mon Sep 17 00:00:00 2001 From: richie Date: Tue, 23 Aug 2016 09:30:11 -0700 Subject: [PATCH 1/2] Update Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index bab825d..ef7b173 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: swagger_rails (1.0.1.pre.beta3) rack - rails (>= 3.1, <= 5) + rails (>= 3.1, < 5.1) GEM remote: https://rubygems.org/ From c558098c39fc39fce4d2c21182383ac7126cc711 Mon Sep 17 00:00:00 2001 From: richie Date: Tue, 11 Oct 2016 18:31:12 -0700 Subject: [PATCH 2/2] rename to rswag plus major refactor - almost a rewrite --- Gemfile | 37 +- Gemfile.lock | 84 +- README.md | 308 +- README.rdoc | 3 - .../swagger_rails/swagger_ui_controller.rb | 30 - config/routes.rb | 3 - .../custom_ui/custom_ui_generator.rb | 9 - .../custom_ui/files/index.html.erb | 153 - lib/generators/swagger_rails/install/USAGE | 9 - .../install/install_generator.rb | 24 - .../install/templates/swagger_helper.rb | 30 - lib/swagger_rails.rb | 15 - lib/swagger_rails/configuration.rb | 10 - lib/swagger_rails/engine.rb | 15 - lib/swagger_rails/middleware/swagger_json.rb | 35 - lib/swagger_rails/rspec/api_metadata.rb | 46 - lib/swagger_rails/rspec/dsl.rb | 83 - lib/swagger_rails/rspec/formatter.rb | 41 - lib/swagger_rails/test_visitor.rb | 71 - lib/swagger_rails/version.rb | 3 - lib/tasks/swagger_rails_tasks.rake | 14 - {spec/dummy => rswag-api}/.rspec | 0 MIT-LICENSE => rswag-api/MIT-LICENSE | 0 Rakefile => rswag-api/Rakefile | 17 +- {bin => rswag-api/bin}/rails | 2 +- .../lib/generators/rswag/api/install/USAGE | 8 + .../rswag/api/install/install_generator.rb | 18 + .../rswag/api/install/templates/rswag-api.rb | 6 +- rswag-api/lib/rswag/api.rb | 15 + rswag-api/lib/rswag/api/configuration.rb | 12 + rswag-api/lib/rswag/api/engine.rb | 13 + rswag-api/lib/rswag/api/middleware.rb | 37 + rswag-api/lib/rswag/api/version.rb | 5 + rswag-api/rswag-api.gemspec | 18 + .../fixtures/config/initializers}/.gitkeep | 0 .../rswag/api/fixtures/config/routes.rb | 2 + .../rswag/api/install_generator_spec.rb | 27 + .../api/tmp/config/initializers/.gitkeep | 0 .../api/tmp/config/initializers/rswag-api.rb | 6 +- .../generators/rswag/api/tmp/config/routes.rb | 4 + .../spec/rswag/api/fixtures/config/routes.rb | 6 + .../api/fixtures/swagger/v1/swagger.json | 8 + rswag-api/spec/rswag/api/middleware_spec.rb | 82 + .../.keep => rswag-api/spec/spec_helper.rb | 0 rswag-specs/.rspec | 2 + rswag-specs/MIT-LICENSE | 20 + rswag-specs/Rakefile | 27 + .../lib/generators/rswag/specs/install/USAGE | 8 + .../rswag/specs/install/install_generator.rb | 14 + .../install/templates}/swagger_helper.rb | 17 +- rswag-specs/lib/rswag/specs.rb | 22 + .../lib/rswag/specs/example_group_helpers.rb | 80 + .../lib/rswag/specs/example_helpers.rb | 43 + rswag-specs/lib/rswag/specs/railtie.rb | 10 + .../lib/rswag/specs/request_factory.rb | 70 + .../lib/rswag/specs/response_validator.rb | 38 + .../lib/rswag/specs/swagger_formatter.rb | 79 + rswag-specs/lib/rswag/specs/version.rb | 5 + rswag-specs/lib/tasks/rswag-specs_tasks.rake | 18 + rswag-specs/rswag-specs.gemspec | 20 + .../rswag/specs/fixtures/spec/.gitkeep | 0 .../rswag/specs/install_generator_spec.rb | 24 + .../generators/rswag/specs/tmp/spec/.gitkeep | 0 .../rswag/specs/tmp/spec}/swagger_helper.rb | 17 +- .../rswag/specs/example_group_helpers_spec.rb | 143 + .../spec/rswag/specs/example_helpers_spec.rb | 66 + .../spec/rswag/specs/request_factory_spec.rb | 171 + .../rswag/specs/response_validator_spec.rb | 72 + .../rswag/specs/swagger_formatter_spec.rb | 125 + rswag-specs/spec/spec_helper.rb | 7 + rswag-specs/spec/swagger_helper.rb | 2 + rswag-ui/.rspec | 2 + rswag-ui/MIT-LICENSE | 20 + rswag-ui/Rakefile | 27 + .../controllers/rswag/ui/home_controller.rb | 11 + .../app/views/rswag/ui/home}/index.html.erb | 125 +- rswag-ui/bin/rails | 12 + rswag-ui/config/routes.rb | 3 + .../lib/generators/rswag/ui/custom}/USAGE | 4 +- .../rswag/ui/custom/custom_generator.rb | 13 + .../lib/generators/rswag/ui/install/USAGE | 8 + .../rswag/ui/install/install_generator.rb | 18 + .../rswag/ui/install/templates/rswag-ui.rb | 9 + rswag-ui/lib/rswag/ui.rb | 15 + rswag-ui/lib/rswag/ui/configuration.rb | 17 + rswag-ui/lib/rswag/ui/engine.rb | 13 + rswag-ui/lib/rswag/ui/version.rb | 5 + rswag-ui/rswag-ui.gemspec | 18 + .../rswag/ui/custom_generator_spec.rb | 21 + .../ui/fixtures/config/initializers/.gitkeep | 0 .../rswag/ui/fixtures/config/routes.rb | 2 + .../rswag/ui/install_generator_spec.rb | 27 + .../rswag/ui/tmp/config/initializers/.gitkeep | 0 .../ui/tmp/config/initializers/rswag-ui.rb | 9 + .../generators/rswag/ui/tmp/config/routes.rb | 4 + .../log/.keep => rswag-ui/spec/spec_helper.rb | 0 .../components/swagger-ui/css/print.css | 215 +- .../components/swagger-ui/css/reset.css | 0 .../components/swagger-ui/css/screen.css | 279 +- .../components/swagger-ui/css/style.css | 0 .../components/swagger-ui/css/typography.css | 14 + .../swagger-ui/fonts/DroidSans-Bold.ttf | Bin 40513 -> 42480 bytes .../components/swagger-ui/fonts/DroidSans.ttf | Bin 39069 -> 41028 bytes .../components/swagger-ui/images/collapse.gif | Bin 0 -> 69 bytes .../components/swagger-ui/images/expand.gif | Bin 0 -> 73 bytes .../swagger-ui/images/explorer_icons.png | Bin 0 -> 5115 bytes .../swagger-ui/images/favicon-16x16.png | Bin 0 -> 445 bytes .../swagger-ui/images/favicon-32x32.png | Bin 0 -> 1141 bytes .../components/swagger-ui/images/favicon.ico | Bin .../swagger-ui/images/logo_small.png | Bin 0 -> 455 bytes .../swagger-ui/images/pet_store_api.png | Bin 0 -> 631 bytes .../components/swagger-ui/images/throbber.gif | Bin .../swagger-ui/images/wordnik_api.png | Bin 0 -> 670 bytes .../assets/components/swagger-ui/lang/ca.js | 53 + .../assets/components/swagger-ui/lang/en.js | 3 + .../assets/components/swagger-ui/lang/es.js | 1 + .../assets/components/swagger-ui/lang/fr.js | 54 + .../assets/components/swagger-ui/lang/geo.js | 56 + .../assets/components/swagger-ui/lang/it.js | 52 + .../assets/components/swagger-ui/lang/ja.js | 53 + .../components/swagger-ui/lang/ko-kr.js | 53 + .../assets/components/swagger-ui/lang/pl.js | 53 + .../assets/components/swagger-ui/lang/pt.js | 0 .../assets/components/swagger-ui/lang/ru.js | 12 +- .../assets/components/swagger-ui/lang/tr.js | 53 + .../components/swagger-ui/lang/translator.js | 2 +- .../components/swagger-ui/lang/zh-cn.js | 53 + .../components/swagger-ui/lib/backbone-min.js | 0 .../components/swagger-ui/lib/es5-shim.js | 2065 + .../swagger-ui/lib/handlebars-4.0.5.js | 4608 +++ .../swagger-ui/lib/highlight.9.1.0.pack.js | 2 + .../lib/highlight.9.1.0.pack_extended.js | 34 + .../swagger-ui/lib/jquery-1.8.0.min.js | 0 .../swagger-ui/lib/jquery.ba-bbq.min.js | 0 .../swagger-ui/lib/jquery.slideto.min.js | 0 .../swagger-ui/lib/jquery.wiggle.min.js | 0 .../components/swagger-ui/lib/js-yaml.min.js | 3 + .../swagger-ui/lib/jsoneditor.min.js | 11 + .../components/swagger-ui/lib/lodash.min.js | 102 + .../components/swagger-ui/lib/marked.js | 0 .../swagger-ui/lib/object-assign-pollyfill.js | 23 + .../swagger-ui/lib/sanitize-html.min.js | 6 + .../swagger-ui/lib/swagger-oauth.js | 121 +- .../assets/components/swagger-ui/o2c.html | 0 .../components/swagger-ui/swagger-ui.js | 24993 ++++++++++++ .../components/swagger-ui/swagger-ui.min.js | 14 + rswag/MIT-LICENSE | 20 + rswag/lib/generators/rswag/install/USAGE | 10 + .../rswag/install/install_generator.rb | 12 + rswag/lib/rswag.rb | 7 + rswag/lib/rswag/railtie.rb | 4 + rswag/lib/rswag/version.rb | 3 + rswag/rswag.gemspec | 21 + .../rswag/specs/fixtures/config/routes.rb | 2 + .../rswag/specs/fixtures/spec/.gitkeep | 0 .../rswag/specs/install_generator_spec.rb | 33 + .../rswag/specs/tmp/config/routes.rb | 2 + .../generators/rswag/specs/tmp/spec/.gitkeep | 0 run_tests.sh | 35 + spec/dummy/README.rdoc | 28 - .../app/assets/javascripts/application.js | 13 - .../app/assets/stylesheets/application.css | 15 - spec/dummy/app/helpers/application_helper.rb | 2 - spec/dummy/bin/bundle | 3 - spec/dummy/bin/rails | 4 - spec/dummy/bin/rake | 4 - spec/dummy/bin/setup | 29 - spec/dummy/config.ru | 4 - spec/dummy/config/application.rb | 31 - spec/dummy/config/boot.rb | 5 - spec/dummy/config/environment.rb | 5 - spec/dummy/config/environments/development.rb | 38 - spec/dummy/config/environments/production.rb | 76 - spec/dummy/config/environments/test.rb | 42 - .../config/initializers/session_store.rb | 3 - spec/dummy/config/locales/en.yml | 23 - spec/dummy/config/routes.rb | 5 - spec/dummy/config/secrets.yml | 22 - spec/dummy/public/404.html | 67 - spec/dummy/public/422.html | 67 - spec/dummy/public/500.html | 66 - spec/dummy/spec/integration/blogs_spec.rb | 57 - spec/dummy/swagger/v1/swagger.json | 96 - .../test/integration/v1_contract_test.rb | 19 - spec/dummy/test/test_helper.rb | 7 - .../swagger_rails/custom_ui_generator_spec.rb | 19 - .../swagger_rails/fixtures/config/routes.rb | 2 - .../swagger_rails/install_generator_spec.rb | 34 - spec/rails_helper.rb | 64 - spec/spec_helper.rb | 94 - spec/support/formatter_support.rb | 340 - .../middleware/swagger_json_spec.rb | 78 - spec/swagger_rails/rspec/api_metadata_spec.rb | 125 - spec/swagger_rails/rspec/formatter_spec.rb | 139 - spec/swagger_rails/test_visitor_spec.rb | 125 - swagger_rails.gemspec | 23 - test-app/.rspec | 2 + {spec/dummy => test-app}/Rakefile | 3 +- .../app/controllers/application_controller.rb | 0 .../app/controllers/blogs_controller.rb | 2 +- test-app/app/models/.gitkeep | 0 {spec/dummy => test-app}/app/models/blog.rb | 2 - test-app/config.ru | 4 + test-app/config/application.rb | 63 + test-app/config/boot.rb | 10 + {spec/dummy => test-app}/config/database.yml | 0 test-app/config/environment.rb | 5 + test-app/config/environments/development.rb | 31 + test-app/config/environments/test.rb | 35 + .../initializers/backtrace_silencers.rb | 0 .../config/initializers/inflections.rb | 11 +- .../config/initializers/mime_types.rb | 1 + test-app/config/initializers/rswag-api.rb | 14 + test-app/config/initializers/rswag-ui.rb | 9 + .../config/initializers/secret_token.rb | 2 +- test-app/config/initializers/session_store.rb | 8 + .../config/initializers/wrap_parameters.rb | 10 + test-app/config/routes.rb | 10 + test-app/db/development.sqlite3 | Bin 0 -> 20480 bytes .../db/migrate/20160218212104_create_blogs.rb | 0 {spec/dummy => test-app}/db/schema.rb | 0 test-app/db/test.sqlite3 | Bin 0 -> 20480 bytes test-app/log/development.log | 8290 ++++ test-app/log/test.log | 8344 ++++ test-app/public/404.html | 26 + test-app/public/422.html | 26 + test-app/public/500.html | 25 + test-app/public/favicon.ico | 0 test-app/script/rails | 6 + test-app/spec/integration/blogs_spec.rb | 65 + {spec/dummy => test-app}/spec/rails_helper.rb | 16 +- .../spec/rake/rswag_specs_swaggerize_spec.rb | 15 + {spec/dummy => test-app}/spec/spec_helper.rb | 19 +- test-app/spec/swagger_helper.rb | 59 + test-app/swagger/v1/swagger.json | 153 + ...rockets%2F7c306050f061626e0f3950e79621e7e4 | Bin 0 -> 993 bytes ...rockets%2F00050310cc269c318fea8890521c033c | Bin 0 -> 11667 bytes ...rockets%2F7540403ab706922217f46d51cf078d2f | Bin 0 -> 1964 bytes ...rockets%2Fab6f8830a4155458f0924245c5765a3c | Bin 0 -> 130664 bytes ...rockets%2F427498418547e04b26baf60dc45f7922 | Bin 0 -> 47271 bytes ...rockets%2F758833f65c3573a325c9b5c293d38d01 | Bin 0 -> 1370 bytes ...rockets%2F11fb992712bf941fc5444b1081249ab0 | Bin 0 -> 53575 bytes ...rockets%2F5a2393364111291ca6ef1b8c60f261e8 | Bin 0 -> 1671 bytes ...rockets%2F2bd1110ff02ed73884f1e9b559661186 | Bin 0 -> 1672 bytes ...rockets%2F4f47f6c8456b82a92ff54a533567d010 | Bin 0 -> 29069 bytes ...rockets%2Fb5f9f7793895a857b56b9c056e41494d | Bin 0 -> 53256 bytes ...rockets%2F955386f7956a67dd17889fe10ec9193f | Bin 0 -> 160543 bytes ...rockets%2F68c927ba32a73c662b01b349641c9c7b | Bin 0 -> 4743 bytes ...rockets%2Ffcb3a67d2880b0a9415a61c93366691e | Bin 0 -> 521 bytes ...rockets%2Ff2fff22f07d184d50c5e4048e5370393 | Bin 0 -> 521 bytes ...rockets%2F1a44ff59443798bfb51541f22882eab3 | Bin 0 -> 21003 bytes ...rockets%2F00d7be933e59b0847ee792e7e42a5219 | Bin 0 -> 1328 bytes ...rockets%2Fbb7eb4c24b93d341d1040283200a2c6d | Bin 0 -> 1328 bytes ...rockets%2Ffd931ba87c789063c98f549369b0df83 | Bin 0 -> 509 bytes ...rockets%2F64316d413a73d4addac61374a299a5f7 | Bin 0 -> 49778 bytes ...rockets%2F11f5160dd16d1bce2a4838b5e89719b2 | Bin 0 -> 28758 bytes ...rockets%2Fcdc2c396ea6666925ce18c1a225334a8 | Bin 0 -> 130663 bytes ...rockets%2Fbd64bc7457667740f7d4a63c2f28a2a2 | Bin 0 -> 508 bytes ...rockets%2F2a62867b314366f2bc2ebe6c10e7c887 | Bin 0 -> 5076 bytes ...rockets%2Fe2f7cf70954a16a5641a9142f1cd865d | Bin 0 -> 2028 bytes ...rockets%2F1453cb26b51cdf2c7b550360fb0f5d23 | Bin 0 -> 1673 bytes ...rockets%2F3ba0a0bc093c0764a2d1c9a9570e0f89 | Bin 0 -> 93180 bytes ...rockets%2F52922cde7c2a2c5051d7be050018efe7 | Bin 0 -> 12229 bytes ...rockets%2F4d5f0581380207bda4235f633fce2fbd | Bin 0 -> 4743 bytes ...rockets%2Feb7aac183fc50477ae770161df27774a | Bin 0 -> 160212 bytes ...rockets%2F1691608e154bc20d7bdef63e73de47c5 | Bin 0 -> 93180 bytes ...rockets%2F42e9a797c5f0e492bdb23764610ecfe0 | Bin 0 -> 47271 bytes ...rockets%2Fb3abb7b107c259387cdc987229fbe547 | Bin 0 -> 1386 bytes ...rockets%2F5c700ef877e083db25d4996c8dbe414a | Bin 0 -> 1387 bytes ...rockets%2F7cd89d513796f56bb19c3a05daa7f249 | Bin 0 -> 2139992 bytes ...rockets%2F4f64ebb7e1c55e37f601d892d16e79b9 | Bin 0 -> 12554 bytes ...rockets%2Fc489047dc6d75e7ebde47371785b61af | Bin 0 -> 28758 bytes ...rockets%2Fddd71f3676faf7d597112f758eec0479 | Bin 0 -> 2139993 bytes ...rockets%2F5078f84b3f0449a40bef0ac0f6d1c52c | Bin 0 -> 11667 bytes ...rockets%2Fe4a1588aab113dd81f5f3e227477db2e | Bin 0 -> 47583 bytes ...rockets%2F6f3c1168b3aafef0781da6472da0c689 | Bin 0 -> 49779 bytes ...rockets%2F9387eef4ddc01a41fb7386f0002f7ab9 | Bin 0 -> 1049 bytes ...rockets%2F8a78810bd25bbf77cb24c6fe4424c5d7 | Bin 0 -> 446532 bytes ...rockets%2F68cf59942b931ea4643d029ffba6dfd5 | Bin 0 -> 1654 bytes ...rockets%2F0bf174208d2cbe8aad74a7a2c9a19d91 | Bin 0 -> 160212 bytes ...rockets%2F102b13e4fc5fc8eb572f4f3062da5b7f | Bin 0 -> 512 bytes ...rockets%2Fb4a9b5cff3ed835f9028c49cd0e50d00 | Bin 0 -> 130990 bytes ...rockets%2Fd7b1c1d7f4f4046dba224d4d5e6c9c77 | Bin 0 -> 50091 bytes ...rockets%2Fcd96057c71f9d40539be8a3bbfbaa619 | Bin 0 -> 993 bytes ...rockets%2Fe7a3e2b664d989f57a1c82ba67eea1c0 | Bin 0 -> 21003 bytes ...rockets%2Fe227ccc2cf546b7c209f571469f0fdfd | Bin 0 -> 512 bytes ...rockets%2Fa99e88d06c0e4cb3797ef037a7fbc9c5 | Bin 0 -> 93512 bytes ...rockets%2F15cbbf713e1fa1c0acebfe9416f20822 | Bin 0 -> 2140305 bytes ...rockets%2F91ddbbd1eb330ac823df75eeb571e469 | Bin 0 -> 1654 bytes ...rockets%2F488c28f2ccc4b46bf191bfd442b5c8ce | Bin 0 -> 446214 bytes ...rockets%2F2ac4f7f4bdde1a79afeb7783f762664a | Bin 0 -> 1049 bytes ...rockets%2F8ccbedb61902c4252e51d5bcfba792fa | Bin 0 -> 1719 bytes ...rockets%2Fcc50cd4b22c3bdc9f646932129ebcebe | Bin 0 -> 53256 bytes ...rockets%2Fdecdca31df0156ec27e61c68b8f2b46e | Bin 0 -> 12007 bytes ...rockets%2Fdc4cae860ed25cdea928ca3a9d75de33 | Bin 0 -> 12229 bytes ...rockets%2Fc9be6e359dfaada1324e3ddaf3158bef | Bin 0 -> 1329 bytes ...rockets%2Ff9a4ddabcbb1fd40d740c7d1faf82c0b | Bin 0 -> 21327 bytes ...rockets%2F2ec8ea4f9f0fb9b3a1bcb6dadebf7d5e | Bin 0 -> 446214 bytes ...MSHMTIZRZ3arsYIOT39Pk5Sk16LDI8RIOGjE.cache | Bin 0 -> 7419 bytes ...fvy85EBksq352dlZ9kMuSarIVqiMw3joQKzY.cache | 1 + ...NaH1HXKv720Gp91YLSpZGv7i9GqmQvggPUNY.cache | 1 + ...nycK2RhzI2uoY_v73C3L0QhjWGNuZ6xBtlNo.cache | 1 + ...2bv-yK4y2ZFOHCGPJtlo6VHg4cSkBSEYHDnc.cache | 1 + ...pyPZTqeUBjN_KOkuS9l5VIiRX3IFdmc2K5Sw.cache | 2 + ...JfhFi5vQmkns50Q-IHQHCfv-9l9xTEm_-ki0.cache | Bin 0 -> 2656 bytes ...vYUyWCLHxSW-K-YhgS_0a77xfIjxBlSaeuuw.cache | Bin 0 -> 36 bytes ...k_LQ4gwd7maAX6ITqbsTRJrmeU3nd6sUsCTQ.cache | 2 + ...0BX-yZFD_tmTwWvFCcCQFl7w_HpoMC4RPK8Y.cache | 2 + ...eS3C_jl4NZ2lHLSSo1yra6lHGg51Ef6R2JiI.cache | 1 + ...zH8xWTgMO5u_M9O4xicfTAM0PLC_t2qhaUTE.cache | 1 + ...zAnIK51olnaezt4SMKQ5-hYTN821lcQ2MtGs.cache | 1 + ...9te1HnfhsQ5usZOHhfYngDuwf794kbxNYrTM.cache | Bin 0 -> 1931 bytes ...k74NbceIcQqTs8hldUBF8xiTFCYIHXvEsVMg.cache | Bin 0 -> 1767 bytes ...R_vrOJmP0iN4Jew4CA8ps049jy_MIMUqTS2A.cache | Bin 0 -> 27170 bytes ...r8CpVcuoxj0O0424U6kltTpzK_umgjwAHQXE.cache | 1 + ...ZdpYC02ECUCHnFIxlNCPVUGRvwund9Xtewo4.cache | 2 + ...iP4AlsXfqGaxMbr9uHchampTH5U9yw3Bdx2g.cache | Bin 0 -> 20026 bytes ...j8Ax1NfUrPo8lrDvy7qxybes0ND-bLx68j_o.cache | 2 + ...X_X9m7sxibJOCQWM_qwXdc1z0beURtdIcUYQ.cache | Bin 0 -> 1533 bytes ...JXEuX5QZGzLI2s8HnrS_XPxEHIq858Cj3_5Q.cache | Bin 0 -> 3987 bytes ...nHC12M0J4CMcGdwpfxjV0OMbK-SLwKMe4zTo.cache | 3 + ...oQX8tKI44ZUocDieu8rsyi2TkMaDotqvEVQQ.cache | 1 + ...tr6khXijMX_XYY-me6MZIdM1Quv-tUvGbPBI.cache | 2 + ...uhZqq4NFKYvbERnBIuwyL9jmzN26Tqfdjk-Q.cache | Bin 0 -> 2729 bytes ...2iyUwMQO5hPtpThbaoxRuKXE_P87TVWg_IJU.cache | 1 + ...M86p-_JYDWZugzD4ihdbhsjdwKrRaUTF5nyU.cache | Bin 0 -> 7624 bytes ...QZM1UBgnscrldY2nxlLphnE1mMcffFHHpTo4.cache | 3 + ...FLxrR4Q6obLQXmK7SKpCbIbLxzCgEdtuEemQ.cache | 1 + ...0TSLaPDerUJvyj7h_7yua17w3NFWBXrpk4-8.cache | Bin 0 -> 2519 bytes ...0AhiAmy6B9X9riEMt8P3QPQ-9glxclOpq2HU.cache | Bin 0 -> 36064 bytes ...q8OARDftwZNkEkzV0es51BqPQErJmiH9eoLc.cache | 1 + ...3Qq1aRHBH0cBzT1FmNHZlLLzCUQJyADCZqC8.cache | Bin 0 -> 2122 bytes ...lGYNlUB7qvsTYA__8nuwVfFo8V1IRNWBijNk.cache | 3 + ...epDqEGd0GdRVmKJ8vh63hNftsKC-atFsoYLU.cache | 2 + ...H5m9l1rKkqQr28R4_VDnn09MAJSp6XnB2UmE.cache | 2 + ...txotmeXpqOnLn9uBw_HQyQqCJLlaZ7p0ERoE.cache | 1 + ...Kk-djrANYoPQ_IexE0De1b7eMGMrwGRaR7RU.cache | 1 + ...8bQfwYf42hROM24uOYU9cWr_0MfDuOIpNQrY.cache | 1 + ...KbSek1n9vUXAu_TNT1HGZgudxaabCxdZOBy0.cache | Bin 0 -> 2351 bytes ...IS_VJLtS4NwA8sXV9eiY8xJ3oJxmFPXimBA0.cache | 1 + ...tWO5PBTmu9MNUXI5qTfb3MUQv_tvWAXGmmN8.cache | 3 + ...hb9zN13uy5O7PlpQ3qDZyQGgxfZ-7x3bRnVk.cache | 2 + ...cn1o_2r6HmKZaANeHpMkKucZ65gbxTWUz66M.cache | 1 + ...USdwRQvf9UTlga4r3yJ2xre5pmLZweDkJL0s.cache | 1 + ...l8QLGOm0dm4LGZtrzHG2H6VfjZEWx9jdpfOU.cache | 1 + ...dwqs3fe4Fjificw2INCC_B3ClDtFCzNRTM_o.cache | 2 + ...czdRhylYUw4ivW0mdL1MQmhRd_ugcirhvlL0.cache | 3 + ...JYPxDdKROhXzErPbYnB8uQSqmOC0fkpICQC4.cache | 1 + ...waHiv8ljQoILjlLavJlEhGibSd7P6Au3oWiQ.cache | Bin 0 -> 36119 bytes ...0XURQYRJVc4Ajy5CAoTqvPntWnn7QOCvF_N0.cache | Bin 0 -> 27091 bytes ...zTI1lZHoB5L2X_rdNxV1C2vYon1c09HDNOb4.cache | 2 + ...eC8r8KobSJXnHbTjBgbttO62WuyzRehyx-gs.cache | 3 + ...KT7hPeDyKIXIHWon6EQKwd4m1fwnAuJPUyfA.cache | 1 + ...YEtPGUin3F4RDDxXD8x8rcr6sbVxyccrqLCA.cache | Bin 0 -> 36 bytes ...Hm6BCpM-f5XPgRE6zdp_tjRGRaIs_dG0MXEo.cache | Bin 0 -> 7491 bytes ...wcSKapRJAHMTkTt2pBqw5eGwhKw1VykhMkrs.cache | 2 + ...AeATLE3UbtWwTCuvpn0h17Fl-cog1_JiWRqc.cache | Bin 0 -> 33844 bytes ...MtDtxpld-5yjlzuilJmfMjfE1pPoa3yBbPrE.cache | 1 + ...NWTj7PLKkigjJwFzYsC2gHLpnEDq-JBLALOE.cache | 2 + ...7OlI5tNPRlNBpRWGHm6lZ9e5uaNtEcn09vMQ.cache | 3 + ...5jBWDGVHKg_SQHczy6qUzYXUplV1OXTd4F8Q.cache | 1 + ...419Y_Ur63YtpWNYglQZQPRO_OBnVS9xH9NwI.cache | Bin 0 -> 125055 bytes ...ad7Xs9tPWRHlLP9L2EFtqpwhBMaTpLQVG0Ng.cache | Bin 0 -> 6314 bytes ...t2Bv3EBo0ANWpTAro8vFWbLoZpNELZaZ3usk.cache | Bin 0 -> 977 bytes ...h41g5Hxi4qxltv0mHMBPgRPxyRRvLxa89JQ0.cache | 1 + ...BTW8CbaAxzRmHbVERtvIzeEUYcOw0QXeXCQw.cache | 1 + ...Q6qy45zmSv1JsIj4ocITUk7Vy_ph8WVNczjU.cache | Bin 0 -> 1541 bytes ...wkURjDq2tV-yG53zfh2fYRGQ4j7HFrjWm3c4.cache | 1 + ...FvzGuTtlds_v0iarosDMcYisn4yM7w2qa91Q.cache | Bin 0 -> 2252 bytes ...juBdoIF9K53GJk9D-gB5u2d6YtS_IBNp3PJs.cache | Bin 0 -> 1889 bytes ...5qNWlMG3rY6YGQJiGihk7SAJy9hyg0vNtZKA.cache | 1 + ...SJc9XxwFPBKZW8p8Uc3P1Bvq2qxkv-aZ4tkU.cache | Bin 0 -> 6765 bytes ...TWNYYLONS6fbAM0uhPIRRWV8o4Sd85OX8BUk.cache | 3 + ...757q-Wy3Be-qvM7zjMZc4ESkr1yRDRlsdRTE.cache | 1 + ...bnB0VZhIVa_aNzEJWCUbeet84vndtfNuKAkU.cache | 1 + ...vliAqx37gkKdBH1lr9K1jE4O41gwhFEhgwfM.cache | 3 + ...ld7cxuqGMPZwOvPqfOQrOVheg_GDMhkpH9yE.cache | 1 + ...nssM_Y8Dkp3zEEWWATOJVMNj_pGeD8Rv34cc.cache | 3 + ...GJPFGIw_N1o-D5la8ZAgH90hVoio8bhHKl48.cache | Bin 0 -> 2152 bytes ...PHItNLaVZGbP0Jix7jeECwtZ7wpj-lqoNAZA.cache | 3 + ...MwrHIXKlzNRat74NSBENLLB4ErCouNjViheA.cache | Bin 0 -> 7706 bytes ..._I8I2xiUNtlP_OzboarE3oXrbQCD_H65lTnw.cache | Bin 0 -> 36 bytes ...kUg3TKffUlHdwJBAD3C4kPxKzuBCI32KHeck.cache | 1 + ...goTpugHi5lRn274P8ga74AznZma2x9SE7gnU.cache | 1 + ...SeSitt1zU53EKOl1v6vx8B4mqgUmw3u8z7xM.cache | 1 + ...MEUDgTapaeCAlokGoXQw5Gw9TZ7HOXo6wK8s.cache | 3 + ...pQNDZzFw_jyeQphmfPPLNgGykUzp4MHJUMsk.cache | 3 + ...1TIvOun3lZYFrvxDJx2znvH3ml-yzwrrwC5o.cache | 1 + ...Re4k91_hCoUilorWyIRBL-LE8Ep30ueKDqEU.cache | 1 + ...Zq7yjR7D2huDarYAO-2nIoHiVc-DL6ptR454.cache | 2 + ...7tbbr1cyL5HPznumqNv0DrlnldsosP6-jEso.cache | 1 + ...ru2cPQZ3FrxJZKVk5W9q-3LZwfCKlGrg4kLs.cache | 3 + ...d0lW0bwaUQWyw7iznP9KxQdk2HkDIzc2H8Sw.cache | Bin 0 -> 4064 bytes ...PIe5uyAXrk5UGy3MAvWuS4vy3oqg94VuOuew.cache | 1 + ...MniLw7ja2OCF_nwbTy8OcudWnthCudwmzjFk.cache | 3 + ...TLrvctplyB-3jZX1xE4FnLxIVFQlATnKxpGI.cache | 1 + ...-EW5ckTa4WPtlIsZQrmJhSvlqq0wphCdquo0.cache | 2 + ...WUXcK6aEG7wPPHJkkX8D-TscozIMmwk_-pIU.cache | 1 + ...L-lYIa3kPnwi8hwRitZldAsJlF0GA1yLPW2I.cache | Bin 0 -> 33776 bytes ...Tmic8O1ULEIytPdC-s2a3W-ome5_yPV2THH4.cache | Bin 0 -> 2186 bytes ...-rhYeNNXrEFNtCCkPxYPn-11MacKVjd9lp9M.cache | 1 + ..._41A5QGfXDXyFVCVx5sLeRS6Kw9x70_y6Ciw.cache | Bin 0 -> 6369 bytes ...z5C2Z4nD_ndnTJ_bI1wmnXn70nNsATLfA5_E.cache | Bin 0 -> 125135 bytes ...v7pmnvvGN0ylShdhibaAC3VFMkyH26JkOJSk.cache | 3 + ...zFQqClBLCq2N85DiTd8xLl12RG8uRfRmKLbQ.cache | Bin 0 -> 19954 bytes ...iNEJJB5c5CAcppl7c-Q5tL7E4X7coarmN9_k.cache | Bin 0 -> 5609 bytes ...l3_UESGZifD7F0nCUxZywqwEnsTJBctqkATc.cache | 1 + ...rmwWIcESwk6xMcpxSGCg87TUndiE3yUaWXyw.cache | 1 + ...8vx5YvHrGdzFUZadOj7gYU2ob6bS66uLMRBA.cache | Bin 0 -> 5688 bytes ...5nyzyxZFoh0ExQmRNrjD6c0q3TlNbNuAl5zQ.cache | 2 + ...oC-jGuXKZkjSBH5vzcYw1YALeD0ebws0Z6-k.cache | 5 + ...u7xA6bpMnHc--ipxfZMu4yCJMiU4NKL_mlos.cache | 1 + ...4ipJ3xBPmLHwSWhpt7OhY3ufuFLvqvLrv_uI.cache | 1 + ...1sqUaFmyyvTgBAQJE2c5z4Y1LkFBOJ6Ow2K0.cache | 1 + ...6Av73DaWS3YGck4xFDX4jGDdIsE-7TQNwr_Q.cache | Bin 0 -> 6696 bytes ...krNQFj3RpAXgANR38tlmtCoE3gvFs3cJSWB0.cache | 1 + ...hQy7_Eg7Pox07YjNO2E8PDOB3c5jjJXUnRK4.cache | 2 + ...rYZIRqTcXxAvcpDIoCkGwydv2oRVHOwX-b9c.cache | 1 + ...RVfrWL6qBhVL3Dkozr_6vlMGDfKtbmZ8MrXk.cache | 3 + ...x8m75xdzgrkhA8vhQmPGk2qaGqkIDjCQ7Qdc.cache | 3 + ...ifvGE1h3yL9PP3ks7pqk05zlHwDpIn9douXI.cache | 2 + ...as2MSD9kB0tLZh4zQacG-wNeuQdHBnMGvC1s.cache | 1 + ...zCD1r_zS3DMMX4u_4_xrNCHXWPrAsCWnFVdc.cache | 3 + ...TNdbHY9GTEtSktihb--UacCef319epJGucmw.cache | 3 + ...HFGGCDq9uOpbefot50KhxX4owOJqkUSdTl8E.cache | 2 + ...QXRtsMeotM9wkfvtXQwFzlnlGroXMdfRmKKk.cache | 1 + ...gM6Thu0ODRnQo3coGAM5TAyrDP9khQasIUPI.cache | 1 + ...MPIfwnpouD1N2jm0PY7NOcW5jEhd-TxvTo6k.cache | Bin 0 -> 1797 bytes ...846xN4WfJnzOAflrLQfid6sBwZQQu_YX72Wk.cache | 1 + ...sGDYixTRXfmGaAERQvEVYgJ8bntrK1RYngeQ.cache | 1 + ...pCWb1cV0rm6TFeLPYpdoNPq8rH_X5X-rMZXE.cache | 1 + ...3pWsAxTzs-zMe7TRrlu644MDeNAgVTIIrOYE.cache | 1 + .../components/swagger-ui/css/typography.css | 26 - .../fonts/droid-sans-v6-latin-700.eot | Bin 22922 -> 0 bytes .../fonts/droid-sans-v6-latin-700.svg | 411 - .../fonts/droid-sans-v6-latin-700.woff | Bin 25992 -> 0 bytes .../fonts/droid-sans-v6-latin-700.woff2 | Bin 11480 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.eot | Bin 22008 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.svg | 403 - .../fonts/droid-sans-v6-latin-regular.woff | Bin 24868 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.woff2 | Bin 11304 -> 0 bytes .../swagger-ui/images/explorer_icons.png | Bin 5763 -> 0 bytes .../swagger-ui/images/favicon-16x16.png | Bin 645 -> 0 bytes .../swagger-ui/images/favicon-32x32.png | Bin 1654 -> 0 bytes .../swagger-ui/images/logo_small.png | Bin 770 -> 0 bytes .../swagger-ui/images/pet_store_api.png | Bin 824 -> 0 bytes .../swagger-ui/images/wordnik_api.png | Bin 980 -> 0 bytes .../swagger-ui/lib/handlebars-2.0.0.js | 28 - .../swagger-ui/lib/highlight.7.3.pack.js | 1 - .../swagger-ui/lib/underscore-min.js | 6 - .../swagger-ui/lib/underscore-min.map | 1 - .../components/swagger-ui/swagger-ui.js | 32187 ---------------- .../components/swagger-ui/swagger-ui.min.js | 14 - 453 files changed, 52410 insertions(+), 35793 deletions(-) delete mode 100644 README.rdoc delete mode 100644 app/controllers/swagger_rails/swagger_ui_controller.rb delete mode 100644 config/routes.rb delete mode 100644 lib/generators/swagger_rails/custom_ui/custom_ui_generator.rb delete mode 100644 lib/generators/swagger_rails/custom_ui/files/index.html.erb delete mode 100644 lib/generators/swagger_rails/install/USAGE delete mode 100644 lib/generators/swagger_rails/install/install_generator.rb delete mode 100644 lib/generators/swagger_rails/install/templates/swagger_helper.rb delete mode 100644 lib/swagger_rails.rb delete mode 100644 lib/swagger_rails/configuration.rb delete mode 100644 lib/swagger_rails/engine.rb delete mode 100644 lib/swagger_rails/middleware/swagger_json.rb delete mode 100644 lib/swagger_rails/rspec/api_metadata.rb delete mode 100644 lib/swagger_rails/rspec/dsl.rb delete mode 100644 lib/swagger_rails/rspec/formatter.rb delete mode 100644 lib/swagger_rails/test_visitor.rb delete mode 100644 lib/swagger_rails/version.rb delete mode 100644 lib/tasks/swagger_rails_tasks.rake rename {spec/dummy => rswag-api}/.rspec (100%) rename MIT-LICENSE => rswag-api/MIT-LICENSE (100%) rename Rakefile => rswag-api/Rakefile (66%) rename {bin => rswag-api/bin}/rails (84%) create mode 100644 rswag-api/lib/generators/rswag/api/install/USAGE create mode 100644 rswag-api/lib/generators/rswag/api/install/install_generator.rb rename lib/generators/swagger_rails/install/templates/swagger_rails.rb => rswag-api/lib/generators/rswag/api/install/templates/rswag-api.rb (74%) create mode 100644 rswag-api/lib/rswag/api.rb create mode 100644 rswag-api/lib/rswag/api/configuration.rb create mode 100644 rswag-api/lib/rswag/api/engine.rb create mode 100644 rswag-api/lib/rswag/api/middleware.rb create mode 100644 rswag-api/lib/rswag/api/version.rb create mode 100644 rswag-api/rswag-api.gemspec rename {spec/generators/swagger_rails/fixtures/spec => rswag-api/spec/generators/rswag/api/fixtures/config/initializers}/.gitkeep (100%) create mode 100644 rswag-api/spec/generators/rswag/api/fixtures/config/routes.rb create mode 100644 rswag-api/spec/generators/rswag/api/install_generator_spec.rb rename spec/dummy/app/assets/images/.keep => rswag-api/spec/generators/rswag/api/tmp/config/initializers/.gitkeep (100%) rename spec/dummy/config/initializers/swagger_rails.rb => rswag-api/spec/generators/rswag/api/tmp/config/initializers/rswag-api.rb (74%) create mode 100644 rswag-api/spec/generators/rswag/api/tmp/config/routes.rb create mode 100644 rswag-api/spec/rswag/api/fixtures/config/routes.rb create mode 100644 rswag-api/spec/rswag/api/fixtures/swagger/v1/swagger.json create mode 100644 rswag-api/spec/rswag/api/middleware_spec.rb rename spec/dummy/app/controllers/concerns/.keep => rswag-api/spec/spec_helper.rb (100%) create mode 100644 rswag-specs/.rspec create mode 100644 rswag-specs/MIT-LICENSE create mode 100644 rswag-specs/Rakefile create mode 100644 rswag-specs/lib/generators/rswag/specs/install/USAGE create mode 100644 rswag-specs/lib/generators/rswag/specs/install/install_generator.rb rename {spec/dummy/spec => rswag-specs/lib/generators/rswag/specs/install/templates}/swagger_helper.rb (55%) create mode 100644 rswag-specs/lib/rswag/specs.rb create mode 100644 rswag-specs/lib/rswag/specs/example_group_helpers.rb create mode 100644 rswag-specs/lib/rswag/specs/example_helpers.rb create mode 100644 rswag-specs/lib/rswag/specs/railtie.rb create mode 100644 rswag-specs/lib/rswag/specs/request_factory.rb create mode 100644 rswag-specs/lib/rswag/specs/response_validator.rb create mode 100644 rswag-specs/lib/rswag/specs/swagger_formatter.rb create mode 100644 rswag-specs/lib/rswag/specs/version.rb create mode 100644 rswag-specs/lib/tasks/rswag-specs_tasks.rake create mode 100644 rswag-specs/rswag-specs.gemspec rename spec/dummy/app/mailers/.keep => rswag-specs/spec/generators/rswag/specs/fixtures/spec/.gitkeep (100%) create mode 100644 rswag-specs/spec/generators/rswag/specs/install_generator_spec.rb rename spec/dummy/app/models/.keep => rswag-specs/spec/generators/rswag/specs/tmp/spec/.gitkeep (100%) rename {spec => rswag-specs/spec/generators/rswag/specs/tmp/spec}/swagger_helper.rb (55%) create mode 100644 rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb create mode 100644 rswag-specs/spec/rswag/specs/example_helpers_spec.rb create mode 100644 rswag-specs/spec/rswag/specs/request_factory_spec.rb create mode 100644 rswag-specs/spec/rswag/specs/response_validator_spec.rb create mode 100644 rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb create mode 100644 rswag-specs/spec/spec_helper.rb create mode 100644 rswag-specs/spec/swagger_helper.rb create mode 100644 rswag-ui/.rspec create mode 100644 rswag-ui/MIT-LICENSE create mode 100644 rswag-ui/Rakefile create mode 100644 rswag-ui/app/controllers/rswag/ui/home_controller.rb rename {app/views/swagger_rails/swagger_ui => rswag-ui/app/views/rswag/ui/home}/index.html.erb (52%) create mode 100755 rswag-ui/bin/rails create mode 100644 rswag-ui/config/routes.rb rename {lib/generators/swagger_rails/custom_ui => rswag-ui/lib/generators/rswag/ui/custom}/USAGE (54%) create mode 100644 rswag-ui/lib/generators/rswag/ui/custom/custom_generator.rb create mode 100644 rswag-ui/lib/generators/rswag/ui/install/USAGE create mode 100644 rswag-ui/lib/generators/rswag/ui/install/install_generator.rb create mode 100644 rswag-ui/lib/generators/rswag/ui/install/templates/rswag-ui.rb create mode 100644 rswag-ui/lib/rswag/ui.rb create mode 100644 rswag-ui/lib/rswag/ui/configuration.rb create mode 100644 rswag-ui/lib/rswag/ui/engine.rb create mode 100644 rswag-ui/lib/rswag/ui/version.rb create mode 100644 rswag-ui/rswag-ui.gemspec create mode 100644 rswag-ui/spec/generators/rswag/ui/custom_generator_spec.rb rename spec/dummy/app/models/concerns/.keep => rswag-ui/spec/generators/rswag/ui/fixtures/config/initializers/.gitkeep (100%) create mode 100644 rswag-ui/spec/generators/rswag/ui/fixtures/config/routes.rb create mode 100644 rswag-ui/spec/generators/rswag/ui/install_generator_spec.rb rename spec/dummy/lib/assets/.keep => rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/.gitkeep (100%) create mode 100644 rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/rswag-ui.rb create mode 100644 rswag-ui/spec/generators/rswag/ui/tmp/config/routes.rb rename spec/dummy/log/.keep => rswag-ui/spec/spec_helper.rb (100%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/css/print.css (88%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/css/reset.css (100%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/css/screen.css (87%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/css/style.css (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/css/typography.css rename vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.ttf => rswag-ui/vendor/assets/components/swagger-ui/fonts/DroidSans-Bold.ttf (86%) rename vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf => rswag-ui/vendor/assets/components/swagger-ui/fonts/DroidSans.ttf (86%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/images/collapse.gif create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/images/expand.gif create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/images/explorer_icons.png create mode 100755 rswag-ui/vendor/assets/components/swagger-ui/images/favicon-16x16.png create mode 100755 rswag-ui/vendor/assets/components/swagger-ui/images/favicon-32x32.png rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/images/favicon.ico (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/images/logo_small.png create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/images/pet_store_api.png rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/images/throbber.gif (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/images/wordnik_api.png create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/ca.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lang/en.js (92%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lang/es.js (98%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/fr.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/geo.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/it.js create mode 100755 rswag-ui/vendor/assets/components/swagger-ui/lang/ja.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/ko-kr.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/pl.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lang/pt.js (100%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lang/ru.js (78%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/tr.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lang/translator.js (95%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lang/zh-cn.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/backbone-min.js (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/es5-shim.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/handlebars-4.0.5.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/highlight.9.1.0.pack.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/highlight.9.1.0.pack_extended.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/jquery-1.8.0.min.js (100%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/jquery.ba-bbq.min.js (100%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/jquery.slideto.min.js (100%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/jquery.wiggle.min.js (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/js-yaml.min.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/jsoneditor.min.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/lodash.min.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/marked.js (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/object-assign-pollyfill.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/lib/sanitize-html.min.js rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/lib/swagger-oauth.js (71%) rename {vendor => rswag-ui/vendor}/assets/components/swagger-ui/o2c.html (100%) create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/swagger-ui.js create mode 100644 rswag-ui/vendor/assets/components/swagger-ui/swagger-ui.min.js create mode 100644 rswag/MIT-LICENSE create mode 100644 rswag/lib/generators/rswag/install/USAGE create mode 100644 rswag/lib/generators/rswag/install/install_generator.rb create mode 100644 rswag/lib/rswag.rb create mode 100644 rswag/lib/rswag/railtie.rb create mode 100644 rswag/lib/rswag/version.rb create mode 100644 rswag/rswag.gemspec create mode 100644 rswag/spec/generators/rswag/specs/fixtures/config/routes.rb rename spec/dummy/public/favicon.ico => rswag/spec/generators/rswag/specs/fixtures/spec/.gitkeep (100%) create mode 100644 rswag/spec/generators/rswag/specs/install_generator_spec.rb create mode 100644 rswag/spec/generators/rswag/specs/tmp/config/routes.rb create mode 100644 rswag/spec/generators/rswag/specs/tmp/spec/.gitkeep create mode 100755 run_tests.sh delete mode 100644 spec/dummy/README.rdoc delete mode 100644 spec/dummy/app/assets/javascripts/application.js delete mode 100644 spec/dummy/app/assets/stylesheets/application.css delete mode 100644 spec/dummy/app/helpers/application_helper.rb delete mode 100755 spec/dummy/bin/bundle delete mode 100755 spec/dummy/bin/rails delete mode 100755 spec/dummy/bin/rake delete mode 100755 spec/dummy/bin/setup delete mode 100644 spec/dummy/config.ru delete mode 100644 spec/dummy/config/application.rb delete mode 100644 spec/dummy/config/boot.rb delete mode 100644 spec/dummy/config/environment.rb delete mode 100644 spec/dummy/config/environments/development.rb delete mode 100644 spec/dummy/config/environments/production.rb delete mode 100644 spec/dummy/config/environments/test.rb delete mode 100644 spec/dummy/config/initializers/session_store.rb delete mode 100644 spec/dummy/config/locales/en.yml delete mode 100644 spec/dummy/config/routes.rb delete mode 100644 spec/dummy/config/secrets.yml delete mode 100644 spec/dummy/public/404.html delete mode 100644 spec/dummy/public/422.html delete mode 100644 spec/dummy/public/500.html delete mode 100644 spec/dummy/spec/integration/blogs_spec.rb delete mode 100644 spec/dummy/swagger/v1/swagger.json delete mode 100644 spec/dummy/test/integration/v1_contract_test.rb delete mode 100644 spec/dummy/test/test_helper.rb delete mode 100644 spec/generators/swagger_rails/custom_ui_generator_spec.rb delete mode 100644 spec/generators/swagger_rails/fixtures/config/routes.rb delete mode 100644 spec/generators/swagger_rails/install_generator_spec.rb delete mode 100644 spec/rails_helper.rb delete mode 100644 spec/spec_helper.rb delete mode 100644 spec/support/formatter_support.rb delete mode 100644 spec/swagger_rails/middleware/swagger_json_spec.rb delete mode 100644 spec/swagger_rails/rspec/api_metadata_spec.rb delete mode 100644 spec/swagger_rails/rspec/formatter_spec.rb delete mode 100644 spec/swagger_rails/test_visitor_spec.rb delete mode 100644 swagger_rails.gemspec create mode 100644 test-app/.rspec rename {spec/dummy => test-app}/Rakefile (80%) rename {spec/dummy => test-app}/app/controllers/application_controller.rb (100%) rename {spec/dummy => test-app}/app/controllers/blogs_controller.rb (84%) create mode 100644 test-app/app/models/.gitkeep rename {spec/dummy => test-app}/app/models/blog.rb (82%) create mode 100644 test-app/config.ru create mode 100644 test-app/config/application.rb create mode 100644 test-app/config/boot.rb rename {spec/dummy => test-app}/config/database.yml (100%) create mode 100644 test-app/config/environment.rb create mode 100644 test-app/config/environments/development.rb create mode 100644 test-app/config/environments/test.rb rename {spec/dummy => test-app}/config/initializers/backtrace_silencers.rb (100%) rename {spec/dummy => test-app}/config/initializers/inflections.rb (50%) rename {spec/dummy => test-app}/config/initializers/mime_types.rb (76%) create mode 100644 test-app/config/initializers/rswag-api.rb create mode 100644 test-app/config/initializers/rswag-ui.rb rename {spec/dummy => test-app}/config/initializers/secret_token.rb (65%) create mode 100644 test-app/config/initializers/session_store.rb create mode 100644 test-app/config/initializers/wrap_parameters.rb create mode 100644 test-app/config/routes.rb create mode 100644 test-app/db/development.sqlite3 rename {spec/dummy => test-app}/db/migrate/20160218212104_create_blogs.rb (100%) rename {spec/dummy => test-app}/db/schema.rb (100%) create mode 100644 test-app/db/test.sqlite3 create mode 100644 test-app/log/development.log create mode 100644 test-app/log/test.log create mode 100644 test-app/public/404.html create mode 100644 test-app/public/422.html create mode 100644 test-app/public/500.html create mode 100644 test-app/public/favicon.ico create mode 100755 test-app/script/rails create mode 100644 test-app/spec/integration/blogs_spec.rb rename {spec/dummy => test-app}/spec/rails_helper.rb (87%) create mode 100644 test-app/spec/rake/rswag_specs_swaggerize_spec.rb rename {spec/dummy => test-app}/spec/spec_helper.rb (83%) create mode 100644 test-app/spec/swagger_helper.rb create mode 100644 test-app/swagger/v1/swagger.json create mode 100644 test-app/tmp/cache/assets/C4E/AF0/sprockets%2F7c306050f061626e0f3950e79621e7e4 create mode 100644 test-app/tmp/cache/assets/C64/190/sprockets%2F00050310cc269c318fea8890521c033c create mode 100644 test-app/tmp/cache/assets/C78/AA0/sprockets%2F7540403ab706922217f46d51cf078d2f create mode 100644 test-app/tmp/cache/assets/C80/370/sprockets%2Fab6f8830a4155458f0924245c5765a3c create mode 100644 test-app/tmp/cache/assets/C87/590/sprockets%2F427498418547e04b26baf60dc45f7922 create mode 100644 test-app/tmp/cache/assets/C87/8C0/sprockets%2F758833f65c3573a325c9b5c293d38d01 create mode 100644 test-app/tmp/cache/assets/CA2/360/sprockets%2F11fb992712bf941fc5444b1081249ab0 create mode 100644 test-app/tmp/cache/assets/CA4/590/sprockets%2F5a2393364111291ca6ef1b8c60f261e8 create mode 100644 test-app/tmp/cache/assets/CB2/210/sprockets%2F2bd1110ff02ed73884f1e9b559661186 create mode 100644 test-app/tmp/cache/assets/CB7/300/sprockets%2F4f47f6c8456b82a92ff54a533567d010 create mode 100644 test-app/tmp/cache/assets/CD2/CC0/sprockets%2Fb5f9f7793895a857b56b9c056e41494d create mode 100644 test-app/tmp/cache/assets/CD8/4F0/sprockets%2F955386f7956a67dd17889fe10ec9193f create mode 100644 test-app/tmp/cache/assets/CDB/B00/sprockets%2F68c927ba32a73c662b01b349641c9c7b create mode 100644 test-app/tmp/cache/assets/CDF/B60/sprockets%2Ffcb3a67d2880b0a9415a61c93366691e create mode 100644 test-app/tmp/cache/assets/CE1/870/sprockets%2Ff2fff22f07d184d50c5e4048e5370393 create mode 100644 test-app/tmp/cache/assets/CE4/180/sprockets%2F1a44ff59443798bfb51541f22882eab3 create mode 100644 test-app/tmp/cache/assets/CEA/580/sprockets%2F00d7be933e59b0847ee792e7e42a5219 create mode 100644 test-app/tmp/cache/assets/CED/8A0/sprockets%2Fbb7eb4c24b93d341d1040283200a2c6d create mode 100644 test-app/tmp/cache/assets/CFE/FF0/sprockets%2Ffd931ba87c789063c98f549369b0df83 create mode 100644 test-app/tmp/cache/assets/D09/E50/sprockets%2F64316d413a73d4addac61374a299a5f7 create mode 100644 test-app/tmp/cache/assets/D0A/4A0/sprockets%2F11f5160dd16d1bce2a4838b5e89719b2 create mode 100644 test-app/tmp/cache/assets/D0D/6B0/sprockets%2Fcdc2c396ea6666925ce18c1a225334a8 create mode 100644 test-app/tmp/cache/assets/D10/090/sprockets%2Fbd64bc7457667740f7d4a63c2f28a2a2 create mode 100644 test-app/tmp/cache/assets/D10/AF0/sprockets%2F2a62867b314366f2bc2ebe6c10e7c887 create mode 100644 test-app/tmp/cache/assets/D11/D90/sprockets%2Fe2f7cf70954a16a5641a9142f1cd865d create mode 100644 test-app/tmp/cache/assets/D28/F90/sprockets%2F1453cb26b51cdf2c7b550360fb0f5d23 create mode 100644 test-app/tmp/cache/assets/D2E/F40/sprockets%2F3ba0a0bc093c0764a2d1c9a9570e0f89 create mode 100644 test-app/tmp/cache/assets/D30/160/sprockets%2F52922cde7c2a2c5051d7be050018efe7 create mode 100644 test-app/tmp/cache/assets/D34/D00/sprockets%2F4d5f0581380207bda4235f633fce2fbd create mode 100644 test-app/tmp/cache/assets/D3D/7B0/sprockets%2Feb7aac183fc50477ae770161df27774a create mode 100644 test-app/tmp/cache/assets/D41/BD0/sprockets%2F1691608e154bc20d7bdef63e73de47c5 create mode 100644 test-app/tmp/cache/assets/D42/5F0/sprockets%2F42e9a797c5f0e492bdb23764610ecfe0 create mode 100644 test-app/tmp/cache/assets/D48/8E0/sprockets%2Fb3abb7b107c259387cdc987229fbe547 create mode 100644 test-app/tmp/cache/assets/D49/540/sprockets%2F5c700ef877e083db25d4996c8dbe414a create mode 100644 test-app/tmp/cache/assets/D4B/370/sprockets%2F7cd89d513796f56bb19c3a05daa7f249 create mode 100644 test-app/tmp/cache/assets/D51/0A0/sprockets%2F4f64ebb7e1c55e37f601d892d16e79b9 create mode 100644 test-app/tmp/cache/assets/D52/B50/sprockets%2Fc489047dc6d75e7ebde47371785b61af create mode 100644 test-app/tmp/cache/assets/D58/B00/sprockets%2Fddd71f3676faf7d597112f758eec0479 create mode 100644 test-app/tmp/cache/assets/D62/150/sprockets%2F5078f84b3f0449a40bef0ac0f6d1c52c create mode 100644 test-app/tmp/cache/assets/D69/420/sprockets%2Fe4a1588aab113dd81f5f3e227477db2e create mode 100644 test-app/tmp/cache/assets/D6D/070/sprockets%2F6f3c1168b3aafef0781da6472da0c689 create mode 100644 test-app/tmp/cache/assets/D6E/FA0/sprockets%2F9387eef4ddc01a41fb7386f0002f7ab9 create mode 100644 test-app/tmp/cache/assets/D72/640/sprockets%2F8a78810bd25bbf77cb24c6fe4424c5d7 create mode 100644 test-app/tmp/cache/assets/D7D/B00/sprockets%2F68cf59942b931ea4643d029ffba6dfd5 create mode 100644 test-app/tmp/cache/assets/D93/240/sprockets%2F0bf174208d2cbe8aad74a7a2c9a19d91 create mode 100644 test-app/tmp/cache/assets/D96/9C0/sprockets%2F102b13e4fc5fc8eb572f4f3062da5b7f create mode 100644 test-app/tmp/cache/assets/D9C/F40/sprockets%2Fb4a9b5cff3ed835f9028c49cd0e50d00 create mode 100644 test-app/tmp/cache/assets/D9F/6B0/sprockets%2Fd7b1c1d7f4f4046dba224d4d5e6c9c77 create mode 100644 test-app/tmp/cache/assets/DA2/B20/sprockets%2Fcd96057c71f9d40539be8a3bbfbaa619 create mode 100644 test-app/tmp/cache/assets/DA3/690/sprockets%2Fe7a3e2b664d989f57a1c82ba67eea1c0 create mode 100644 test-app/tmp/cache/assets/DA6/3D0/sprockets%2Fe227ccc2cf546b7c209f571469f0fdfd create mode 100644 test-app/tmp/cache/assets/DB2/820/sprockets%2Fa99e88d06c0e4cb3797ef037a7fbc9c5 create mode 100644 test-app/tmp/cache/assets/DB9/820/sprockets%2F15cbbf713e1fa1c0acebfe9416f20822 create mode 100644 test-app/tmp/cache/assets/DCC/FC0/sprockets%2F91ddbbd1eb330ac823df75eeb571e469 create mode 100644 test-app/tmp/cache/assets/DD1/6A0/sprockets%2F488c28f2ccc4b46bf191bfd442b5c8ce create mode 100644 test-app/tmp/cache/assets/DDB/FB0/sprockets%2F2ac4f7f4bdde1a79afeb7783f762664a create mode 100644 test-app/tmp/cache/assets/DF4/5D0/sprockets%2F8ccbedb61902c4252e51d5bcfba792fa create mode 100644 test-app/tmp/cache/assets/DF6/9A0/sprockets%2Fcc50cd4b22c3bdc9f646932129ebcebe create mode 100644 test-app/tmp/cache/assets/DFB/100/sprockets%2Fdecdca31df0156ec27e61c68b8f2b46e create mode 100644 test-app/tmp/cache/assets/E2E/9A0/sprockets%2Fdc4cae860ed25cdea928ca3a9d75de33 create mode 100644 test-app/tmp/cache/assets/E5B/060/sprockets%2Fc9be6e359dfaada1324e3ddaf3158bef create mode 100644 test-app/tmp/cache/assets/E7B/B30/sprockets%2Ff9a4ddabcbb1fd40d740c7d1faf82c0b create mode 100644 test-app/tmp/cache/assets/EED/630/sprockets%2F2ec8ea4f9f0fb9b3a1bcb6dadebf7d5e create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/-T/-TfUYkKMSHMTIZRZ3arsYIOT39Pk5Sk16LDI8RIOGjE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/0v/0v1ONTmfvy85EBksq352dlZ9kMuSarIVqiMw3joQKzY.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/1i/1iKQqsVNaH1HXKv720Gp91YLSpZGv7i9GqmQvggPUNY.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/1k/1kIm2IvnycK2RhzI2uoY_v73C3L0QhjWGNuZ6xBtlNo.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/2W/2WlO5cq2bv-yK4y2ZFOHCGPJtlo6VHg4cSkBSEYHDnc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/2h/2hmXinPpyPZTqeUBjN_KOkuS9l5VIiRX3IFdmc2K5Sw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/2m/2m-T7rNJfhFi5vQmkns50Q-IHQHCfv-9l9xTEm_-ki0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/2u/2uELoQWvYUyWCLHxSW-K-YhgS_0a77xfIjxBlSaeuuw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/35/35p_RJyk_LQ4gwd7maAX6ITqbsTRJrmeU3nd6sUsCTQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/3h/3hZJZhA0BX-yZFD_tmTwWvFCcCQFl7w_HpoMC4RPK8Y.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/4M/4M5ebMVeS3C_jl4NZ2lHLSSo1yra6lHGg51Ef6R2JiI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/4O/4OLBTVFzH8xWTgMO5u_M9O4xicfTAM0PLC_t2qhaUTE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/4X/4XihhGszAnIK51olnaezt4SMKQ5-hYTN821lcQ2MtGs.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/4d/4dGwlKI9te1HnfhsQ5usZOHhfYngDuwf794kbxNYrTM.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/4v/4vZHE-1k74NbceIcQqTs8hldUBF8xiTFCYIHXvEsVMg.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/53/53b0rdhR_vrOJmP0iN4Jew4CA8ps049jy_MIMUqTS2A.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/5W/5W6yIIGr8CpVcuoxj0O0424U6kltTpzK_umgjwAHQXE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/5X/5XayFNMZdpYC02ECUCHnFIxlNCPVUGRvwund9Xtewo4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/6S/6Sgwo9fiP4AlsXfqGaxMbr9uHchampTH5U9yw3Bdx2g.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/6t/6t4-k6hj8Ax1NfUrPo8lrDvy7qxybes0ND-bLx68j_o.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/75/75hU8oIX_X9m7sxibJOCQWM_qwXdc1z0beURtdIcUYQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/7D/7D3WKj4JXEuX5QZGzLI2s8HnrS_XPxEHIq858Cj3_5Q.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/7w/7wkzuoAnHC12M0J4CMcGdwpfxjV0OMbK-SLwKMe4zTo.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/8R/8R839cUoQX8tKI44ZUocDieu8rsyi2TkMaDotqvEVQQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/8y/8yz-zAhtr6khXijMX_XYY-me6MZIdM1Quv-tUvGbPBI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/AF/AF2B7X5uhZqq4NFKYvbERnBIuwyL9jmzN26Tqfdjk-Q.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/AN/ANELXr02iyUwMQO5hPtpThbaoxRuKXE_P87TVWg_IJU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Ai/AiVqU_GM86p-_JYDWZugzD4ihdbhsjdwKrRaUTF5nyU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Ak/AkFsU9OQZM1UBgnscrldY2nxlLphnE1mMcffFHHpTo4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Ax/AxHTmqIFLxrR4Q6obLQXmK7SKpCbIbLxzCgEdtuEemQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/BZ/BZh4ur30TSLaPDerUJvyj7h_7yua17w3NFWBXrpk4-8.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/CB/CBu2LfZ0AhiAmy6B9X9riEMt8P3QPQ-9glxclOpq2HU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Eo/EohYG_xq8OARDftwZNkEkzV0es51BqPQErJmiH9eoLc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/FD/FDnS4rs3Qq1aRHBH0cBzT1FmNHZlLLzCUQJyADCZqC8.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/FU/FU9uG6mlGYNlUB7qvsTYA__8nuwVfFo8V1IRNWBijNk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Fn/Fn4iVqPepDqEGd0GdRVmKJ8vh63hNftsKC-atFsoYLU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/H_/H_CvrXPH5m9l1rKkqQr28R4_VDnn09MAJSp6XnB2UmE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Hs/Hsl2l11txotmeXpqOnLn9uBw_HQyQqCJLlaZ7p0ERoE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/IC/ICoeFSiKk-djrANYoPQ_IexE0De1b7eMGMrwGRaR7RU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/IZ/IZvE-uc8bQfwYf42hROM24uOYU9cWr_0MfDuOIpNQrY.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Iv/IvxOEtSKbSek1n9vUXAu_TNT1HGZgudxaabCxdZOBy0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Iv/ivDZdCjIS_VJLtS4NwA8sXV9eiY8xJ3oJxmFPXimBA0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/JS/JSXzsLgtWO5PBTmu9MNUXI5qTfb3MUQv_tvWAXGmmN8.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/JT/JTSffA7hb9zN13uy5O7PlpQ3qDZyQGgxfZ-7x3bRnVk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Jm/JmIzdDwcn1o_2r6HmKZaANeHpMkKucZ65gbxTWUz66M.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/KK/KKIjkgVUSdwRQvf9UTlga4r3yJ2xre5pmLZweDkJL0s.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/LP/LPF2tHTl8QLGOm0dm4LGZtrzHG2H6VfjZEWx9jdpfOU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/LU/LUTqrINdwqs3fe4Fjificw2INCC_B3ClDtFCzNRTM_o.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/MR/MRndOg0czdRhylYUw4ivW0mdL1MQmhRd_ugcirhvlL0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/N8/N8izuhHJYPxDdKROhXzErPbYnB8uQSqmOC0fkpICQC4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Na/NaxTKLRwaHiv8ljQoILjlLavJlEhGibSd7P6Au3oWiQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/O1/O11ox3E0XURQYRJVc4Ajy5CAoTqvPntWnn7QOCvF_N0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/O6/O6XL1DtzTI1lZHoB5L2X_rdNxV1C2vYon1c09HDNOb4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/OG/OG9uGHaeC8r8KobSJXnHbTjBgbttO62WuyzRehyx-gs.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/OS/OSST0O3KT7hPeDyKIXIHWon6EQKwd4m1fwnAuJPUyfA.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/OS/oS5xhiDYEtPGUin3F4RDDxXD8x8rcr6sbVxyccrqLCA.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Oa/OaRoX0UHm6BCpM-f5XPgRE6zdp_tjRGRaIs_dG0MXEo.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/P7/P7cRQ0OwcSKapRJAHMTkTt2pBqw5eGwhKw1VykhMkrs.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/PK/PKieAG9AeATLE3UbtWwTCuvpn0h17Fl-cog1_JiWRqc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/PZ/PZmg7NtMtDtxpld-5yjlzuilJmfMjfE1pPoa3yBbPrE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/QW/QWVQ6AqNWTj7PLKkigjJwFzYsC2gHLpnEDq-JBLALOE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/RI/RImd_G27OlI5tNPRlNBpRWGHm6lZ9e5uaNtEcn09vMQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Ra/RaywOtR5jBWDGVHKg_SQHczy6qUzYXUplV1OXTd4F8Q.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/SG/SGtvfET419Y_Ur63YtpWNYglQZQPRO_OBnVS9xH9NwI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Sc/ScIs1PTad7Xs9tPWRHlLP9L2EFtqpwhBMaTpLQVG0Ng.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/T1/T1kZg-Ut2Bv3EBo0ANWpTAro8vFWbLoZpNELZaZ3usk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/T2/T23n6RFh41g5Hxi4qxltv0mHMBPgRPxyRRvLxa89JQ0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/T7/T7_iT6DBTW8CbaAxzRmHbVERtvIzeEUYcOw0QXeXCQw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/TB/TBk2EMiQ6qy45zmSv1JsIj4ocITUk7Vy_ph8WVNczjU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/TC/TCxEziIwkURjDq2tV-yG53zfh2fYRGQ4j7HFrjWm3c4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/TG/TGf-TNdFvzGuTtlds_v0iarosDMcYisn4yM7w2qa91Q.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/U0/U0TKpkBjuBdoIF9K53GJk9D-gB5u2d6YtS_IBNp3PJs.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Ui/UiM9WFY5qNWlMG3rY6YGQJiGihk7SAJy9hyg0vNtZKA.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/V4/V410_f-SJc9XxwFPBKZW8p8Uc3P1Bvq2qxkv-aZ4tkU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/W2/W2nemvkTWNYYLONS6fbAM0uhPIRRWV8o4Sd85OX8BUk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/WC/WCUxguY757q-Wy3Be-qvM7zjMZc4ESkr1yRDRlsdRTE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/WG/WGwRdpxbnB0VZhIVa_aNzEJWCUbeet84vndtfNuKAkU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Wj/WjWQRyUvliAqx37gkKdBH1lr9K1jE4O41gwhFEhgwfM.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/XM/XMMMkUald7cxuqGMPZwOvPqfOQrOVheg_GDMhkpH9yE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/XS/XSENHRCnssM_Y8Dkp3zEEWWATOJVMNj_pGeD8Rv34cc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Xe/Xe62azGGJPFGIw_N1o-D5la8ZAgH90hVoio8bhHKl48.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Yk/YkwZjRDPHItNLaVZGbP0Jix7jeECwtZ7wpj-lqoNAZA.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Z7/Z7zYD5mMwrHIXKlzNRat74NSBENLLB4ErCouNjViheA.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/ZM/ZMI-bUi_I8I2xiUNtlP_OzboarE3oXrbQCD_H65lTnw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/ZU/ZU__N44kUg3TKffUlHdwJBAD3C4kPxKzuBCI32KHeck.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/ZY/ZY1jV5RgoTpugHi5lRn274P8ga74AznZma2x9SE7gnU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/Zk/ZkI8AtESeSitt1zU53EKOl1v6vx8B4mqgUmw3u8z7xM.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/_9/_9ctPYBMEUDgTapaeCAlokGoXQw5Gw9TZ7HOXo6wK8s.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/_g/_g5y0yCpQNDZzFw_jyeQphmfPPLNgGykUzp4MHJUMsk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/bI/bIolJsq1TIvOun3lZYFrvxDJx2znvH3ml-yzwrrwC5o.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/bb/bbGc584Re4k91_hCoUilorWyIRBL-LE8Ep30ueKDqEU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/bv/bvb6c1TZq7yjR7D2huDarYAO-2nIoHiVc-DL6ptR454.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/dV/dVKP2nv7tbbr1cyL5HPznumqNv0DrlnldsosP6-jEso.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/de/deyI2Tzru2cPQZ3FrxJZKVk5W9q-3LZwfCKlGrg4kLs.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/dy/dy1YgVnd0lW0bwaUQWyw7iznP9KxQdk2HkDIzc2H8Sw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/eY/eYyyS68PIe5uyAXrk5UGy3MAvWuS4vy3oqg94VuOuew.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/gK/gKHvQOeMniLw7ja2OCF_nwbTy8OcudWnthCudwmzjFk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/gq/gQ2gj-uTLrvctplyB-3jZX1xE4FnLxIVFQlATnKxpGI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/gq/gqOi5JB-EW5ckTa4WPtlIsZQrmJhSvlqq0wphCdquo0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/h1/h1NkBlkWUXcK6aEG7wPPHJkkX8D-TscozIMmwk_-pIU.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/iO/iO8VElIL-lYIa3kPnwi8hwRitZldAsJlF0GA1yLPW2I.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/jr/jrPAWq1Tmic8O1ULEIytPdC-s2a3W-ome5_yPV2THH4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/k9/k9SVZrt-rhYeNNXrEFNtCCkPxYPn-11MacKVjd9lp9M.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/kO/kOHCcxg_41A5QGfXDXyFVCVx5sLeRS6Kw9x70_y6Ciw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/kR/Krphgd_z5C2Z4nD_ndnTJ_bI1wmnXn70nNsATLfA5_E.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/kR/kR67XDov7pmnvvGN0ylShdhibaAC3VFMkyH26JkOJSk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/ku/kuHal3FzFQqClBLCq2N85DiTd8xLl12RG8uRfRmKLbQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/kx/kxR1WBMiNEJJB5c5CAcppl7c-Q5tL7E4X7coarmN9_k.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/lF/lFxlr-_l3_UESGZifD7F0nCUxZywqwEnsTJBctqkATc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/lq/lqpoJFMrmwWIcESwk6xMcpxSGCg87TUndiE3yUaWXyw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/mK/mK-pQ7Y8vx5YvHrGdzFUZadOj7gYU2ob6bS66uLMRBA.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/mc/mcjZfI65nyzyxZFoh0ExQmRNrjD6c0q3TlNbNuAl5zQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/mn/mnRkw00oC-jGuXKZkjSBH5vzcYw1YALeD0ebws0Z6-k.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/nX/nXgEOftu7xA6bpMnHc--ipxfZMu4yCJMiU4NKL_mlos.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/nZ/nZ9flIV4ipJ3xBPmLHwSWhpt7OhY3ufuFLvqvLrv_uI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/nb/nb5sTMc1sqUaFmyyvTgBAQJE2c5z4Y1LkFBOJ6Ow2K0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/oe/oeE0hX76Av73DaWS3YGck4xFDX4jGDdIsE-7TQNwr_Q.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/q3/q3SEkL-krNQFj3RpAXgANR38tlmtCoE3gvFs3cJSWB0.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/q9/q9j-4PChQy7_Eg7Pox07YjNO2E8PDOB3c5jjJXUnRK4.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/qL/qLVY0YArYZIRqTcXxAvcpDIoCkGwydv2oRVHOwX-b9c.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/qZ/qZqIXzhRVfrWL6qBhVL3Dkozr_6vlMGDfKtbmZ8MrXk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/qk/qk9_CfYx8m75xdzgrkhA8vhQmPGk2qaGqkIDjCQ7Qdc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/rW/rW-0m-SifvGE1h3yL9PP3ks7pqk05zlHwDpIn9douXI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/rY/rYZFf_Gas2MSD9kB0tLZh4zQacG-wNeuQdHBnMGvC1s.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/s7/s7yzMGKzCD1r_zS3DMMX4u_4_xrNCHXWPrAsCWnFVdc.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/tI/tI0HNOcTNdbHY9GTEtSktihb--UacCef319epJGucmw.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/tp/tpSiX-xHFGGCDq9uOpbefot50KhxX4owOJqkUSdTl8E.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/ud/ud4SNIsQXRtsMeotM9wkfvtXQwFzlnlGroXMdfRmKKk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/v3/v3OUqzxgM6Thu0ODRnQo3coGAM5TAyrDP9khQasIUPI.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/wz/wzFbU2kMPIfwnpouD1N2jm0PY7NOcW5jEhd-TxvTo6k.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/xn/xnwxfmw846xN4WfJnzOAflrLQfid6sBwZQQu_YX72Wk.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/y4/y4NGH_-sGDYixTRXfmGaAERQvEVYgJ8bntrK1RYngeQ.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/zX/zXTuF0gpCWb1cV0rm6TFeLPYpdoNPq8rH_X5X-rMZXE.cache create mode 100644 test-app/tmp/cache/assets/sprockets/v3.0/zb/zbYNO3K3pWsAxTzs-zMe7TRrlu644MDeNAgVTIIrOYE.cache delete mode 100644 vendor/assets/components/swagger-ui/css/typography.css delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.eot delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.svg delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.woff delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.woff2 delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.eot delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.svg delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.woff delete mode 100644 vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2 delete mode 100644 vendor/assets/components/swagger-ui/images/explorer_icons.png delete mode 100755 vendor/assets/components/swagger-ui/images/favicon-16x16.png delete mode 100755 vendor/assets/components/swagger-ui/images/favicon-32x32.png delete mode 100644 vendor/assets/components/swagger-ui/images/logo_small.png delete mode 100644 vendor/assets/components/swagger-ui/images/pet_store_api.png delete mode 100644 vendor/assets/components/swagger-ui/images/wordnik_api.png delete mode 100644 vendor/assets/components/swagger-ui/lib/handlebars-2.0.0.js delete mode 100644 vendor/assets/components/swagger-ui/lib/highlight.7.3.pack.js delete mode 100644 vendor/assets/components/swagger-ui/lib/underscore-min.js delete mode 100644 vendor/assets/components/swagger-ui/lib/underscore-min.map delete mode 100644 vendor/assets/components/swagger-ui/swagger-ui.js delete mode 100644 vendor/assets/components/swagger-ui/swagger-ui.min.js diff --git a/Gemfile b/Gemfile index bf08a2e..11e7d94 100644 --- a/Gemfile +++ b/Gemfile @@ -1,26 +1,29 @@ -source 'https://rubygems.org' +source "https://rubygems.org" -# Declare your gem's dependencies in swagger_rails.gemspec. -# Bundler will treat runtime dependencies like base dependencies, and -# development dependencies will be added by default to the :development group. -gemspec +# Allow the rails version to come from an ENV setting so Travis can test multiple versions. +# See http://www.schneems.com/post/50991826838/testing-against-multiple-rails-versions/ +rails_version = ENV['RAILS_VERSION'] || '3.2.22' -# Declare any dependencies that are still in development here instead of in -# your gemspec. These might include edge Rails or gems from your path or -# Git. Remember to move these dependencies to your gemspec before releasing -# your gem to rubygems.org. +gem 'rails', "#{rails_version}" + +case rails_version.split('.').first +when '3' + gem 'strong_parameters' +when '4', '5' + gem 'responders' +end -# To use a debugger -# gem 'debugger', group: [:development, :test] -# gem 'sqlite3' -group :development, :test do - gem 'pry' - gem 'generator_spec' -end +gem 'rswag-api', path: './rswag-api' +gem 'rswag-ui', path: './rswag-ui' + +# To use debugger +# gem 'debugger' group :test do gem 'test-unit' - gem 'database_cleaner' + gem 'rspec-rails' + gem 'generator_spec' + gem 'rswag-specs', path: '~/src/rswag/rswag-specs' end diff --git a/Gemfile.lock b/Gemfile.lock index ef7b173..8ab8788 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,20 @@ PATH - remote: . + remote: ./rswag-api specs: - swagger_rails (1.0.1.pre.beta3) - rack + rswag-api (1.0.0) + rails (>= 3.1, < 5.1) + +PATH + remote: ./rswag-ui + specs: + rswag-ui (1.0.0) + rails (>= 3.1, < 5.1) + +PATH + remote: ~/src/rswag/rswag-specs + specs: + rswag-specs (1.0.0) + json-schema rails (>= 3.1, < 5.1) GEM @@ -35,10 +47,9 @@ GEM activesupport (3.2.22) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) + addressable (2.4.0) arel (3.0.3) builder (3.0.4) - coderay (1.1.0) - database_cleaner (1.5.3) diff-lcs (1.2.5) erubis (2.7.0) generator_spec (0.9.3) @@ -48,20 +59,17 @@ GEM i18n (0.7.0) journey (1.0.4) json (1.8.3) + json-schema (2.7.0) + addressable (>= 2.4) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - method_source (0.8.2) mime-types (1.25.1) - multi_json (1.11.2) + multi_json (1.12.1) polyglot (0.3.5) - power_assert (0.2.6) - pry (0.10.3) - coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) + power_assert (0.3.1) rack (1.4.7) - rack-cache (1.5.1) + rack-cache (1.6.1) rack (>= 0.4) rack-ssl (1.3.4) rack @@ -82,34 +90,38 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - rake (10.4.2) + rake (11.3.0) rdoc (3.12.2) json (~> 1.4) - rspec-core (3.4.4) - rspec-support (~> 3.4.0) - rspec-expectations (3.4.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-mocks (3.4.1) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-rails (3.4.1) - actionpack (>= 3.0, < 4.3) - activesupport (>= 3.0, < 4.3) - railties (>= 3.0, < 4.3) - rspec-core (~> 3.4.0) - rspec-expectations (~> 3.4.0) - rspec-mocks (~> 3.4.0) - rspec-support (~> 3.4.0) - rspec-support (3.4.1) - slop (3.6.0) + rspec-support (~> 3.5.0) + rspec-rails (3.5.2) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) sprockets (2.2.3) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) sqlite3 (1.3.11) - test-unit (3.1.5) + strong_parameters (0.2.3) + actionpack (~> 3.0) + activemodel (~> 3.0) + activesupport (~> 3.0) + railties (~> 3.0) + test-unit (3.2.1) power_assert thor (0.19.1) tilt (1.4.1) @@ -122,10 +134,12 @@ PLATFORMS ruby DEPENDENCIES - database_cleaner generator_spec - pry - rspec-rails (~> 3.0) + rails (= 3.2.22) + rspec-rails + rswag-api! + rswag-specs! + rswag-ui! sqlite3 - swagger_rails! + strong_parameters test-unit diff --git a/README.md b/README.md index 93cbc67..90846a2 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,51 @@ -swagger_rails +rswag (formerly swagger_rails) ========= -Generate API documentation, including a slick discovery UI and playground, directly from your rspec integration specs. Use the provided DSL to describe and test API operations in your spec files. Then, you can easily generate corresponding swagger.json files and serve them up with an embedded version of [swagger-ui](https://github.com/swagger-api/swagger-ui). Best of all, it requires minimal coding and maintenance, allowing you to focus on building an awesome API! +[Swagger](http://swagger.io) tooling for Rails API's. Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests. + +Rswag extends rspec-rails "request specs" with a Swagger-based DSL for describing and testing API operations. You describe your API operations with a succinct, intuitive syntax, and it automaticaly runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as JSON endpoints. Rswag also provides an embedded version of the awesome [swagger-ui](https://github.com/swagger-api/swagger-ui) that's powered by the exposed JSON. This toolchain makes it seamless to go from integration specs, which you're probably doing in some form already, to living documentation for your API consumers. And that's not all ... -Once you have a Web API that can describe itself in Swagger, you've opened the treasure chest of Swagger-based tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen](https://github.com/swagger-api/swagger-codegen) for more details. - -__NOTE__: It's early days so please be gentle when reporting issues :) As author of a similar project in the .NET space - [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle), that's become very popular, I think there's real potential here. Please feel free to contribute. I'll be more than happy to consider PR's ... so long as they include tests. +Once you have an API that can describe itself in Swagger, you've opened the treasure chest of Swagger-based tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen](https://github.com/swagger-api/swagger-codegen) for more details. ## Getting Started ## 1. Add this line to your applications _Gemfile_: ```ruby - gem 'swagger_rails' + gem 'rswag' ``` 2. Run the install generator ```ruby - rails g swagger_rails:install + rails g rswag:install ``` -3. Create an integration spec to describe and test your API +3. Create an integration spec to describe and test your API. ```ruby + # spec/integration/blogs_spec.rb require 'swagger_helper' describe 'Blogs API' do path '/blogs' do - post 'creates a new blog' do - operation_description 'Detailed implementation notes for the create a new blog API endpoint' - consumes 'application/json' - parameter :blog, in: :body, schema: { + post 'Creates a blog' do + tags 'Blogs' + consumes 'application/json', 'application/xml' + parameter name: :blog, in: :body, schema: { type: :object, properties: { - title: { :type => :string }, - content: { :type => :string } + title: { type: :string }, + content: { type: :string } }, required: [ 'title', 'content' ] } - response '200', 'success' do + response '201', 'blog created' do let(:blog) { { title: 'foo', content: 'bar' } } run_test! end @@ -55,100 +56,267 @@ __NOTE__: It's early days so please be gentle when reporting issues :) As author end end end + + path '/blogs/{id}' do + + get 'Retrieves a blog' do + tags 'Blogs' + produces 'application/json', 'application/xml' + parameter name: :id, :in => :path, :type => :string + + response '200', 'blog found' do + schema type: :object, + properties: { + id: { type: :integer }, + title: { type: :string }, + content: { type: :string } + }, + required: [ 'id', 'title', 'content' ] + + let(:id) { Blog.create(title: 'foo', content: 'bar').id } + run_test! + end + + response '404', 'blog not found' do + let(:id) { 'invalid' } + run_test! + end + end + end end ``` -4. Generate the swagger.json file(s) +4. Generate the Swagger JSON file(s) ```ruby - rake swaggerize + rake rswag:specs:swaggerize ``` 5. Spin up your app and check out the awesome, auto-generated docs at _/api-docs_! -## How does it Work? ## +## The rspec DSL ## -There's two separate parts to swagger rails: +### Paths, Operations and Responses ### -1. Tooling to easily generate swagger descriptions directly from your API tests/specs -2. Rails middleware to auto-magically serve a swagger-ui that's powered by those descriptions +If you've used [Swagger](http://swagger.io/specification) before, then the syntax should be very familiar. To describe your API operations, start by specifying a path and then list the supported operations (i.e. HTTP verbs) for that path. Path parameters must be surrounded by curly braces ({}). Within an operation block (see "post" or "get" in the example above), most of the fields supported by the [Swagger "Operation" object](http://swagger.io/specification/#operationObject) are available as methods on the example group. To list (and test) the various responses for an operation, create one or more response blocks. Again, you can reference the [Swagger "Response" object](http://swagger.io/specification/#responseObject) for available fields. -The tooling is designed to fit seamlessly into your development workflow, with the swagger docs and UI being a by-product that you get for free ... well almost free :) You'll need to use the provided rspec DSL. But, it's an intuitive syntax (based on the [swagger-spec](http://swagger.io/specification/)) and, IMO, a very succint and expressive way to write api/integration tests. +Take special note of the __run_test!__ method that's called within each response block. This tells rswag to create and execute a corresponding example. It builds and submits a request based on parameter descriptions and corresponding values that have been provided using the rspec "let" syntax. For example, the "post" description in the example above specifies a 'body' parameter called 'blog'. It also lists 2 different responses. For the success case (i.e. the 201 response), notice how "let" is used to set the blog parameter to a value that matches the provided schema. For the failure case (i.e. the 422 response), notice how it's set to a value that does not match the provided schema. When the test is executed, rswag also validates the actual response code and, where applicable, the response body against the provided [JSON Schema](http://json-schema.org/documentation.html). -Once you've generated the swagger files, the functionality to serve them up, along with the swagger-ui, is provided as a Rails Engine. After running the install generator, you'll see the following line added to _routes.rb_ +If you'd like your specs to be a little more explicit about what's going on here, you can replace the call to __run_test!__ with equivalent "before" and "it" blocks: - ```ruby - mount SwaggerRails::Engine => '/api-docs' - ``` +```ruby +response '201', 'blog created' do + let(:blog) { { title: 'foo', content: 'bar' } } + + before do |example| + submit_request(example.metadata) + end + + it 'returns a valid 201 response' do |example| + assert_response_matches_metadata(example.metadata) + end +end +``` + +### Global Metadata ### + +In addition to paths, operations and responses, Swagger also supports global API metadata. When you install rswag, a file called _swagger_helper.rb_ is added to your spec folder. This is where you define one or more Swagger documents and provide global metadata. Again, the format is based on Swagger so most of the global fields supported by the top level ["Swagger" object](http://swagger.io/specification/#swaggerObject) can be provided with each document definition. As an example, you could define a Swagger document for each version of your API and in each case specify a title, version string and URL basePath: + +```ruby +# spec/swagger_helper.rb +RSpec.configure do |config| + config.swagger_root = Rails.root.to_s + '/swagger' -This will wire up routes for the swagger docs and swagger-ui assets, all prefixed with "/api-docs". For example, if you navigate to "/api-docs/index.html" you'll get the swagger-ui. If you navigate to "/api-docs/v1/swagger.json", you'll get the swagger.json file under your app root at "swagger/v1/swagger.json" - assuming it was generated. + config.swagger_docs = { + 'v1/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V1', + version: 'v1' + }, + basePath: '/api/v1' + }, -If you'd like your swagger resources to appear under a different base path, you can change the Engine mount point from "/api-docs" to something else. + 'v2/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V2', + version: 'v2' + }, + basePath: '/api/v2' + } + } +end +``` -## Multiple Swagger Documents ## +__NOTE__: By default, the paths, operations and responses defined in your spec files will be associated with the first Swagger document in _swagger_helper.rb_. If you're using multiple documents, you'll need to tag the individual specs with their target document name: -By default, the generator will create all operation descriptions in a single swagger.json file. You can customize this by defining additional documents in the swagger_helper (installed under your spec folder) ... +```ruby +# spec/integration/v2/blogs_spec.rb +describe 'Blogs API', swagger_doc: 'v2/swagger.json' do - ```ruby - RSpec.configure do |config| - ... + path '/blogs' do + ... - config.swagger_docs = { - 'v1/swagger.json' => { - swagger: '2.0', - info: { - title: 'API V1', - version: 'v1' + path '/blogs/{id}' do + ... +end +``` + +## Configuration & Customization ## + +The steps described above will get you up and running with minimal setup. However, rswag offers a lot of flexibility to customize as you see fit. Before exploring the various options, you'll need to be aware of it's different components. The following table lists each of them and the files that get added/updated as part of a standard install. + +|Gem|Description|Added/Updated| +|---------|-----------|-------------| +|__rswag-specs__|Swagger-based DSL for rspec & accompanying rake task for generating Swagger files|_spec/swagger_helper.rb_| +|__rswag-api__ |Rails Engine that exposes the Swagger files as JSON endpoints|_config/initializers/rswag-api.rb, config/routes.rb_| +|__rswag-ui__ |Rails Engine that includes [swagger-ui](https://github.com/swagger-api/swagger-ui) and powers it from the Swagger endpoints|_config/initializers/rswag-ui.rb, config/routes.rb_| + +### Output Location for Generated Swagger Files ### + +You can adjust this in the _swagger_helper.rb_ that's installed with __rspec-specs__: + +```ruby +# spec/swagger_helper.rb +RSpec.configure do |config| + config.swagger_root = Rails.root.to_s + '/your-custom-folder-name' + ... +end +``` + +__NOTE__: If you do change this, you'll also need to update the rswag-api.rb initializer (assuming you're using rswag-api). More on this later. + +### Referenced Parameters and Schema Definitions ### + +Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals. For example, you might have a standard response structure for all failed operations. Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec: + +```ruby +# spec/swagger_helper.rb +config.swagger_docs = { + 'v1/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V1' + }, + definitions: { + errors_object: { + type: 'object', + properties: { + errors: { '$ref' => '#/definitions/errors_map' } } }, - - 'v2/swagger.json' => { - swagger: '2.0', - info: { - title: 'API V2', - version: 'v2' + errors_map: { + type: 'object', + additionalProperties: { + type: 'array', + items: { type: 'string' } } } } - end - ``` + } +} -And then tagging your spec's with the target swagger_doc: +# spec/integration/blogs_spec.rb +describe 'Blogs API' do - ```ruby - require 'swagger_helper' + path '/blogs' do - describe 'Blogs API V2', swagger_doc: 'v2/swagger.json' do + post 'Creates a blog' do + + response 422, 'invalid request' do + schema '$ref' => '#/definitions/errors_object' + ... +end + +# spec/integration/comments_spec.rb +describe 'Blogs API' do - path '/blogs' do - ... - end - end - end - ``` + path '/blogs/{blog_id}/comments' do -Then, when you run the generator and spin up the swagger-ui, you'll see a select box in the top right allowing your audience to switch between the different API versions. + post 'Creates a comment' do + + response 422, 'invalid request' do + schema '$ref' => '#/definitions/errors_object' + ... +end +``` -## Tweaking the Swagger Document with Request Context ## +### Route Prefix for Swagger JSON Endpoints ### -You can provide global metadata for Swagger documents in the swagger_helper file and this will be included in the resulting Swagger JSON when you run the "swaggerize" rake task. For the most part, this is sufficient. However, you may want to make some changes that require the current request context. This is possible by applying an optional swagger_filter in the swagger_rails initializer (installed into config/initializers): +The functionality to expose Swagger files, such as those generated by rswag-specs, as JSON endpoints is implemented as a Rails Engine. As with any Engine, you can change it's mount prefix in _routes.rb_: - ```ruby - SwaggerRails.configure do |c| - ... +```ruby +TestApp::Application.routes.draw do + ... - c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } - end - ``` + mount Rswag::Api::Engine => 'your-custom-prefix' +end +``` -This function will get called prior to serialization of any Swagger file and is passed the rack env for the current request. This provides a lot of flexibilty. For example, you could dynamically assign the "host" property (as shown above) or you could inspect session information or Authoriation header and remove operations based on user permissions. +Assuming a Swagger file exists at '<swagger_root>/v1/swagger.json', this configuration would expose the file as the following JSON endpoint: -## Customizing the UI ## +``` +GET http:///your-custom-prefix/v1/swagger.json +``` + +### Root Location for Swagger Files ### + +You can adjust this in the _rswag-api.rb_ initializer that's installed with __rspec-api__: + +```ruby +Rswag::Api.configure do |c| + c.swagger_root = Rails.root.to_s + '/your-custom-folder-name' + ... +end +``` + +__NOTE__: If you're using rswag-specs to generate Swagger files, you'll want to ensure they both use the same <swagger_root>. The reason for separate settings is to maintain independence between the two gems. For example, you could install rswag-api independently and create your Swagger files manually. + +### Dynamic Values for Swagger JSON ## + +There may be cases where you need to add dynamic values to the Swagger JSON that's returned by rswag-api. For example, you may want to provide an explicit host name. Rather than hardcoding it, you can configure a filter that's executed prior to serializing every Swagger document: + +```ruby +Rswag::Api.configure do |c| + ... + + c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } +end +``` + +Note how the filter is passed the rack env for the current request. This provides a lot of flexibilty. For example, you can assign the "host" property (as shown) or you could inspect session information or an Authoriation header and remove operations based on user permissions. + +### Enable Swagger Endpoints for swagger-ui ### + +You can update the _rswag-ui.rb_ initializer, installed with rswag-ui, to specify which Swagger endpoints should be available to power the documentation UI. If you're using rswag-api, these should correspond to the Swagger endpoints it exposes. When the UI is rendered, you'll see these listed in a drop-down to the top right of the page: + +```ruby +Rswag::Ui.configure do |c| + c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs' + c.swagger_endpoint '/api-docs/v2/swagger.json', 'API V2 Docs' +end +``` + +### Route Prefix for the swagger-ui ### + +Similar to rswag-api, you can customize the swagger-ui path by changing it's mount prefix in _routes.rb_: + +```ruby +TestApp::Application.routes.draw do + ... + + mount Rswag::Api::Engine => 'api-docs' + mount Rswag::Ui::Engine => 'your-custom-prefix' +end +``` + +### Customizing the swagger-ui ### The swagger-ui provides several options for customizing it's behavior, all of which are documented here https://github.com/swagger-api/swagger-ui#swaggerui. If you need to tweak these or customize the overall look and feel of your swagger-ui, then you'll need to provide your own version of index.html. You can do this with the following generator. ```ruby -rails g swagger_rails:custom_ui +rails g rswag:ui:custom_ui ``` -This will add a local version that you can customize at "app/views/swagger_rails/swagger_ui/index.html.erb" +This will add a local version that you can modify at _app/views/rswag/ui/home/index.html.erb_ diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index bdf9b56..0000000 --- a/README.rdoc +++ /dev/null @@ -1,3 +0,0 @@ -= SwaggerRails - -This project rocks and uses MIT-LICENSE. \ No newline at end of file diff --git a/app/controllers/swagger_rails/swagger_ui_controller.rb b/app/controllers/swagger_rails/swagger_ui_controller.rb deleted file mode 100644 index 525fa31..0000000 --- a/app/controllers/swagger_rails/swagger_ui_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'json' - -module SwaggerRails - class SwaggerUiController < ApplicationController - - def index - swagger_root = SwaggerRails.config.resolve_swagger_root(request.env) - swagger_root.concat('/') unless swagger_root.end_with?('/') - - swagger_filenames = Dir["#{swagger_root}/**/*.json"] - - @discovery_paths = Hash[ - swagger_filenames.map do |filename| - [ - filename.sub(swagger_root, root_path.chomp('/')), - load_json(filename)["info"]["title"] - ] - end - ] - - render :index, layout: false - end - - private - - def load_json(filename) - JSON.parse(File.read(filename)) - end - end -end diff --git a/config/routes.rb b/config/routes.rb deleted file mode 100644 index 8543722..0000000 --- a/config/routes.rb +++ /dev/null @@ -1,3 +0,0 @@ -SwaggerRails::Engine.routes.draw do - root to: 'swagger_ui#index' -end diff --git a/lib/generators/swagger_rails/custom_ui/custom_ui_generator.rb b/lib/generators/swagger_rails/custom_ui/custom_ui_generator.rb deleted file mode 100644 index 4177abd..0000000 --- a/lib/generators/swagger_rails/custom_ui/custom_ui_generator.rb +++ /dev/null @@ -1,9 +0,0 @@ -module SwaggerRails - class CustomUiGenerator < Rails::Generators::Base - source_root File.expand_path('../files', __FILE__) - - def add_custom_index - copy_file('index.html.erb', 'app/views/swagger_rails/swagger_ui/index.html.erb') - end - end -end diff --git a/lib/generators/swagger_rails/custom_ui/files/index.html.erb b/lib/generators/swagger_rails/custom_ui/files/index.html.erb deleted file mode 100644 index 302d46a..0000000 --- a/lib/generators/swagger_rails/custom_ui/files/index.html.erb +++ /dev/null @@ -1,153 +0,0 @@ - - - - - Swagger UI - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
-
- - diff --git a/lib/generators/swagger_rails/install/USAGE b/lib/generators/swagger_rails/install/USAGE deleted file mode 100644 index cece75d..0000000 --- a/lib/generators/swagger_rails/install/USAGE +++ /dev/null @@ -1,9 +0,0 @@ -Description: - Adds default files required to use swagger_rails - -Example: - rails generate swagger_rails:install - - This will create: - config/swagger/v1/swagger.json - config/initializers/swagger_rails.rb diff --git a/lib/generators/swagger_rails/install/install_generator.rb b/lib/generators/swagger_rails/install/install_generator.rb deleted file mode 100644 index 9930130..0000000 --- a/lib/generators/swagger_rails/install/install_generator.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'rails/generators' - -module SwaggerRails - - class InstallGenerator < Rails::Generators::Base - source_root File.expand_path('../templates', __FILE__) - - def add_swagger_dir - empty_directory('swagger/v1') - end - - def add_initializer - template('swagger_rails.rb', 'config/initializers/swagger_rails.rb') - end - - def add_rspec_helper - template('swagger_helper.rb', 'spec/swagger_helper.rb') - end - - def add_routes - route("mount SwaggerRails::Engine => '/api-docs'") - end - end -end diff --git a/lib/generators/swagger_rails/install/templates/swagger_helper.rb b/lib/generators/swagger_rails/install/templates/swagger_helper.rb deleted file mode 100644 index 26523b3..0000000 --- a/lib/generators/swagger_rails/install/templates/swagger_helper.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rails_helper' -require 'swagger_rails/rspec/dsl' - -RSpec.configure do |config| - # NOTE: Should be no need to modify these 3 lines - config.add_setting :swagger_root - config.add_setting :swagger_docs - config.extend SwaggerRails::RSpec::DSL - - # Specify a root folder where Swagger JSON files are generated - # NOTE: If you're using the Swagger JSON middleware to serve API descriptions, you'll need - # to ensure that the same folder is also specified in the swagger_rails initializer - config.swagger_root = Rails.root.to_s + '/swagger' - - # Define one or more Swagger documents and provide global metadata for each one - # When you run the "swaggerize" rake task, the complete Swagger will be generated - # at the provided relative path under swagger_root - # By default, the operations defined in spec files are added to the first - # document below. You can override this behavior by adding a swagger_doc tag to the - # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' - config.swagger_docs = { - 'v1/swagger.json' => { - swagger: '2.0', - info: { - title: 'API V1', - version: 'v1' - } - } - } -end diff --git a/lib/swagger_rails.rb b/lib/swagger_rails.rb deleted file mode 100644 index 637bc72..0000000 --- a/lib/swagger_rails.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'swagger_rails/version' -require 'swagger_rails/configuration' - -module SwaggerRails - - def self.configure - yield(config) - end - - def self.config - @config ||= Configuration.new - end -end - -require 'swagger_rails/engine' if defined?(Rails) diff --git a/lib/swagger_rails/configuration.rb b/lib/swagger_rails/configuration.rb deleted file mode 100644 index 8d434e6..0000000 --- a/lib/swagger_rails/configuration.rb +++ /dev/null @@ -1,10 +0,0 @@ -module SwaggerRails - class Configuration - attr_accessor :swagger_root, :swagger_filter - - def resolve_swagger_root(env) - path_params = env['action_dispatch.request.path_parameters'] || {} - path_params[:swagger_root] || swagger_root - end - end -end diff --git a/lib/swagger_rails/engine.rb b/lib/swagger_rails/engine.rb deleted file mode 100644 index 68d95d1..0000000 --- a/lib/swagger_rails/engine.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'swagger_rails/middleware/swagger_json' - -module SwaggerRails - class Engine < ::Rails::Engine - isolate_namespace SwaggerRails - - initializer 'swagger_rails.initialize' do |app| - middleware.use SwaggerJson, SwaggerRails.config - - if app.config.respond_to?(:assets) - app.config.assets.precompile += [ 'swagger-ui/*' ] - end - end - end -end diff --git a/lib/swagger_rails/middleware/swagger_json.rb b/lib/swagger_rails/middleware/swagger_json.rb deleted file mode 100644 index 56e50ed..0000000 --- a/lib/swagger_rails/middleware/swagger_json.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'json' - -module SwaggerRails - class SwaggerJson - - def initialize(app, config) - @app = app - @config = config - end - - def call(env) - path = env['PATH_INFO'] - filename = "#{@config.resolve_swagger_root(env)}/#{path}" - - if env['REQUEST_METHOD'] == 'GET' && File.file?(filename) - swagger = load_json(filename) - @config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil? - - return [ - '200', - { 'Content-Type' => 'application/json' }, - [ JSON.dump(swagger) ] - ] - end - - return @app.call(env) - end - - private - - def load_json(filename) - JSON.parse(File.read(filename)) - end - end -end diff --git a/lib/swagger_rails/rspec/api_metadata.rb b/lib/swagger_rails/rspec/api_metadata.rb deleted file mode 100644 index e474975..0000000 --- a/lib/swagger_rails/rspec/api_metadata.rb +++ /dev/null @@ -1,46 +0,0 @@ -module SwaggerRails::RSpec - class APIMetadata - - def initialize metadata - @metadata = metadata - end - - def response_example? - @metadata.has_key?(:response_code) - end - - def swagger_doc - @metadata[:swagger_doc] - end - - def swagger_data - { - paths: { - @metadata[:path_template] => { - @metadata[:http_verb] => operation_metadata - } - } - } - end - - private - - - def operation_metadata - { - tags: [find_root_of(@metadata)[:description]], - summary: @metadata[:summary], - description: @metadata[:operation_description], - consumes: @metadata[:consumes], - produces: @metadata[:produces], - parameters: @metadata[:parameters], - responses: { @metadata[:response_code] => @metadata[:response] } - } - end - - def find_root_of(node) - parent = node[:parent_example_group] - parent.nil? ? node : find_root_of(parent) - end - end -end \ No newline at end of file diff --git a/lib/swagger_rails/rspec/dsl.rb b/lib/swagger_rails/rspec/dsl.rb deleted file mode 100644 index 6590f79..0000000 --- a/lib/swagger_rails/rspec/dsl.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'swagger_rails/test_visitor' - -module SwaggerRails - module RSpec - module DSL - - def path(path_template, &block) - metadata = { - path_template: path_template - } - describe(path_template, metadata, &block) - end - - def operation(http_verb, summary=nil, &block) - metadata = { - http_verb: http_verb, - summary: summary, - parameters: [] - } - describe(http_verb, metadata, &block) - end - - def operation_description(message) - metadata[:operation_description] = message - end - - [ :get, :post, :patch, :put, :delete, :head ].each do |http_verb| - define_method(http_verb) do |summary=nil, &block| - operation(http_verb, summary, &block) - end - end - - def consumes(*mime_types) - metadata[:consumes] = mime_types - end - - def produces(*mime_types) - metadata[:produces] = mime_types - end - - # Accepts parameter objects: - # parameter :petId, in: :path, type: :integer, required: true - # Or references: - # parameter ref: '#/parameters/Pet' - def parameter(name, attributes={}) - metadata[:parameters] << if name.respond_to?(:has_key?) - { '$ref': name.delete(:ref) || name.delete('ref') } - else - { name: name.to_s }.merge(attributes) - end - end - - def response(code, description, &block) - metadata = { - response_code: code, - response: { - description: description - } - } - context(description, metadata, &block) - end - - def run_test! - if metadata.has_key?(:swagger_doc) - swagger_doc = ::RSpec.configuration.swagger_docs[metadata[:swagger_doc]] - else - swagger_doc = ::RSpec.configuration.swagger_docs.values.first - end - - test_visitor = SwaggerRails::TestVisitor.new(swagger_doc) - - before do |example| - test_visitor.submit_request!(self, example.metadata) - end - - it "returns a #{metadata[:response_code]} status" do |example| - test_visitor.assert_response!(self, example.metadata) - end - end - end - end -end - diff --git a/lib/swagger_rails/rspec/formatter.rb b/lib/swagger_rails/rspec/formatter.rb deleted file mode 100644 index 45f657c..0000000 --- a/lib/swagger_rails/rspec/formatter.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'rspec/core/formatters' -require 'swagger_helper' -require 'swagger_rails/rspec/api_metadata' - -module SwaggerRails - module RSpec - class Formatter - ::RSpec::Core::Formatters.register self, - :example_group_finished, - :stop - - def initialize(output) - @output = output - @swagger_root = ::RSpec.configuration.swagger_root - @swagger_docs = ::RSpec.configuration.swagger_docs - - @output.puts 'Generating Swagger Docs ...' - end - - def example_group_finished(notification) - metadata = APIMetadata.new(notification.group.metadata) - return unless metadata.response_example? - - swagger_doc = @swagger_docs[metadata.swagger_doc] || @swagger_docs.values.first - swagger_doc.deep_merge!(metadata.swagger_data) - end - - def stop(notification) - @swagger_docs.each do |url_path, doc| - file_path = File.join(@swagger_root, url_path) - - File.open(file_path, 'w') do |file| - file.write(JSON.pretty_generate(doc)) - end - end - - @output.puts 'Swagger Doc generated' - end - end - end -end diff --git a/lib/swagger_rails/test_visitor.rb b/lib/swagger_rails/test_visitor.rb deleted file mode 100644 index f7e850d..0000000 --- a/lib/swagger_rails/test_visitor.rb +++ /dev/null @@ -1,71 +0,0 @@ -module SwaggerRails - class TestVisitor - - def initialize(swagger_doc) - @swagger_doc = swagger_doc - end - - def submit_request!(test, metadata) - params_data = params_data_for(test, metadata[:parameters]) - - path = build_path(metadata[:path_template], params_data) - body_or_params = build_body_or_params(params_data) - headers = build_headers(params_data, metadata[:consumes], metadata[:produces]) - test.send(metadata[:http_verb], path, { params: body_or_params, headers: headers }) - end - - def assert_response!(test, metadata) - test.assert_response(metadata[:response_code].to_i) - end - - private - - def params_data_for(test, parameters) - parameters.map do |parameter| - parameter = resolve_param_ref(parameter[:$ref]) if parameter.has_key?(:$ref) - - parameter - .slice(:name, :in) - .merge(value: test.send(parameter[:name].to_s.underscore)) - end - end - - def resolve_param_ref ref - raise "Invalid parameter reference: #{ref}" unless %r{#/parameters/(?.+)} =~ ref - - parameter = (@swagger_doc[:parameters][name] || @swagger_doc[:parameters][name.to_sym]) - raise "Unknown parameter reference: #{ref}" unless parameter - - parameter.merge(name: name) - end - - def build_path(path_template, params_data) - path_params_data = params_data.select { |p| p[:in] == :path } - - path_template.dup.tap do |path| - path_params_data.each do |param_data| - path.sub!("\{#{param_data[:name]}\}", param_data[:value].to_s) - end - path.prepend(@swagger_doc[:basePath] || '') - end - end - - def build_body_or_params(params_data) - body_params_data = params_data.select { |p| p[:in] == :body } - return body_params_data.first[:value].to_json if body_params_data.any? - - query_params_data = params_data.select { |p| p[:in] == :query } - Hash[query_params_data.map { |p| [ p[:name], p[:value] ] }] - end - - def build_headers(params_data, consumes, produces) - header_params_data = params_data.select { |p| p[:in] == :header } - headers = Hash[header_params_data.map { |p| [ p[:name], p[:value] ] }] - - headers['ACCEPT'] = produces.join(';') if produces.present? - headers['CONTENT_TYPE'] = consumes.join(';') if consumes.present? - - return headers - end - end -end diff --git a/lib/swagger_rails/version.rb b/lib/swagger_rails/version.rb deleted file mode 100644 index 35672c0..0000000 --- a/lib/swagger_rails/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module SwaggerRails - VERSION = "1.0.1-beta3" -end diff --git a/lib/tasks/swagger_rails_tasks.rake b/lib/tasks/swagger_rails_tasks.rake deleted file mode 100644 index 56c985f..0000000 --- a/lib/tasks/swagger_rails_tasks.rake +++ /dev/null @@ -1,14 +0,0 @@ -# desc "Explaining what the task does" -# task :swagger_rails do -# # Task goes here -# end - -if defined?(RSpec) - require 'rspec/core/rake_task' - - desc 'Generate Swagger JSON files from integration specs' - RSpec::Core::RakeTask.new('swaggerize') do |t| - t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb' - t.rspec_opts = [ '--format SwaggerRails::RSpec::Formatter', '--dry-run', '--order defined' ] - end -end diff --git a/spec/dummy/.rspec b/rswag-api/.rspec similarity index 100% rename from spec/dummy/.rspec rename to rswag-api/.rspec diff --git a/MIT-LICENSE b/rswag-api/MIT-LICENSE similarity index 100% rename from MIT-LICENSE rename to rswag-api/MIT-LICENSE diff --git a/Rakefile b/rswag-api/Rakefile similarity index 66% rename from Rakefile rename to rswag-api/Rakefile index 907ed62..2cbae8a 100644 --- a/Rakefile +++ b/rswag-api/Rakefile @@ -1,16 +1,20 @@ -require 'rails' - +#!/usr/bin/env rake begin require 'bundler/setup' rescue LoadError puts 'You must `gem install bundler` and `bundle install` to run rake tasks' end - -require 'rdoc/task' +begin + require 'rdoc/task' +rescue LoadError + require 'rdoc/rdoc' + require 'rake/rdoctask' + RDoc::Task = Rake::RDocTask +end RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'SwaggerRails' + rdoc.title = 'rswag-specs' rdoc.options << '--line-numbers' rdoc.rdoc_files.include('README.rdoc') rdoc.rdoc_files.include('lib/**/*.rb') @@ -18,9 +22,6 @@ end -load 'rails/tasks/statistics.rake' - - Bundler::GemHelper.install_tasks diff --git a/bin/rails b/rswag-api/bin/rails similarity index 84% rename from bin/rails rename to rswag-api/bin/rails index 4801e85..1ef582f 100755 --- a/bin/rails +++ b/rswag-api/bin/rails @@ -2,7 +2,7 @@ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application. ENGINE_ROOT = File.expand_path('../..', __FILE__) -ENGINE_PATH = File.expand_path('../../lib/swagger_rails/engine', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/rswag/api/engine', __FILE__) # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) diff --git a/rswag-api/lib/generators/rswag/api/install/USAGE b/rswag-api/lib/generators/rswag/api/install/USAGE new file mode 100644 index 0000000..87b8bc5 --- /dev/null +++ b/rswag-api/lib/generators/rswag/api/install/USAGE @@ -0,0 +1,8 @@ +Description: + Adds rswag-api initializer for configuration + +Example: + rails generate rswag:api:install + + This will create: + config/initializers/rswag-api.rb diff --git a/rswag-api/lib/generators/rswag/api/install/install_generator.rb b/rswag-api/lib/generators/rswag/api/install/install_generator.rb new file mode 100644 index 0000000..744f2b1 --- /dev/null +++ b/rswag-api/lib/generators/rswag/api/install/install_generator.rb @@ -0,0 +1,18 @@ +require 'rails/generators' + +module Rswag + module Api + + class InstallGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + def add_initializer + template('rswag-api.rb', 'config/initializers/rswag-api.rb') + end + + def add_routes + route("mount Rswag::Api::Engine => '/api-docs'") + end + end + end +end diff --git a/lib/generators/swagger_rails/install/templates/swagger_rails.rb b/rswag-api/lib/generators/rswag/api/install/templates/rswag-api.rb similarity index 74% rename from lib/generators/swagger_rails/install/templates/swagger_rails.rb rename to rswag-api/lib/generators/rswag/api/install/templates/rswag-api.rb index aa0d9ec..5f3ddc4 100644 --- a/lib/generators/swagger_rails/install/templates/swagger_rails.rb +++ b/rswag-api/lib/generators/rswag/api/install/templates/rswag-api.rb @@ -1,9 +1,9 @@ -SwaggerRails.configure do |c| +Rswag::Api.configure do |c| # Specify a root folder where Swagger JSON files are located # This is used by the Swagger middleware to serve requests for API descriptions - # NOTE: If you're using the rspec DSL to generate Swagger, you'll need to ensure - # that the same folder is also specified in spec/swagger_helper.rb + # NOTE: If you're using rswag-specs to generate Swagger, you'll need to ensure + # that it's configured to generate files in the same folder c.swagger_root = Rails.root.to_s + '/swagger' # Inject a lamda function to alter the returned Swagger prior to serialization diff --git a/rswag-api/lib/rswag/api.rb b/rswag-api/lib/rswag/api.rb new file mode 100644 index 0000000..553cb15 --- /dev/null +++ b/rswag-api/lib/rswag/api.rb @@ -0,0 +1,15 @@ +require 'rswag/api/version' +require 'rswag/api/configuration' +require 'rswag/api/engine' + +module Rswag + module Api + def self.configure + yield(config) + end + + def self.config + @config ||= Configuration.new + end + end +end diff --git a/rswag-api/lib/rswag/api/configuration.rb b/rswag-api/lib/rswag/api/configuration.rb new file mode 100644 index 0000000..ff56180 --- /dev/null +++ b/rswag-api/lib/rswag/api/configuration.rb @@ -0,0 +1,12 @@ +module Rswag + module Api + class Configuration + attr_accessor :swagger_root, :swagger_filter + + def resolve_swagger_root(env) + path_params = env['action_dispatch.request.path_parameters'] || {} + path_params[:swagger_root] || swagger_root + end + end + end +end diff --git a/rswag-api/lib/rswag/api/engine.rb b/rswag-api/lib/rswag/api/engine.rb new file mode 100644 index 0000000..893cf5f --- /dev/null +++ b/rswag-api/lib/rswag/api/engine.rb @@ -0,0 +1,13 @@ +require 'rswag/api/middleware' + +module Rswag + module Api + class Engine < ::Rails::Engine + isolate_namespace Rswag::Api + + initializer 'rswag-api.initialize' do |app| + middleware.use Rswag::Api::Middleware, Rswag::Api.config + end + end + end +end diff --git a/rswag-api/lib/rswag/api/middleware.rb b/rswag-api/lib/rswag/api/middleware.rb new file mode 100644 index 0000000..118c987 --- /dev/null +++ b/rswag-api/lib/rswag/api/middleware.rb @@ -0,0 +1,37 @@ +require 'json' + +module Rswag + module Api + class Middleware + + def initialize(app, config) + @app = app + @config = config + end + + def call(env) + path = env['PATH_INFO'] + filename = "#{@config.resolve_swagger_root(env)}/#{path}" + + if env['REQUEST_METHOD'] == 'GET' && File.file?(filename) + swagger = load_json(filename) + @config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil? + + return [ + '200', + { 'Content-Type' => 'application/json' }, + [ JSON.dump(swagger) ] + ] + end + + return @app.call(env) + end + + private + + def load_json(filename) + JSON.parse(File.read(filename)) + end + end + end +end diff --git a/rswag-api/lib/rswag/api/version.rb b/rswag-api/lib/rswag/api/version.rb new file mode 100644 index 0000000..8613ba9 --- /dev/null +++ b/rswag-api/lib/rswag/api/version.rb @@ -0,0 +1,5 @@ +module Rswag + module Api + VERSION = '1.0.0' + end +end diff --git a/rswag-api/rswag-api.gemspec b/rswag-api/rswag-api.gemspec new file mode 100644 index 0000000..6efa326 --- /dev/null +++ b/rswag-api/rswag-api.gemspec @@ -0,0 +1,18 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require 'rswag/api/version' + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "rswag-api" + s.version = Rswag::Api::VERSION + s.authors = ["Richie Morris"] + s.email = ["domaindrivendev@gmail.com"] + s.homepage = "https://github.com/domaindrivendev/rswag" + s.summary = "A Rails Engine that exposes Swagger files as JSON endpoints" + + s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ] + + s.add_dependency "rails", ">= 3.1", "< 5.1" +end diff --git a/spec/generators/swagger_rails/fixtures/spec/.gitkeep b/rswag-api/spec/generators/rswag/api/fixtures/config/initializers/.gitkeep similarity index 100% rename from spec/generators/swagger_rails/fixtures/spec/.gitkeep rename to rswag-api/spec/generators/rswag/api/fixtures/config/initializers/.gitkeep diff --git a/rswag-api/spec/generators/rswag/api/fixtures/config/routes.rb b/rswag-api/spec/generators/rswag/api/fixtures/config/routes.rb new file mode 100644 index 0000000..9d8e33d --- /dev/null +++ b/rswag-api/spec/generators/rswag/api/fixtures/config/routes.rb @@ -0,0 +1,2 @@ +TestApp::Application.routes.draw do +end diff --git a/rswag-api/spec/generators/rswag/api/install_generator_spec.rb b/rswag-api/spec/generators/rswag/api/install_generator_spec.rb new file mode 100644 index 0000000..fe2294d --- /dev/null +++ b/rswag-api/spec/generators/rswag/api/install_generator_spec.rb @@ -0,0 +1,27 @@ +require 'generator_spec' +require 'generators/rswag/api/install/install_generator' + +module Rswag + module Api + + describe InstallGenerator do + include GeneratorSpec::TestCase + destination File.expand_path('../tmp', __FILE__) + + before(:all) do + prepare_destination + fixtures_dir = File.expand_path('../fixtures', __FILE__) + FileUtils.cp_r("#{fixtures_dir}/config", destination_root) + + run_generator + end + + it 'installs the Rails initializer' do + assert_file('config/initializers/rswag-api.rb') + end + + # Don't know how to test this + #it 'wires up routes' + end + end +end diff --git a/spec/dummy/app/assets/images/.keep b/rswag-api/spec/generators/rswag/api/tmp/config/initializers/.gitkeep similarity index 100% rename from spec/dummy/app/assets/images/.keep rename to rswag-api/spec/generators/rswag/api/tmp/config/initializers/.gitkeep diff --git a/spec/dummy/config/initializers/swagger_rails.rb b/rswag-api/spec/generators/rswag/api/tmp/config/initializers/rswag-api.rb similarity index 74% rename from spec/dummy/config/initializers/swagger_rails.rb rename to rswag-api/spec/generators/rswag/api/tmp/config/initializers/rswag-api.rb index aa0d9ec..5f3ddc4 100644 --- a/spec/dummy/config/initializers/swagger_rails.rb +++ b/rswag-api/spec/generators/rswag/api/tmp/config/initializers/rswag-api.rb @@ -1,9 +1,9 @@ -SwaggerRails.configure do |c| +Rswag::Api.configure do |c| # Specify a root folder where Swagger JSON files are located # This is used by the Swagger middleware to serve requests for API descriptions - # NOTE: If you're using the rspec DSL to generate Swagger, you'll need to ensure - # that the same folder is also specified in spec/swagger_helper.rb + # NOTE: If you're using rswag-specs to generate Swagger, you'll need to ensure + # that it's configured to generate files in the same folder c.swagger_root = Rails.root.to_s + '/swagger' # Inject a lamda function to alter the returned Swagger prior to serialization diff --git a/rswag-api/spec/generators/rswag/api/tmp/config/routes.rb b/rswag-api/spec/generators/rswag/api/tmp/config/routes.rb new file mode 100644 index 0000000..4254c4d --- /dev/null +++ b/rswag-api/spec/generators/rswag/api/tmp/config/routes.rb @@ -0,0 +1,4 @@ +TestApp::Application.routes.draw do + mount Rswag::Api::Engine => '/api-docs' + +end diff --git a/rswag-api/spec/rswag/api/fixtures/config/routes.rb b/rswag-api/spec/rswag/api/fixtures/config/routes.rb new file mode 100644 index 0000000..ed6b6ed --- /dev/null +++ b/rswag-api/spec/rswag/api/fixtures/config/routes.rb @@ -0,0 +1,6 @@ +TestApp::Application.routes.draw do + resources :blogs, defaults: { :format => :json } + + mount Rswag::Api::Engine => 'api-docs' + mount Rswag::Ui::Engine => 'api-docs' +end diff --git a/rswag-api/spec/rswag/api/fixtures/swagger/v1/swagger.json b/rswag-api/spec/rswag/api/fixtures/swagger/v1/swagger.json new file mode 100644 index 0000000..11b296d --- /dev/null +++ b/rswag-api/spec/rswag/api/fixtures/swagger/v1/swagger.json @@ -0,0 +1,8 @@ +{ + "swagger": "2.0", + "info": { + "title": "API V1", + "version": "v1" + }, + "paths": {} +} diff --git a/rswag-api/spec/rswag/api/middleware_spec.rb b/rswag-api/spec/rswag/api/middleware_spec.rb new file mode 100644 index 0000000..aaa148b --- /dev/null +++ b/rswag-api/spec/rswag/api/middleware_spec.rb @@ -0,0 +1,82 @@ +require 'rswag/api/middleware' +require 'rswag/api/configuration' + +module Rswag + module Api + + describe Middleware do + let(:app) { double('app') } + let(:swagger_root) { File.expand_path('../fixtures/swagger', __FILE__) } + let(:config) do + Configuration.new.tap { |c| c.swagger_root = swagger_root } + end + + subject { described_class.new(app, config) } + + describe '#call(env)' do + let(:response) { subject.call(env) } + let(:env_defaults) do + { + 'HTTP_HOST' => 'tempuri.org', + 'REQUEST_METHOD' => 'GET', + } + end + + context 'given a path that maps to an existing swagger file' do + let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.json') } + + it 'returns a 200 status' do + expect(response.length).to eql(3) + expect(response.first).to eql('200') + end + + it 'returns contents of the swagger file' do + expect(response.length).to eql(3) + expect(response[1]).to include( 'Content-Type' => 'application/json') + expect(response[2].join).to include('"title":"API V1"') + end + end + + context "given a path that doesn't map to any swagger file" do + let(:env) { env_defaults.merge('PATH_INFO' => 'foobar.json') } + before do + allow(app).to receive(:call).and_return([ '500', {}, [] ]) + end + + it 'delegates to the next middleware' do + expect(response).to include('500') + end + end + + context 'when the env contains a specific swagger_root' do + let(:env) do + env_defaults.merge( + 'PATH_INFO' => 'v1/swagger.json', + 'action_dispatch.request.path_parameters' => { + swagger_root: swagger_root + } + ) + end + + it 'locates files at the provided swagger_root' do + expect(response.length).to eql(3) + expect(response[1]).to include( 'Content-Type' => 'application/json') + expect(response[2].join).to include('"swagger":"2.0"') + end + end + + context 'when a swagger_filter is configured' do + before do + config.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } + end + let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.json') } + + it 'applies the filter prior to serialization' do + expect(response.length).to eql(3) + expect(response[2].join).to include('"host":"tempuri.org"') + end + end + end + end + end +end diff --git a/spec/dummy/app/controllers/concerns/.keep b/rswag-api/spec/spec_helper.rb similarity index 100% rename from spec/dummy/app/controllers/concerns/.keep rename to rswag-api/spec/spec_helper.rb diff --git a/rswag-specs/.rspec b/rswag-specs/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/rswag-specs/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/rswag-specs/MIT-LICENSE b/rswag-specs/MIT-LICENSE new file mode 100644 index 0000000..f95875d --- /dev/null +++ b/rswag-specs/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright 2015 domaindrivendev + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rswag-specs/Rakefile b/rswag-specs/Rakefile new file mode 100644 index 0000000..2cbae8a --- /dev/null +++ b/rswag-specs/Rakefile @@ -0,0 +1,27 @@ +#!/usr/bin/env rake +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end +begin + require 'rdoc/task' +rescue LoadError + require 'rdoc/rdoc' + require 'rake/rdoctask' + RDoc::Task = Rake::RDocTask +end + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'rswag-specs' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.rdoc') + rdoc.rdoc_files.include('lib/**/*.rb') +end + + + + +Bundler::GemHelper.install_tasks + diff --git a/rswag-specs/lib/generators/rswag/specs/install/USAGE b/rswag-specs/lib/generators/rswag/specs/install/USAGE new file mode 100644 index 0000000..38ec3b4 --- /dev/null +++ b/rswag-specs/lib/generators/rswag/specs/install/USAGE @@ -0,0 +1,8 @@ +Description: + Adds swagger_helper to enable Swagger DSL in integration specs + +Example: + rails generate rswag:specs:install + + This will create: + spec/swagger_helper.rb diff --git a/rswag-specs/lib/generators/rswag/specs/install/install_generator.rb b/rswag-specs/lib/generators/rswag/specs/install/install_generator.rb new file mode 100644 index 0000000..050c57b --- /dev/null +++ b/rswag-specs/lib/generators/rswag/specs/install/install_generator.rb @@ -0,0 +1,14 @@ +require 'rails/generators' + +module Rswag + module Specs + + class InstallGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + def add_swagger_helper + template('swagger_helper.rb', 'spec/swagger_helper.rb') + end + end + end +end diff --git a/spec/dummy/spec/swagger_helper.rb b/rswag-specs/lib/generators/rswag/specs/install/templates/swagger_helper.rb similarity index 55% rename from spec/dummy/spec/swagger_helper.rb rename to rswag-specs/lib/generators/rswag/specs/install/templates/swagger_helper.rb index 26523b3..366d13e 100644 --- a/spec/dummy/spec/swagger_helper.rb +++ b/rswag-specs/lib/generators/rswag/specs/install/templates/swagger_helper.rb @@ -1,20 +1,14 @@ require 'rails_helper' -require 'swagger_rails/rspec/dsl' RSpec.configure do |config| - # NOTE: Should be no need to modify these 3 lines - config.add_setting :swagger_root - config.add_setting :swagger_docs - config.extend SwaggerRails::RSpec::DSL - # Specify a root folder where Swagger JSON files are generated - # NOTE: If you're using the Swagger JSON middleware to serve API descriptions, you'll need - # to ensure that the same folder is also specified in the swagger_rails initializer + # NOTE: If you're using the rswag-api to serve API descriptions, you'll need + # to ensure that it's confiugred to server Swagger from the same folder config.swagger_root = Rails.root.to_s + '/swagger' # Define one or more Swagger documents and provide global metadata for each one - # When you run the "swaggerize" rake task, the complete Swagger will be generated - # at the provided relative path under swagger_root + # When you run the 'rswag:specs:to_swagger' rake task, the complete Swagger will + # be generated at the provided relative path under swagger_root # By default, the operations defined in spec files are added to the first # document below. You can override this behavior by adding a swagger_doc tag to the # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' @@ -24,7 +18,8 @@ RSpec.configure do |config| info: { title: 'API V1', version: 'v1' - } + }, + paths: {} } } end diff --git a/rswag-specs/lib/rswag/specs.rb b/rswag-specs/lib/rswag/specs.rb new file mode 100644 index 0000000..5d04e97 --- /dev/null +++ b/rswag-specs/lib/rswag/specs.rb @@ -0,0 +1,22 @@ +require 'rspec/core' +require 'rswag/specs/version' +require 'rswag/specs/example_group_helpers' +require 'rswag/specs/example_helpers' +require 'rswag/specs/railtie' if defined?(Rails::Railtie) + +module Rswag + module Specs + + # Extend RSpec with a swagger-based DSL + ::RSpec.configure do |c| + c.add_setting :swagger_root + c.add_setting :swagger_docs + c.extend ExampleGroupHelpers, type: :request + c.include ExampleHelpers, type: :request + end + + # Support Rails 3+ and RSpec 2+ (sigh!) + RAILS_VERSION = Rails::VERSION::MAJOR + RSPEC_VERSION = RSpec::Core::Version::STRING.split('.').first.to_i + end +end diff --git a/rswag-specs/lib/rswag/specs/example_group_helpers.rb b/rswag-specs/lib/rswag/specs/example_group_helpers.rb new file mode 100644 index 0000000..b09420d --- /dev/null +++ b/rswag-specs/lib/rswag/specs/example_group_helpers.rb @@ -0,0 +1,80 @@ +module Rswag + module Specs + module ExampleGroupHelpers + + def path(path, &block) + api_metadata = { path: path} + describe(path, api_metadata, &block) + end + + [ :get, :post, :patch, :put, :delete, :head ].each do |verb| + define_method(verb) do |summary, &block| + api_metadata = { operation: { verb: verb, summary: summary } } + describe(verb, api_metadata, &block) + end + end + + [ :operationId, :deprecated, :security ].each do |attr_name| + define_method(attr_name) do |value| + metadata[:operation][attr_name] = value + end + end + + # NOTE: 'description' requires special treatment because ExampleGroup already + # defines a method with that name. Provide an override that supports the existing + # functionality while also setting the appropriate metadata if applicable + def description(value=nil) + return super() if value.nil? + metadata[:operation][:description] = value + end + + # These are array properties - note the splat operator + [ :tags, :consumes, :produces, :schemes ].each do |attr_name| + define_method(attr_name) do |*value| + metadata[:operation][attr_name] = value + end + end + + def parameter(attributes) + attributes[:required] = true if attributes[:in].to_sym == :path + metadata[:operation][:parameters] ||= [] + metadata[:operation][:parameters] << attributes + end + + def response(code, description, &block) + api_metadata = { response: { code: code, description: description } } + context(description, api_metadata, &block) + end + + def schema(value) + metadata[:response][:schema] = value + end + + def header(name, attributes) + metadata[:response][:headers] ||= {} + metadata[:response][:headers][name] = attributes + end + + def run_test! + # NOTE: rspec 2.x support + if RSPEC_VERSION < 3 + before do + submit_request(example.metadata) + end + + it "returns a #{metadata[:response][:code]} response" do + assert_response_matches_metadata(example.metadata) + end + else + before do |example| + submit_request(example.metadata) + end + + it "returns a #{metadata[:response][:code]} response" do |example| + assert_response_matches_metadata(example.metadata) + end + end + end + end + end +end diff --git a/rswag-specs/lib/rswag/specs/example_helpers.rb b/rswag-specs/lib/rswag/specs/example_helpers.rb new file mode 100644 index 0000000..dbfd302 --- /dev/null +++ b/rswag-specs/lib/rswag/specs/example_helpers.rb @@ -0,0 +1,43 @@ +require 'rswag/specs/request_factory' +require 'rswag/specs/response_validator' + +module Rswag + module Specs + module ExampleHelpers + + def submit_request(api_metadata) + factory = RequestFactory.new(api_metadata, global_metadata(api_metadata[:swagger_doc])) + + if RAILS_VERSION < 5 + send( + api_metadata[:operation][:verb], + factory.build_fullpath(self), + factory.build_body(self), + factory.build_headers(self) + ) + else + send( + api_metadata[:operation][:verb], + factory.build_fullpath(self), + { + params: factory.build_body(self), + headers: factory.build_headers(self) + } + ) + end + end + + def assert_response_matches_metadata(api_metadata) + validator = ResponseValidator.new(api_metadata, global_metadata(api_metadata[:swagger_doc])) + validator.validate!(response) + end + + private + + def global_metadata(swagger_doc) + swagger_docs = ::RSpec.configuration.swagger_docs + swagger_doc.nil? ? swagger_docs.values.first : swagger_docs[swagger_doc] + end + end + end +end diff --git a/rswag-specs/lib/rswag/specs/railtie.rb b/rswag-specs/lib/rswag/specs/railtie.rb new file mode 100644 index 0000000..8deec2b --- /dev/null +++ b/rswag-specs/lib/rswag/specs/railtie.rb @@ -0,0 +1,10 @@ +module Rswag + module Specs + class Railtie < ::Rails::Railtie + + rake_tasks do + load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__) + end + end + end +end diff --git a/rswag-specs/lib/rswag/specs/request_factory.rb b/rswag-specs/lib/rswag/specs/request_factory.rb new file mode 100644 index 0000000..50dcc2a --- /dev/null +++ b/rswag-specs/lib/rswag/specs/request_factory.rb @@ -0,0 +1,70 @@ +require 'active_support/core_ext/hash/slice' +require 'json' + +module Rswag + module Specs + class RequestFactory + + def initialize(api_metadata, global_metadata) + @api_metadata = api_metadata + @global_metadata = global_metadata + end + + def build_fullpath(example) + @api_metadata[:path].dup.tap do |t| + parameters_in(:path).each { |p| t.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s) } + t.concat(build_query(example)) + t.prepend(@global_metadata[:basePath] || '') + end + end + + def build_query(example) + query_string = parameters_in(:query) + .map { |p| "#{p[:name]}=#{example.send(p[:name])}" } + .join('&') + + query_string.empty? ? '' : "?#{query_string}" + end + + def build_body(example) + body_parameter = parameters_in(:body).first + body_parameter.nil? ? '' : example.send(body_parameter[:name]).to_json + end + + def build_headers(example) + headers = Hash[ parameters_in(:header).map { |p| [ p[:name], example.send(p[:name]).to_s ] } ] + headers.tap do |h| + produces = @api_metadata[:operation][:produces] || @global_metadata[:produces] + consumes = @api_metadata[:operation][:consumes] || @global_metadata[:consumes] + h['ACCEPT'] = produces.join(';') unless produces.nil? + h['CONTENT_TYPE'] = consumes.join(';') unless consumes.nil? + end + end + + private + + def parameters_in(location) + (@api_metadata[:operation][:parameters] || []) + .map { |p| p['$ref'] ? resolve_parameter(p['$ref']) : p } # resolve any references + .concat(resolve_api_key_parameters) + .select { |p| p[:in] == location } + end + + def resolve_parameter(ref) + defined_params = @global_metadata[:parameters] + key = ref.sub('#/parameters/', '') + raise "Referenced parameter '#{ref}' must be defined" unless defined_params && defined_params[key] + defined_params[key] + end + + def resolve_api_key_parameters + @api_key_params ||= begin + global_requirements = (@global_metadata[:security] || {}) + requirements = global_requirements.merge(@api_metadata[:operation][:security] || {}) + definitions = (@global_metadata[:securityDefinitions] || {}).slice(*requirements.keys) + definitions.values.select { |d| d[:type] == :apiKey } + end + end + end + end +end diff --git a/rswag-specs/lib/rswag/specs/response_validator.rb b/rswag-specs/lib/rswag/specs/response_validator.rb new file mode 100644 index 0000000..febedbf --- /dev/null +++ b/rswag-specs/lib/rswag/specs/response_validator.rb @@ -0,0 +1,38 @@ +require 'json-schema' + +module Rswag + module Specs + class ResponseValidator + + def initialize(api_metadata, global_metadata) + @api_metadata = api_metadata + @global_metadata = global_metadata + end + + def validate!(response) + validate_code!(response.code) + validate_body!(response.body) + end + + private + + def validate_code!(code) + if code.to_s != @api_metadata[:response][:code].to_s + raise UnexpectedResponse, "Expected response code '#{code}' to match '#{@api_metadata[:response][:code]}'" + end + end + + def validate_body!(body) + schema = @api_metadata[:response][:schema] + return if schema.nil? + begin + JSON::Validator.validate!(schema.merge(@global_metadata), body) + rescue JSON::Schema::ValidationError => ex + raise UnexpectedResponse, "Expected response body to match schema: #{ex.message}" + end + end + end + + class UnexpectedResponse < StandardError; end + end +end diff --git a/rswag-specs/lib/rswag/specs/swagger_formatter.rb b/rswag-specs/lib/rswag/specs/swagger_formatter.rb new file mode 100644 index 0000000..2df6262 --- /dev/null +++ b/rswag-specs/lib/rswag/specs/swagger_formatter.rb @@ -0,0 +1,79 @@ +require 'active_support/core_ext/hash/deep_merge' +require 'rspec/core/formatters/base_text_formatter' +require 'swagger_helper' + +module Rswag + module Specs + class SwaggerFormatter + + # NOTE: rspec 2.x support + if RSPEC_VERSION > 2 + ::RSpec::Core::Formatters.register self, :example_group_finished, :stop + end + + def initialize(output) + @output = output + @swagger_root = ::RSpec.configuration.swagger_root + raise ConfigurationError, 'Missing swagger_root. See swagger_helper.rb' if @swagger_root.nil? + @swagger_docs = ::RSpec.configuration.swagger_docs || [] + raise ConfigurationError, 'Missing swagger_docs. See swagger_helper.rb' if @swagger_docs.empty? + + @output.puts 'Generating Swagger docs ...' + end + + def example_group_finished(notification) + # NOTE: rspec 2.x support + if RSPEC_VERSION > 2 + metadata = notification.group.metadata + else + metadata = notification.metadata + end + + return unless metadata.has_key?(:response) + swagger_doc = get_swagger_doc(metadata[:swagger_doc]) + swagger_doc.deep_merge!(metadata_to_swagger(metadata)) + end + + def stop(notification=nil) + @swagger_docs.each do |url_path, doc| + file_path = File.join(@swagger_root, url_path) + dirname = File.dirname(file_path) + FileUtils.mkdir_p dirname unless File.exists?(dirname) + + File.open(file_path, 'w') do |file| + file.write(JSON.pretty_generate(doc)) + end + + @output.puts "Swagger doc generated at #{file_path}" + end + end + + private + + def get_swagger_doc(tag) + return @swagger_docs.values.first if tag.nil? + raise ConfigurationError, "Unknown swagger_doc '#{tag}'" unless @swagger_docs.has_key?(tag) + @swagger_docs[tag] + end + + def metadata_to_swagger(metadata) + response_code = metadata[:response][:code] + response = metadata[:response].reject { |k,v| k == :code } + verb = metadata[:operation][:verb] + operation = metadata[:operation] + .reject { |k,v| k == :verb } + .merge(responses: { response_code => response }) + + { + paths: { + metadata[:path] => { + verb => operation + } + } + } + end + end + + class ConfigurationError < StandardError; end + end +end diff --git a/rswag-specs/lib/rswag/specs/version.rb b/rswag-specs/lib/rswag/specs/version.rb new file mode 100644 index 0000000..9bdc1b5 --- /dev/null +++ b/rswag-specs/lib/rswag/specs/version.rb @@ -0,0 +1,5 @@ +module Rswag + module Specs + VERSION = '1.0.0' + end +end diff --git a/rswag-specs/lib/tasks/rswag-specs_tasks.rake b/rswag-specs/lib/tasks/rswag-specs_tasks.rake new file mode 100644 index 0000000..646af02 --- /dev/null +++ b/rswag-specs/lib/tasks/rswag-specs_tasks.rake @@ -0,0 +1,18 @@ +require 'rspec/core/rake_task' + +namespace :rswag do + namespace :specs do + + desc 'Generate Swagger JSON files from integration specs' + RSpec::Core::RakeTask.new('swaggerize') do |t| + t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb' + + # NOTE: rspec 2.x support + if Rswag::Specs::RSPEC_VERSION > 2 + t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--dry-run', '--order defined' ] + else + t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--order defined' ] + end + end + end +end diff --git a/rswag-specs/rswag-specs.gemspec b/rswag-specs/rswag-specs.gemspec new file mode 100644 index 0000000..ab80fb3 --- /dev/null +++ b/rswag-specs/rswag-specs.gemspec @@ -0,0 +1,20 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require 'rswag/specs/version' + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "rswag-specs" + s.version = Rswag::Specs::VERSION + s.authors = ["Richie Morris"] + s.email = ["domaindrivendev@gmail.com"] + s.homepage = "https://github.com/domaindrivendev/rswag" + s.summary = "A Swagger-based DSL for rspec-rails & accompanying rake task for generating Swagger files" + + s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ] + + s.add_dependency "rails", ">= 3.1", "< 5.1" + s.add_dependency 'json-schema' + s.add_development_dependency 'rspec-rails' +end diff --git a/spec/dummy/app/mailers/.keep b/rswag-specs/spec/generators/rswag/specs/fixtures/spec/.gitkeep similarity index 100% rename from spec/dummy/app/mailers/.keep rename to rswag-specs/spec/generators/rswag/specs/fixtures/spec/.gitkeep diff --git a/rswag-specs/spec/generators/rswag/specs/install_generator_spec.rb b/rswag-specs/spec/generators/rswag/specs/install_generator_spec.rb new file mode 100644 index 0000000..39dc6fe --- /dev/null +++ b/rswag-specs/spec/generators/rswag/specs/install_generator_spec.rb @@ -0,0 +1,24 @@ +require 'generator_spec' +require 'generators/rswag/specs/install/install_generator' + +module Rswag + module Specs + + describe InstallGenerator do + include GeneratorSpec::TestCase + destination File.expand_path('../tmp', __FILE__) + + before(:all) do + prepare_destination + fixtures_dir = File.expand_path('../fixtures', __FILE__) + FileUtils.cp_r("#{fixtures_dir}/spec", destination_root) + + run_generator + end + + it 'installs the swagger_helper for rspec' do + assert_file('spec/swagger_helper.rb') + end + end + end +end diff --git a/spec/dummy/app/models/.keep b/rswag-specs/spec/generators/rswag/specs/tmp/spec/.gitkeep similarity index 100% rename from spec/dummy/app/models/.keep rename to rswag-specs/spec/generators/rswag/specs/tmp/spec/.gitkeep diff --git a/spec/swagger_helper.rb b/rswag-specs/spec/generators/rswag/specs/tmp/spec/swagger_helper.rb similarity index 55% rename from spec/swagger_helper.rb rename to rswag-specs/spec/generators/rswag/specs/tmp/spec/swagger_helper.rb index 26523b3..366d13e 100644 --- a/spec/swagger_helper.rb +++ b/rswag-specs/spec/generators/rswag/specs/tmp/spec/swagger_helper.rb @@ -1,20 +1,14 @@ require 'rails_helper' -require 'swagger_rails/rspec/dsl' RSpec.configure do |config| - # NOTE: Should be no need to modify these 3 lines - config.add_setting :swagger_root - config.add_setting :swagger_docs - config.extend SwaggerRails::RSpec::DSL - # Specify a root folder where Swagger JSON files are generated - # NOTE: If you're using the Swagger JSON middleware to serve API descriptions, you'll need - # to ensure that the same folder is also specified in the swagger_rails initializer + # NOTE: If you're using the rswag-api to serve API descriptions, you'll need + # to ensure that it's confiugred to server Swagger from the same folder config.swagger_root = Rails.root.to_s + '/swagger' # Define one or more Swagger documents and provide global metadata for each one - # When you run the "swaggerize" rake task, the complete Swagger will be generated - # at the provided relative path under swagger_root + # When you run the 'rswag:specs:to_swagger' rake task, the complete Swagger will + # be generated at the provided relative path under swagger_root # By default, the operations defined in spec files are added to the first # document below. You can override this behavior by adding a swagger_doc tag to the # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' @@ -24,7 +18,8 @@ RSpec.configure do |config| info: { title: 'API V1', version: 'v1' - } + }, + paths: {} } } end diff --git a/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb b/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb new file mode 100644 index 0000000..e8eecdf --- /dev/null +++ b/rswag-specs/spec/rswag/specs/example_group_helpers_spec.rb @@ -0,0 +1,143 @@ +require 'rswag/specs/example_group_helpers' + +module Rswag + module Specs + + describe ExampleGroupHelpers do + subject { double('example_group') } + let(:api_metadata) { {} } + before do + subject.extend ExampleGroupHelpers + allow(subject).to receive(:describe) + allow(subject).to receive(:context) + allow(subject).to receive(:metadata).and_return(api_metadata) + end + + describe '#path(path)' do + before { subject.path('/blogs') } + + it "delegates to 'describe' with 'path' metadata" do + expect(subject).to have_received(:describe).with( + '/blogs', path: '/blogs' + ) + end + end + + describe '#get|post|patch|put|delete|head(verb, summary)' do + before { subject.post('Creates a blog') } + + it "delegates to 'describe' with 'operation' metadata" do + expect(subject).to have_received(:describe).with( + :post, operation: { verb: :post, summary: 'Creates a blog' } + ) + end + end + + describe '#tags|description|operationId|consumes|produces|schemes|deprecated(value)' do + let(:api_metadata) { { operation: {} } } + before do + subject.tags('Blogs', 'Admin') + subject.description('Some description') + subject.operationId('createBlog') + subject.consumes('application/json', 'application/xml') + subject.produces('application/json', 'application/xml') + subject.schemes('http', 'https') + subject.deprecated(true) + end + + it "adds to the 'operation' metadata" do + expect(api_metadata[:operation]).to match( + tags: [ 'Blogs', 'Admin' ], + description: 'Some description', + operationId: 'createBlog', + consumes: [ 'application/json', 'application/xml' ], + produces: [ 'application/json', 'application/xml' ], + schemes: [ 'http', 'https' ], + deprecated: true + ) + end + end + + describe '#tags|description|operationId|consumes|produces|schemes|deprecated|security(value)' do + let(:api_metadata) { { operation: {} } } + before do + subject.tags('Blogs', 'Admin') + subject.description('Some description') + subject.operationId('createBlog') + subject.consumes('application/json', 'application/xml') + subject.produces('application/json', 'application/xml') + subject.schemes('http', 'https') + subject.deprecated(true) + subject.security(api_key: []) + end + + it "adds to the 'operation' metadata" do + expect(api_metadata[:operation]).to match( + tags: [ 'Blogs', 'Admin' ], + description: 'Some description', + operationId: 'createBlog', + consumes: [ 'application/json', 'application/xml' ], + produces: [ 'application/json', 'application/xml' ], + schemes: [ 'http', 'https' ], + deprecated: true, + security: { api_key: [] } + ) + end + end + + describe '#parameter(attributes)' do + let(:api_metadata) { { operation: {} } } + + context 'always' do + before { subject.parameter(name: :blog, in: :body, schema: { type: 'object' }) } + + it "adds to the 'operation parameters' metadata" do + expect(api_metadata[:operation][:parameters]).to match( + [ name: :blog, in: :body, schema: { type: 'object' } ] + ) + end + end + + context "'path' parameter" do + before { subject.parameter(name: :id, in: :path) } + + it "automatically sets the 'required' flag" do + expect(api_metadata[:operation][:parameters]).to match( + [ name: :id, in: :path, required: true ] + ) + end + end + end + + describe '#response(code, description)' do + before { subject.response('201', 'success') } + + it "delegates to 'context' with 'response' metadata" do + expect(subject).to have_received(:context).with( + 'success', response: { code: '201', description: 'success' } + ) + end + end + + describe '#schema(value)' do + let(:api_metadata) { { response: {} } } + before { subject.schema(type: 'object') } + + it "adds to the 'response' metadata" do + expect(api_metadata[:response][:schema]).to match(type: 'object') + end + end + + describe '#header(name, attributes)' do + let(:api_metadata) { { response: {} } } + before { subject.header('Date', type: 'string') } + + it "adds to the 'response headers' metadata" do + expect(api_metadata[:response][:headers]).to match( + 'Date' => { type: 'string' } + ) + end + end + end + end +end diff --git a/rswag-specs/spec/rswag/specs/example_helpers_spec.rb b/rswag-specs/spec/rswag/specs/example_helpers_spec.rb new file mode 100644 index 0000000..4c02a1f --- /dev/null +++ b/rswag-specs/spec/rswag/specs/example_helpers_spec.rb @@ -0,0 +1,66 @@ +require 'rswag/specs/example_helpers' + +module Rswag + module Specs + + describe ExampleHelpers do + let(:api_metadata) do + { + path: '/blogs/{blog_id}/comments/{id}', + operation: { + verb: :put, + summary: 'Updates a blog', + parameters: [ + { name: :blog_id, in: :path, type: 'integer' }, + { name: 'id', in: :path, type: 'integer' }, + { name: 'q1', in: :query, type: 'string' }, + { name: :blog, in: :body, schema: { type: 'object' } } + ], + security: { + api_key: [] + } + } + } + end + let(:global_metadata) do + { + securityDefinitions: { + api_key: { + type: :apiKey, + name: 'api_key', + in: :query + } + } + } + end + + subject { double('example') } + + before do + subject.extend ExampleHelpers + allow(subject).to receive(:blog_id).and_return(1) + allow(subject).to receive(:id).and_return(2) + allow(subject).to receive(:q1).and_return('foo') + allow(subject).to receive(:api_key).and_return('fookey') + allow(subject).to receive(:blog).and_return(text: 'Some comment') + allow(subject).to receive(:global_metadata).and_return(global_metadata) + allow(subject).to receive(:put) + end + + describe '#submit_request(api_metadata)' do + before do + stub_const('Rails::VERSION::MAJOR', 3) + subject.submit_request(api_metadata) + end + + it "submits a request built from metadata and 'let' values" do + expect(subject).to have_received(:put).with( + '/blogs/1/comments/2?q1=foo&api_key=fookey', + "{\"text\":\"Some comment\"}", + {} + ) + end + end + end + end +end diff --git a/rswag-specs/spec/rswag/specs/request_factory_spec.rb b/rswag-specs/spec/rswag/specs/request_factory_spec.rb new file mode 100644 index 0000000..61a9f1f --- /dev/null +++ b/rswag-specs/spec/rswag/specs/request_factory_spec.rb @@ -0,0 +1,171 @@ +require 'rswag/specs/request_factory' + +module Rswag + module Specs + + describe RequestFactory do + let(:api_metadata) do + { + path: '/blogs/{blog_id}/comments/{id}', + operation: { + verb: :put, + summary: 'Updates a blog', + parameters: [ + { name: :blog_id, in: :path, type: 'integer' }, + { name: 'id', in: :path, type: 'integer' } + ] + } + } + end + let(:global_metadata) { {} } + + subject { RequestFactory.new(api_metadata, global_metadata) } + + let(:example) { double('example') } + before do + allow(example).to receive(:blog_id).and_return(1) + allow(example).to receive(:id).and_return('2') + end + + describe '#build_fullpath(example)' do + let(:path) { subject.build_fullpath(example) } + + context 'always' do + it "builds a path using metadata and example values" do + expect(path).to eq('/blogs/1/comments/2') + end + end + + context "'query' parameters" do + before do + api_metadata[:operation][:parameters] << { name: 'q1', in: :query, type: 'string' } + api_metadata[:operation][:parameters] << { name: 'q2', in: :query, type: 'string' } + allow(example).to receive(:q1).and_return('foo') + allow(example).to receive(:q2).and_return('bar') + end + + it "appends a query string using metadata and example values" do + expect(path).to eq('/blogs/1/comments/2?q1=foo&q2=bar') + end + end + + context "global definition for 'api_key in query'" do + before do + global_metadata[:securityDefinitions] = { api_key: { type: :apiKey, name: 'api_key', in: :query } } + allow(example).to receive(:api_key).and_return('fookey') + end + + context 'global requirement' do + before { global_metadata[:security] = { api_key: [] } } + + it "appends the api_key using metadata and example value" do + expect(path).to eq('/blogs/1/comments/2?api_key=fookey') + end + end + + context 'operation-specific requirement' do + before { api_metadata[:operation][:security] = { api_key: [] } } + + it "appends the api_key using metadata and example value" do + expect(path).to eq('/blogs/1/comments/2?api_key=fookey') + end + end + end + + context 'global basePath' do + before { global_metadata[:basePath] = '/foobar' } + + it 'prepends the basePath' do + expect(path).to eq('/foobar/blogs/1/comments/2') + end + end + end + + describe '#build_body(example)' do + let(:body) { subject.build_body(example) } + + context "no 'body' parameter" do + it "returns ''" do + expect(body).to eq('') + end + end + + context "'body' parameter" do + before do + api_metadata[:operation][:parameters] << { name: 'comment', in: :body, schema: { type: 'object' } } + allow(example).to receive(:comment).and_return(text: 'Some comment') + end + + it 'returns the example value as a json string' do + expect(body).to eq("{\"text\":\"Some comment\"}") + end + end + + context "referenced 'body' parameter" do + before do + api_metadata[:operation][:parameters] << { '$ref' => '#/parameters/comment' } + global_metadata[:parameters] = { + 'comment' => { name: 'comment', in: :body, schema: { type: 'object' } } + } + allow(example).to receive(:comment).and_return(text: 'Some comment') + end + + it 'returns the example value as a json string' do + expect(body).to eq("{\"text\":\"Some comment\"}") + end + end + end + + describe '#build_headers' do + let(:headers) { subject.build_headers(example) } + + context "no 'header' params" do + it 'returns an empty hash' do + expect(headers).to eq({}) + end + end + + context "'header' params" do + before do + api_metadata[:operation][:parameters] << { name: 'Api-Key', in: :header, type: 'string' } + allow(example).to receive(:'Api-Key').and_return('foobar') + end + + it 'returns a hash of names with example values' do + expect(headers).to eq({ 'Api-Key' => 'foobar' }) + end + end + + context 'consumes & produces' do + before do + api_metadata[:operation][:consumes] = [ 'application/json', 'application/xml' ] + api_metadata[:operation][:produces] = [ 'application/json', 'application/xml' ] + end + + it "includes corresponding 'Accept' & 'Content-Type' headers" do + expect(headers).to match( + 'ACCEPT' => 'application/json;application/xml', + 'CONTENT_TYPE' => 'application/json;application/xml' + ) + end + end + + context 'global consumes & produces' do + let(:global_metadata) do + { + consumes: [ 'application/json', 'application/xml' ], + produces: [ 'application/json', 'application/xml' ] + } + end + + it "includes corresponding 'Accept' & 'Content-Type' headers" do + expect(headers).to match( + 'ACCEPT' => 'application/json;application/xml', + 'CONTENT_TYPE' => 'application/json;application/xml' + ) + end + end + end + end + end +end diff --git a/rswag-specs/spec/rswag/specs/response_validator_spec.rb b/rswag-specs/spec/rswag/specs/response_validator_spec.rb new file mode 100644 index 0000000..fcafea8 --- /dev/null +++ b/rswag-specs/spec/rswag/specs/response_validator_spec.rb @@ -0,0 +1,72 @@ +require 'rswag/specs/response_validator' + +module Rswag + module Specs + + describe ResponseValidator do + let(:api_metadata) { { response: { code: 200 } } } + let(:global_metadata) { {} } + + subject { ResponseValidator.new(api_metadata, global_metadata) } + + describe '#validate!(response)' do + let(:call) { subject.validate!(response) } + + context "no 'schema' provided" do + context 'response code matches' do + let(:response) { OpenStruct.new(code: 200, body: '') } + it { expect { call }.to_not raise_error } + end + + context 'response code does not match' do + let(:response) { OpenStruct.new(code: 201, body: '') } + it { expect { call }.to raise_error UnexpectedResponse } + end + end + + context "'schema' provided" do + before do + api_metadata[:response][:schema] = { + type: 'object', + properties: { text: { type: 'string' } }, + required: [ 'text' ] + } + end + + context 'response code & body matches' do + let(:response) { OpenStruct.new(code: 200, body: "{\"text\":\"Some comment\"}") } + it { expect { call }.to_not raise_error } + end + + context 'response code matches & body does not' do + let(:response) { OpenStruct.new(code: 200, body: "{\"foo\":\"Some comment\"}") } + it { expect { call }.to raise_error UnexpectedResponse } + end + end + + context "referenced 'schema' provided" do + before do + api_metadata[:response][:schema] = { '$ref' => '#/definitions/author' } + global_metadata[:definitions] = { + author: { + type: 'object', + properties: { name: { type: 'string' } }, + required: [ 'name' ] + } + } + end + + context 'response code & body matches' do + let(:response) { OpenStruct.new(code: 200, body: "{\"name\":\"Some name\"}") } + it { expect { call }.to_not raise_error } + end + + context 'response code matches & body does not' do + let(:response) { OpenStruct.new(code: 200, body: "{\"foo\":\"Some name\"}") } + it { expect { call }.to raise_error UnexpectedResponse } + end + end + end + end + end +end diff --git a/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb b/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb new file mode 100644 index 0000000..56f3964 --- /dev/null +++ b/rswag-specs/spec/rswag/specs/swagger_formatter_spec.rb @@ -0,0 +1,125 @@ +require 'rswag/specs/swagger_formatter' +require 'ostruct' + +module Rswag + module Specs + + describe SwaggerFormatter do + # Mock infrastructure - output, RSpec.configuration etc. + let(:output) { double('output').as_null_object } + let(:swagger_root) { File.expand_path('../tmp', __FILE__) } + let(:swagger_docs) do + { + 'v1/swagger.json' => { info: { version: 'v1' } } + } + end + let(:config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) } + before { allow(RSpec).to receive(:configuration).and_return(config) } + + subject { described_class.new(output) } + + describe '::new(output)' do + context 'swagger_root not configured' do + let(:swagger_root) { nil } + it { expect { subject }.to raise_error ConfigurationError } + end + + context 'swagger_docs not configured' do + let(:swagger_docs) { nil } + it { expect { subject }.to raise_error ConfigurationError } + end + end + + describe '#example_group_finished(notification)' do + # Mock notification parameter + let(:api_metadata) do + { + path: '/blogs', + operation: { verb: :post, summary: 'Creates a blog' }, + response: { code: '201', description: 'blog created' } + } + end + let(:notification) { OpenStruct.new(group: OpenStruct.new(metadata: api_metadata)) } + + let(:call) { subject.example_group_finished(notification) } + + context 'single swagger_doc' do + before { call } + + it 'converts metadata to swagger and merges into the doc' do + expect(swagger_docs.values.first).to match( + info: { version: 'v1' }, + paths: { + '/blogs' => { + post: { + summary: 'Creates a blog', + responses: { + '201' => { description: 'blog created' } + } + } + } + } + ) + end + end + + context 'multiple swagger_docs' do + let(:swagger_docs) do + { + 'v1/swagger.json' => {}, + 'v2/swagger.json' => {} + } + end + + context "no 'swagger_doc' tag" do + before { call } + + it 'merges into the first doc' do + expect(swagger_docs.values.first).to have_key(:paths) + end + end + + context "matching 'swagger_doc' tag" do + before do + api_metadata[:swagger_doc] = 'v2/swagger.json' + call + end + + it 'merges into the matched doc' do + expect(swagger_docs.values.last).to have_key(:paths) + end + end + + context "non matching 'swagger_doc' tag" do + before { api_metadata[:swagger_doc] = 'foobar' } + it { expect { call }.to raise_error ConfigurationError } + end + end + end + + describe '#stop' do + let(:notification) { double('notification') } + let(:swagger_docs) do + { + 'v1/swagger.json' => { info: { version: 'v1' } }, + 'v2/swagger.json' => { info: { version: 'v2' } }, + } + end + + before do + FileUtils.rm_r(swagger_root) if File.exists?(swagger_root) + subject.stop(notification) + end + + it 'writes the swagger_doc(s) to file' do + expect(File).to exist("#{swagger_root}/v1/swagger.json") + expect(File).to exist("#{swagger_root}/v2/swagger.json") + end + + after do + FileUtils.rm_r(swagger_root) if File.exists?(swagger_root) + end + end + end + end +end diff --git a/rswag-specs/spec/spec_helper.rb b/rswag-specs/spec/spec_helper.rb new file mode 100644 index 0000000..63504e1 --- /dev/null +++ b/rswag-specs/spec/spec_helper.rb @@ -0,0 +1,7 @@ +module Rails + module VERSION + MAJOR = 3 + end +end + +require 'rswag/specs' diff --git a/rswag-specs/spec/swagger_helper.rb b/rswag-specs/spec/swagger_helper.rb new file mode 100644 index 0000000..330a7a4 --- /dev/null +++ b/rswag-specs/spec/swagger_helper.rb @@ -0,0 +1,2 @@ +# NOTE: For the specs in this gem, all configuration is completely mocked out +# The file just needs to be present because it gets required by the swagger_formatter diff --git a/rswag-ui/.rspec b/rswag-ui/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/rswag-ui/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/rswag-ui/MIT-LICENSE b/rswag-ui/MIT-LICENSE new file mode 100644 index 0000000..f95875d --- /dev/null +++ b/rswag-ui/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright 2015 domaindrivendev + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rswag-ui/Rakefile b/rswag-ui/Rakefile new file mode 100644 index 0000000..2cbae8a --- /dev/null +++ b/rswag-ui/Rakefile @@ -0,0 +1,27 @@ +#!/usr/bin/env rake +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end +begin + require 'rdoc/task' +rescue LoadError + require 'rdoc/rdoc' + require 'rake/rdoctask' + RDoc::Task = Rake::RDocTask +end + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'rswag-specs' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.rdoc') + rdoc.rdoc_files.include('lib/**/*.rb') +end + + + + +Bundler::GemHelper.install_tasks + diff --git a/rswag-ui/app/controllers/rswag/ui/home_controller.rb b/rswag-ui/app/controllers/rswag/ui/home_controller.rb new file mode 100644 index 0000000..f55a945 --- /dev/null +++ b/rswag-ui/app/controllers/rswag/ui/home_controller.rb @@ -0,0 +1,11 @@ +module Rswag + module Ui + class HomeController < ActionController::Base + + def index + @swagger_endpoints = Rswag::Ui.config.swagger_endpoints + render :index, layout: false + end + end + end +end diff --git a/app/views/swagger_rails/swagger_ui/index.html.erb b/rswag-ui/app/views/rswag/ui/home/index.html.erb similarity index 52% rename from app/views/swagger_rails/swagger_ui/index.html.erb rename to rswag-ui/app/views/rswag/ui/home/index.html.erb index 491bffa..d829bb2 100644 --- a/app/views/swagger_rails/swagger_ui/index.html.erb +++ b/rswag-ui/app/views/rswag/ui/home/index.html.erb @@ -10,15 +10,19 @@ + + - - + + - - + + + + @@ -29,75 +33,43 @@ - - +
+
+ + + diff --git a/rswag-ui/bin/rails b/rswag-ui/bin/rails new file mode 100755 index 0000000..1ef582f --- /dev/null +++ b/rswag-ui/bin/rails @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/rswag/api/engine', __FILE__) + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/rswag-ui/config/routes.rb b/rswag-ui/config/routes.rb new file mode 100644 index 0000000..813a0c1 --- /dev/null +++ b/rswag-ui/config/routes.rb @@ -0,0 +1,3 @@ +Rswag::Ui::Engine.routes.draw do + root to: 'home#index' +end diff --git a/lib/generators/swagger_rails/custom_ui/USAGE b/rswag-ui/lib/generators/rswag/ui/custom/USAGE similarity index 54% rename from lib/generators/swagger_rails/custom_ui/USAGE rename to rswag-ui/lib/generators/rswag/ui/custom/USAGE index 472b6d9..170beed 100644 --- a/lib/generators/swagger_rails/custom_ui/USAGE +++ b/rswag-ui/lib/generators/rswag/ui/custom/USAGE @@ -2,7 +2,7 @@ Description: Adds a local version of index.html.erb for customizing the swagger-ui Example: - rails generate swagger_rails:custom_ui + rails generate rswag:ui:custom This will create: - app/views/swagger_rails/swagger_ui/index.html.erb + app/views/rswag/ui/home/index.html.erb diff --git a/rswag-ui/lib/generators/rswag/ui/custom/custom_generator.rb b/rswag-ui/lib/generators/rswag/ui/custom/custom_generator.rb new file mode 100644 index 0000000..6d16382 --- /dev/null +++ b/rswag-ui/lib/generators/rswag/ui/custom/custom_generator.rb @@ -0,0 +1,13 @@ +require 'rails/generators' + +module Rswag + module Ui + class CustomGenerator < Rails::Generators::Base + source_root File.expand_path('../../../../../../app/views/rswag/ui/home', __FILE__) + + def add_custom_index + copy_file('index.html.erb', 'app/views/rswag/ui/home/index.html.erb') + end + end + end +end diff --git a/rswag-ui/lib/generators/rswag/ui/install/USAGE b/rswag-ui/lib/generators/rswag/ui/install/USAGE new file mode 100644 index 0000000..87b8bc5 --- /dev/null +++ b/rswag-ui/lib/generators/rswag/ui/install/USAGE @@ -0,0 +1,8 @@ +Description: + Adds rswag-api initializer for configuration + +Example: + rails generate rswag:api:install + + This will create: + config/initializers/rswag-api.rb diff --git a/rswag-ui/lib/generators/rswag/ui/install/install_generator.rb b/rswag-ui/lib/generators/rswag/ui/install/install_generator.rb new file mode 100644 index 0000000..f6f5b19 --- /dev/null +++ b/rswag-ui/lib/generators/rswag/ui/install/install_generator.rb @@ -0,0 +1,18 @@ +require 'rails/generators' + +module Rswag + module Ui + + class InstallGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + def add_initializer + template('rswag-ui.rb', 'config/initializers/rswag-ui.rb') + end + + def add_routes + route("mount Rswag::Ui::Engine => '/api-docs'") + end + end + end +end diff --git a/rswag-ui/lib/generators/rswag/ui/install/templates/rswag-ui.rb b/rswag-ui/lib/generators/rswag/ui/install/templates/rswag-ui.rb new file mode 100644 index 0000000..0805940 --- /dev/null +++ b/rswag-ui/lib/generators/rswag/ui/install/templates/rswag-ui.rb @@ -0,0 +1,9 @@ +Rswag::Ui.configure do |c| + + # List the Swagger endpoints that you want to be documented through the swagger-ui + # The first parameter is the path (absolute or relative to the UI host) to the corresponding + # JSON endpoint and the second is a title that will be displayed in the document selector + # NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON endpoints, + # then the list below should correspond to the relative paths for those endpoints + c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs' +end diff --git a/rswag-ui/lib/rswag/ui.rb b/rswag-ui/lib/rswag/ui.rb new file mode 100644 index 0000000..bdbcf25 --- /dev/null +++ b/rswag-ui/lib/rswag/ui.rb @@ -0,0 +1,15 @@ +require 'rswag/ui/version' +require 'rswag/ui/configuration' +require 'rswag/ui/engine' + +module Rswag + module Ui + def self.configure + yield(config) + end + + def self.config + @config ||= Configuration.new + end + end +end diff --git a/rswag-ui/lib/rswag/ui/configuration.rb b/rswag-ui/lib/rswag/ui/configuration.rb new file mode 100644 index 0000000..5b1a518 --- /dev/null +++ b/rswag-ui/lib/rswag/ui/configuration.rb @@ -0,0 +1,17 @@ +require 'ostruct' + +module Rswag + module Ui + class Configuration + attr_reader :swagger_endpoints + + def initialize + @swagger_endpoints = [] + end + + def swagger_endpoint(path, title) + @swagger_endpoints << OpenStruct.new(path: path, title: title) + end + end + end +end diff --git a/rswag-ui/lib/rswag/ui/engine.rb b/rswag-ui/lib/rswag/ui/engine.rb new file mode 100644 index 0000000..9e35902 --- /dev/null +++ b/rswag-ui/lib/rswag/ui/engine.rb @@ -0,0 +1,13 @@ +module Rswag + module Ui + class Engine < ::Rails::Engine + isolate_namespace Rswag::Ui + + initializer 'rswag-ui.initialize' do |app| + if app.config.respond_to?(:assets) + app.config.assets.precompile += [ 'swagger-ui/*' ] + end + end + end + end +end diff --git a/rswag-ui/lib/rswag/ui/version.rb b/rswag-ui/lib/rswag/ui/version.rb new file mode 100644 index 0000000..fa210ef --- /dev/null +++ b/rswag-ui/lib/rswag/ui/version.rb @@ -0,0 +1,5 @@ +module Rswag + module Ui + VERSION = '1.0.0' + end +end diff --git a/rswag-ui/rswag-ui.gemspec b/rswag-ui/rswag-ui.gemspec new file mode 100644 index 0000000..7f06dd9 --- /dev/null +++ b/rswag-ui/rswag-ui.gemspec @@ -0,0 +1,18 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require 'rswag/ui/version' + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "rswag-ui" + s.version = Rswag::Ui::VERSION + s.authors = ["Richie Morris"] + s.email = ["domaindrivendev@gmail.com"] + s.homepage = "https://github.com/domaindrivendev/rswag" + s.summary = "A Rails Engine that includes swagger-ui and powers it from configured Swagger endpoints" + + s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ] + + s.add_dependency "rails", ">= 3.1", "< 5.1" +end diff --git a/rswag-ui/spec/generators/rswag/ui/custom_generator_spec.rb b/rswag-ui/spec/generators/rswag/ui/custom_generator_spec.rb new file mode 100644 index 0000000..72d0289 --- /dev/null +++ b/rswag-ui/spec/generators/rswag/ui/custom_generator_spec.rb @@ -0,0 +1,21 @@ +require 'generator_spec' +require 'generators/rswag/ui/custom/custom_generator' + +module Rswag + module Ui + + describe CustomGenerator do + include GeneratorSpec::TestCase + destination File.expand_path('../tmp', __FILE__) + + before(:all) do + prepare_destination + run_generator + end + + it 'creates a local version of index.html.erb' do + assert_file('app/views/rswag/ui/home/index.html.erb') + end + end + end +end diff --git a/spec/dummy/app/models/concerns/.keep b/rswag-ui/spec/generators/rswag/ui/fixtures/config/initializers/.gitkeep similarity index 100% rename from spec/dummy/app/models/concerns/.keep rename to rswag-ui/spec/generators/rswag/ui/fixtures/config/initializers/.gitkeep diff --git a/rswag-ui/spec/generators/rswag/ui/fixtures/config/routes.rb b/rswag-ui/spec/generators/rswag/ui/fixtures/config/routes.rb new file mode 100644 index 0000000..9d8e33d --- /dev/null +++ b/rswag-ui/spec/generators/rswag/ui/fixtures/config/routes.rb @@ -0,0 +1,2 @@ +TestApp::Application.routes.draw do +end diff --git a/rswag-ui/spec/generators/rswag/ui/install_generator_spec.rb b/rswag-ui/spec/generators/rswag/ui/install_generator_spec.rb new file mode 100644 index 0000000..2015459 --- /dev/null +++ b/rswag-ui/spec/generators/rswag/ui/install_generator_spec.rb @@ -0,0 +1,27 @@ +require 'generator_spec' +require 'generators/rswag/ui/install/install_generator' + +module Rswag + module Ui + + describe InstallGenerator do + include GeneratorSpec::TestCase + destination File.expand_path('../tmp', __FILE__) + + before(:all) do + prepare_destination + fixtures_dir = File.expand_path('../fixtures', __FILE__) + FileUtils.cp_r("#{fixtures_dir}/config", destination_root) + + run_generator + end + + it 'installs the Rails initializer' do + assert_file('config/initializers/rswag-ui.rb') + end + + # Don't know how to test this + #it 'wires up routes' + end + end +end diff --git a/spec/dummy/lib/assets/.keep b/rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/.gitkeep similarity index 100% rename from spec/dummy/lib/assets/.keep rename to rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/.gitkeep diff --git a/rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/rswag-ui.rb b/rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/rswag-ui.rb new file mode 100644 index 0000000..0805940 --- /dev/null +++ b/rswag-ui/spec/generators/rswag/ui/tmp/config/initializers/rswag-ui.rb @@ -0,0 +1,9 @@ +Rswag::Ui.configure do |c| + + # List the Swagger endpoints that you want to be documented through the swagger-ui + # The first parameter is the path (absolute or relative to the UI host) to the corresponding + # JSON endpoint and the second is a title that will be displayed in the document selector + # NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON endpoints, + # then the list below should correspond to the relative paths for those endpoints + c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs' +end diff --git a/rswag-ui/spec/generators/rswag/ui/tmp/config/routes.rb b/rswag-ui/spec/generators/rswag/ui/tmp/config/routes.rb new file mode 100644 index 0000000..316f8ac --- /dev/null +++ b/rswag-ui/spec/generators/rswag/ui/tmp/config/routes.rb @@ -0,0 +1,4 @@ +TestApp::Application.routes.draw do + mount Rswag::Ui::Engine => '/api-docs' + +end diff --git a/spec/dummy/log/.keep b/rswag-ui/spec/spec_helper.rb similarity index 100% rename from spec/dummy/log/.keep rename to rswag-ui/spec/spec_helper.rb diff --git a/vendor/assets/components/swagger-ui/css/print.css b/rswag-ui/vendor/assets/components/swagger-ui/css/print.css similarity index 88% rename from vendor/assets/components/swagger-ui/css/print.css rename to rswag-ui/vendor/assets/components/swagger-ui/css/print.css index cd3aa8b..f9cb043 100644 --- a/vendor/assets/components/swagger-ui/css/print.css +++ b/rswag-ui/vendor/assets/components/swagger-ui/css/print.css @@ -82,7 +82,7 @@ .swagger-section pre .vhdl .attribute, .swagger-section pre .clojure .attribute, .swagger-section pre .coffeescript .property { - color: #8888ff; + color: #88F; } .swagger-section pre .keyword, .swagger-section pre .id, @@ -120,12 +120,75 @@ .swagger-section pre .xml .cdata { opacity: 0.5; } +.swagger-section .hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section .hljs, +.swagger-section .hljs-subst { + color: #444; +} +.swagger-section .hljs-keyword, +.swagger-section .hljs-attribute, +.swagger-section .hljs-selector-tag, +.swagger-section .hljs-meta-keyword, +.swagger-section .hljs-doctag, +.swagger-section .hljs-name { + font-weight: bold; +} +.swagger-section .hljs-built_in, +.swagger-section .hljs-literal, +.swagger-section .hljs-bullet, +.swagger-section .hljs-code, +.swagger-section .hljs-addition { + color: #1F811F; +} +.swagger-section .hljs-regexp, +.swagger-section .hljs-symbol, +.swagger-section .hljs-variable, +.swagger-section .hljs-template-variable, +.swagger-section .hljs-link, +.swagger-section .hljs-selector-attr, +.swagger-section .hljs-selector-pseudo { + color: #BC6060; +} +.swagger-section .hljs-type, +.swagger-section .hljs-string, +.swagger-section .hljs-number, +.swagger-section .hljs-selector-id, +.swagger-section .hljs-selector-class, +.swagger-section .hljs-quote, +.swagger-section .hljs-template-tag, +.swagger-section .hljs-deletion { + color: #880000; +} +.swagger-section .hljs-title, +.swagger-section .hljs-section { + color: #880000; + font-weight: bold; +} +.swagger-section .hljs-comment { + color: #888888; +} +.swagger-section .hljs-meta { + color: #2B6EA1; +} +.swagger-section .hljs-emphasis { + font-style: italic; +} +.swagger-section .hljs-strong { + font-weight: bold; +} .swagger-section .swagger-ui-wrap { line-height: 1; font-family: "Droid Sans", sans-serif; + min-width: 760px; max-width: 960px; margin-left: auto; margin-right: auto; + /* JSONEditor specific styling */ } .swagger-section .swagger-ui-wrap b, .swagger-section .swagger-ui-wrap strong { @@ -364,6 +427,7 @@ } .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { display: block; + min-width: 230px; margin: 0; padding: 0; } @@ -458,6 +522,17 @@ .swagger-section .swagger-ui-wrap .required { font-weight: bold; } +.swagger-section .swagger-ui-wrap .editor_holder { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap .editor_holder label { + font-weight: normal!important; + /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */ +} +.swagger-section .swagger-ui-wrap .editor_holder label.required { + font-weight: bold!important; +} .swagger-section .swagger-ui-wrap input.parameter { width: 300px; border: 1px solid #aaa; @@ -592,6 +667,7 @@ } .swagger-section .swagger-ui-wrap .markdown pre code { line-height: 1.6em; + overflow: auto; } .swagger-section .swagger-ui-wrap div.gist { margin: 20px 0 25px 0 !important; @@ -712,6 +788,9 @@ color: black; text-decoration: none; } +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated { + text-decoration: line-through; +} .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { text-decoration: underline; } @@ -753,6 +832,11 @@ .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { text-decoration: none; } +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p { + color: inherit; + padding: 0; + line-height: inherit; +} .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { color: black; } @@ -807,6 +891,9 @@ outline: 2px solid black; outline-color: #cc0000; } +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] { + max-width: 300px; +} .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; padding: 10px; @@ -1100,18 +1187,122 @@ } .swagger-section .oauth_submit { text-align: center; + display: inline-block; +} +.swagger-section .authorize-wrapper { + margin: 15px 0 10px; +} +.swagger-section .authorize-wrapper_operation { + float: right; +} +.swagger-section .authorize__btn:hover { + text-decoration: underline; + cursor: pointer; +} +.swagger-section .authorize__btn_operation:hover .authorize-scopes { + display: block; +} +.swagger-section .authorize-scopes { + position: absolute; + margin-top: 20px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .authorize-scopes .authorize__scope { + text-decoration: none; +} +.swagger-section .authorize__btn_operation { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .authorize__btn_operation_login { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .authorize__btn_operation_logout { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section #auth_container { + color: #fff; + display: inline-block; + border: none; + padding: 5px; + width: 87px; + height: 13px; +} +.swagger-section #auth_container .authorize__btn { + color: #fff; +} +.swagger-section .auth_container { + padding: 0 0 10px; + margin-bottom: 5px; + border-bottom: solid 1px #CCC; + font-size: 0.9em; +} +.swagger-section .auth_container .auth__title { + color: #547f00; + font-size: 1.2em; +} +.swagger-section .auth_container .basic_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .auth_container .auth__description { + color: #999999; + margin-bottom: 5px; +} +.swagger-section .auth_container .auth__button { + margin-top: 10px; + height: 30px; +} +.swagger-section .auth_container .key_auth__field { + margin: 5px 0; +} +.swagger-section .auth_container .key_auth__label { + display: inline-block; + width: 60px; } .swagger-section .api-popup-dialog { - z-index: 10000; position: absolute; + display: none; +} +.swagger-section .api-popup-dialog-wrapper { + z-index: 1000; width: 500px; background: #FFF; padding: 20px; border: 1px solid #ccc; border-radius: 5px; - display: none; font-size: 13px; color: #777; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.swagger-section .api-popup-dialog-shadow { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.2; + background-color: gray; + z-index: 900; } .swagger-section .api-popup-dialog .api-popup-title { font-size: 24px; @@ -1121,14 +1312,18 @@ font-size: 24px; padding: 10px 0; } -.swagger-section .api-popup-dialog p.error-msg { +.swagger-section .api-popup-dialog .error-msg { padding-left: 5px; padding-bottom: 5px; } -.swagger-section .api-popup-dialog button.api-popup-authbtn { +.swagger-section .api-popup-dialog .api-popup-content { + max-height: 500px; + overflow-y: auto; +} +.swagger-section .api-popup-dialog .api-popup-authbtn { height: 30px; } -.swagger-section .api-popup-dialog button.api-popup-cancel { +.swagger-section .api-popup-dialog .api-popup-cancel { height: 30px; } .swagger-section .api-popup-scopes { @@ -1138,14 +1333,14 @@ padding: 5px 0; line-height: 20px; } -.swagger-section .api-popup-scopes .api-scope-desc { - padding-left: 20px; - font-style: italic; -} .swagger-section .api-popup-scopes li input { position: relative; top: 2px; } +.swagger-section .api-popup-scopes .api-scope-desc { + padding-left: 20px; + font-style: italic; +} .swagger-section .api-popup-actions { padding-top: 10px; } diff --git a/vendor/assets/components/swagger-ui/css/reset.css b/rswag-ui/vendor/assets/components/swagger-ui/css/reset.css similarity index 100% rename from vendor/assets/components/swagger-ui/css/reset.css rename to rswag-ui/vendor/assets/components/swagger-ui/css/reset.css diff --git a/vendor/assets/components/swagger-ui/css/screen.css b/rswag-ui/vendor/assets/components/swagger-ui/css/screen.css similarity index 87% rename from vendor/assets/components/swagger-ui/css/screen.css rename to rswag-ui/vendor/assets/components/swagger-ui/css/screen.css index 436cc28..39ff583 100644 --- a/vendor/assets/components/swagger-ui/css/screen.css +++ b/rswag-ui/vendor/assets/components/swagger-ui/css/screen.css @@ -82,7 +82,7 @@ .swagger-section pre .vhdl .attribute, .swagger-section pre .clojure .attribute, .swagger-section pre .coffeescript .property { - color: #8888ff; + color: #88F; } .swagger-section pre .keyword, .swagger-section pre .id, @@ -120,12 +120,75 @@ .swagger-section pre .xml .cdata { opacity: 0.5; } +.swagger-section .hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section .hljs, +.swagger-section .hljs-subst { + color: #444; +} +.swagger-section .hljs-keyword, +.swagger-section .hljs-attribute, +.swagger-section .hljs-selector-tag, +.swagger-section .hljs-meta-keyword, +.swagger-section .hljs-doctag, +.swagger-section .hljs-name { + font-weight: bold; +} +.swagger-section .hljs-built_in, +.swagger-section .hljs-literal, +.swagger-section .hljs-bullet, +.swagger-section .hljs-code, +.swagger-section .hljs-addition { + color: #1F811F; +} +.swagger-section .hljs-regexp, +.swagger-section .hljs-symbol, +.swagger-section .hljs-variable, +.swagger-section .hljs-template-variable, +.swagger-section .hljs-link, +.swagger-section .hljs-selector-attr, +.swagger-section .hljs-selector-pseudo { + color: #BC6060; +} +.swagger-section .hljs-type, +.swagger-section .hljs-string, +.swagger-section .hljs-number, +.swagger-section .hljs-selector-id, +.swagger-section .hljs-selector-class, +.swagger-section .hljs-quote, +.swagger-section .hljs-template-tag, +.swagger-section .hljs-deletion { + color: #880000; +} +.swagger-section .hljs-title, +.swagger-section .hljs-section { + color: #880000; + font-weight: bold; +} +.swagger-section .hljs-comment { + color: #888888; +} +.swagger-section .hljs-meta { + color: #2B6EA1; +} +.swagger-section .hljs-emphasis { + font-style: italic; +} +.swagger-section .hljs-strong { + font-weight: bold; +} .swagger-section .swagger-ui-wrap { line-height: 1; font-family: "Droid Sans", sans-serif; + min-width: 760px; max-width: 960px; margin-left: auto; margin-right: auto; + /* JSONEditor specific styling */ } .swagger-section .swagger-ui-wrap b, .swagger-section .swagger-ui-wrap strong { @@ -364,6 +427,7 @@ } .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { display: block; + min-width: 230px; margin: 0; padding: 0; } @@ -458,6 +522,17 @@ .swagger-section .swagger-ui-wrap .required { font-weight: bold; } +.swagger-section .swagger-ui-wrap .editor_holder { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap .editor_holder label { + font-weight: normal!important; + /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */ +} +.swagger-section .swagger-ui-wrap .editor_holder label.required { + font-weight: bold!important; +} .swagger-section .swagger-ui-wrap input.parameter { width: 300px; border: 1px solid #aaa; @@ -592,6 +667,7 @@ } .swagger-section .swagger-ui-wrap .markdown pre code { line-height: 1.6em; + overflow: auto; } .swagger-section .swagger-ui-wrap div.gist { margin: 20px 0 25px 0 !important; @@ -712,6 +788,9 @@ color: black; text-decoration: none; } +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated { + text-decoration: line-through; +} .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { text-decoration: underline; } @@ -753,6 +832,11 @@ .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { text-decoration: none; } +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p { + color: inherit; + padding: 0; + line-height: inherit; +} .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { color: black; } @@ -807,6 +891,9 @@ outline: 2px solid black; outline-color: #cc0000; } +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] { + max-width: 300px; +} .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; padding: 10px; @@ -1100,18 +1187,122 @@ } .swagger-section .oauth_submit { text-align: center; + display: inline-block; +} +.swagger-section .authorize-wrapper { + margin: 15px 0 10px; +} +.swagger-section .authorize-wrapper_operation { + float: right; +} +.swagger-section .authorize__btn:hover { + text-decoration: underline; + cursor: pointer; +} +.swagger-section .authorize__btn_operation:hover .authorize-scopes { + display: block; +} +.swagger-section .authorize-scopes { + position: absolute; + margin-top: 20px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .authorize-scopes .authorize__scope { + text-decoration: none; +} +.swagger-section .authorize__btn_operation { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .authorize__btn_operation_login { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .authorize__btn_operation_logout { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section #auth_container { + color: #fff; + display: inline-block; + border: none; + padding: 5px; + width: 87px; + height: 13px; +} +.swagger-section #auth_container .authorize__btn { + color: #fff; +} +.swagger-section .auth_container { + padding: 0 0 10px; + margin-bottom: 5px; + border-bottom: solid 1px #CCC; + font-size: 0.9em; +} +.swagger-section .auth_container .auth__title { + color: #547f00; + font-size: 1.2em; +} +.swagger-section .auth_container .basic_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .auth_container .auth__description { + color: #999999; + margin-bottom: 5px; +} +.swagger-section .auth_container .auth__button { + margin-top: 10px; + height: 30px; +} +.swagger-section .auth_container .key_auth__field { + margin: 5px 0; +} +.swagger-section .auth_container .key_auth__label { + display: inline-block; + width: 60px; } .swagger-section .api-popup-dialog { - z-index: 10000; position: absolute; + display: none; +} +.swagger-section .api-popup-dialog-wrapper { + z-index: 1000; width: 500px; background: #FFF; padding: 20px; border: 1px solid #ccc; border-radius: 5px; - display: none; font-size: 13px; color: #777; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.swagger-section .api-popup-dialog-shadow { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.2; + background-color: gray; + z-index: 900; } .swagger-section .api-popup-dialog .api-popup-title { font-size: 24px; @@ -1121,14 +1312,18 @@ font-size: 24px; padding: 10px 0; } -.swagger-section .api-popup-dialog p.error-msg { +.swagger-section .api-popup-dialog .error-msg { padding-left: 5px; padding-bottom: 5px; } -.swagger-section .api-popup-dialog button.api-popup-authbtn { +.swagger-section .api-popup-dialog .api-popup-content { + max-height: 500px; + overflow-y: auto; +} +.swagger-section .api-popup-dialog .api-popup-authbtn { height: 30px; } -.swagger-section .api-popup-dialog button.api-popup-cancel { +.swagger-section .api-popup-dialog .api-popup-cancel { height: 30px; } .swagger-section .api-popup-scopes { @@ -1138,14 +1333,14 @@ padding: 5px 0; line-height: 20px; } -.swagger-section .api-popup-scopes .api-scope-desc { - padding-left: 20px; - font-style: italic; -} .swagger-section .api-popup-scopes li input { position: relative; top: 2px; } +.swagger-section .api-popup-scopes .api-scope-desc { + padding-left: 20px; + font-style: italic; +} .swagger-section .api-popup-actions { padding-top: 10px; } @@ -1217,34 +1412,33 @@ } .swagger-section #header { background-color: #89bf04; - padding: 14px; + padding: 9px 14px 19px 14px; + height: 23px; + min-width: 775px; } -.swagger-section #header a#logo { - font-size: 1.5em; - font-weight: bold; - text-decoration: none; - background: transparent url(../images/logo_small.png) no-repeat left center; - padding: 20px 0 20px 40px; - color: white; +.swagger-section #input_baseUrl { + width: 400px; } -.swagger-section #header form#api_selector { +.swagger-section #api_selector { display: block; clear: none; float: right; } -.swagger-section #header form#api_selector .input { - display: block; +.swagger-section #api_selector .input { + display: inline-block; clear: none; - float: left; margin: 0 10px 0 0; } -.swagger-section #header form#api_selector .input input#input_apiKey { +.swagger-section #api_selector input { + font-size: 0.9em; + padding: 3px; + margin: 0; +} +.swagger-section #input_apiKey { width: 200px; } -.swagger-section #header form#api_selector .input input#input_baseUrl { - width: 400px; -} -.swagger-section #header form#api_selector .input a#explore { +.swagger-section #explore, +.swagger-section #auth_container .authorize__btn { display: block; text-decoration: none; font-weight: bold; @@ -1259,13 +1453,24 @@ -khtml-border-radius: 4px; border-radius: 4px; } -.swagger-section #header form#api_selector .input a#explore:hover { +.swagger-section #explore:hover, +.swagger-section #auth_container .authorize__btn:hover { background-color: #547f00; } -.swagger-section #header form#api_selector .input input { - font-size: 0.9em; - padding: 3px; - margin: 0; +.swagger-section #header #logo { + font-size: 1.5em; + font-weight: bold; + text-decoration: none; + color: white; +} +.swagger-section #header #logo .logo__img { + display: block; + float: left; + margin-top: 2px; +} +.swagger-section #header #logo .logo__title { + display: inline-block; + padding: 5px 0 0 10px; } .swagger-section #content_message { margin: 10px 15px; @@ -1277,3 +1482,13 @@ text-align: center; padding-top: 10px; } +.swagger-section .swagger-collapse:before { + content: "-"; +} +.swagger-section .swagger-expand:before { + content: "+"; +} +.swagger-section .error { + outline-color: #cc0000; + background-color: #f2dede; +} diff --git a/vendor/assets/components/swagger-ui/css/style.css b/rswag-ui/vendor/assets/components/swagger-ui/css/style.css similarity index 100% rename from vendor/assets/components/swagger-ui/css/style.css rename to rswag-ui/vendor/assets/components/swagger-ui/css/style.css diff --git a/rswag-ui/vendor/assets/components/swagger-ui/css/typography.css b/rswag-ui/vendor/assets/components/swagger-ui/css/typography.css new file mode 100644 index 0000000..efb785f --- /dev/null +++ b/rswag-ui/vendor/assets/components/swagger-ui/css/typography.css @@ -0,0 +1,14 @@ +/* Google Font's Droid Sans */ +@font-face { + font-family: 'Droid Sans'; + font-style: normal; + font-weight: 400; + src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype'); +} +/* Google Font's Droid Sans Bold */ +@font-face { + font-family: 'Droid Sans'; + font-style: normal; + font-weight: 700; + src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype'); +} diff --git a/vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.ttf b/rswag-ui/vendor/assets/components/swagger-ui/fonts/DroidSans-Bold.ttf similarity index 86% rename from vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-700.ttf rename to rswag-ui/vendor/assets/components/swagger-ui/fonts/DroidSans-Bold.ttf index 15896c441fd59e5c2e9bdab90816661de06cdcba..036c4d135bf954ef3c2b15bc6493f72a01909aee 100644 GIT binary patch delta 2874 zcmbVOYj6|S6+SCzWm~r7M+`P#;D(UIC9-TBUU5=t8-u|yV6ZVs1FhH6N?N1GW_RTW zm}rvFR1lEuTpqSDB@p63+F~an>5DRfw!t9E$w004Q|*?_EB)3kom=)>0lUivkT7S-0QUV}4M z)IUR=)HZBv9Qj0j1c2tC(L1$`TdG5SMi0QNzhLtl8%xUpTz>!%PNUx94NK~Yk4DA; z#3mdY^tK!1@!xOW1dy{0z+(5Sfp}Q|)qSG?cHAIM3`n{PX(;g07&^!bgu48@x2x#@ z`B?z5qM$7KKEFynL}zZCUlzmx=TT8Y-G}`K0F;V6ZqZ zoDNH!sY42jKiC zI&C%CLQ01IA-ictr<*>z>wdbdon%;TUdwq9k(rTQ&8elG>#cK4;uv{QWSRjSL=BCh zh|cQnjH7=$Hq_Z8Sg>R5?guBWHqoAzp5e&Ma=Nl}a`W!VpE0wbaMtWOMRVuPC-*M6 zZ{ht9JXrjtMGv`4Jf&re%O75{blLJ1k5oMR*q2v+rLwAe73Q#Z?Yg?h>(_7C*sy7H zW7C%Atxr7p)u-aywtwyGEfT!&&Al(u{%^f<^3?FFubuh!>)-kA$l1|1Qg5Am`}^NJ z|DzvY0L>>W*E&zAJD|M{5`&-v1Uk$pL)~!thtGN=ObPFX*5|%)1mNAv@Be)4y&u3u z_$7?9vgo>U7540Z{=mMT-oE6a!*Fou*wITr`I-1Mz)yFk_IGz@rk-dY7E*JCJ@tY! zbz;3$#Mi0Kq77eXnh(rJrsfHe+e|HiHjV{wz*`(!uz;^{Y=zl^$gvHwg-VV^m?=EN zu^n7OisLlcU}@nv9r7)|;n)EI>je;Se|$2b$Tqbo3p}=u_#!8G#308mSR{VJaUNvb zSFnWJGSE7RZ4wKR563ySKsvm`u@#oUIL9_TFyUB)xLau8iXDoCLma2UkZ_6PbSSor zaO{9q3%rYX8l@iNwR%+|^(+=oTL z!e(*&x6Iz*{;s^Sv=zkfuoT45*~ugPtOT7ijj^bk?iG_W1G!OA_v0+voRy-CD_D(2 z5HSY*$S(^g#w`iXMmT>{c4l_sILr)WvoaZ9EP*TpFOIUx4x_~Q$n;vhU2nvsG#Cp@ z5oC?%SPw*(HN{ViE>-qRUYYo0T?s^xPJdJ*F`PtRkzaotCovz$yd09fh8B$|UM%}? zIEH|iH(nM7iV77_iqc_<2Twnpd=@PGs60IEQoLmhL#-$+?da(6NWA*J_~CeP12&6J zMrAHR= z?A_v?b_TXu_C&QnX=u`yURu9y)#?q6t4mA+yh85z!W9CLQucl>;6Ee*FMPOsYxH;Q zeQ~h-Z|pr*u<_sKj0=@QHiAF-Y~=^o+n7f67axn~erhUyxDMBagpd?&V*A+AU}>|Q zv%0K7>#+5vtFCF?D~=l| zf6pw=Tqz_nTQYB~R;B%$_!I$GIVNG~K$)4vRq)87tcQGZWAj@8iK!I`OBJyAM84>)EX1Rfh3DyJr2P@RV A8vpVA$dXC} zX>1cqO)D}93U3sIM)jv43Mxp52$Dcrik2u4`cZRr=^kd7b7sDo;qraSm*Uf5u?YeI zjhzf{Y02s}@Zv4dTE*)0(y~nzKeun(#XJT~ds14lr5Kq|fu5zDcWtUzSlI7;7zd6H zFs}+mbt9_Rd;pF)n70KFHTu_&m2U=Gn2NS)qbAz$Zr(l4cK~*!MsH{`U}vB;U^T3X z9H~CO)8NB`4>;(n4eO!bul%C`&j*TXS#V!bbRKw`c}{J#G1mKh?S0PkLehpveNYeS z&Dp@AO71Zb)nkS>|hsY+eWw5tSWssr7%%tsbT9X~yEm3xI!YZALML4F4S(mUFzLYK_ z3uUQTK_2#{VjEXc})S-1-d!^3aMVyyx2jR11b+s0lG!`zU6MG4YeTQk;;= zJ^9idDdCxr%jAplH~FW{XIo=m=~(6%_T;MxuZ5&H;e74mRn`^mPCL diff --git a/vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf b/rswag-ui/vendor/assets/components/swagger-ui/fonts/DroidSans.ttf similarity index 86% rename from vendor/assets/components/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf rename to rswag-ui/vendor/assets/components/swagger-ui/fonts/DroidSans.ttf index fb8cea662b2434571debae37fa6ba09fc5f24776..e517a0c5b9dfcdde4c4dee7569fa191b40f208bb 100644 GIT binary patch delta 2802 zcmai0dvFuS8UOC|uq{hMcFZerV3R=V82Ky=jd|6@U~Hh~5p19#r14ogNoOS8_5`p&ffjL-TfZ>`@UW2+1JFg6JiDg04T?o6G(G&`=$rRms|umQiY?HElum1amImi z6UwAz>z1}tpXkd`j-b*+OWXD}i9zcqzzYl5{Fb)bIskt!fN%okj&M@eU%Y(k0)VR> z$40~57FqsyLn}bwD$4(j>ak4H{P3P}0QbKEid-?-)G?4EbWn)~ys<=Ybi+#Tet?QT z0Ie#n$dT*U$%m*M#QFQAI+%cxs^1=pNN z%00S66_`Gb0ZGX#lFH?_$4&!eF^Hn9u9??pHG5b?$O)CVUWPM5o=xWrTH_4#K6%F1u6m^o|qoVmB(QF-UQ`Q)y)9TFha~`VAYu)w*f(maPwLYir-W zW9PRY-1Sgq_ru?Lq(g=$pE`J$jy(6{mtG$G$xl!I?3Gu4e(D$FujSr&{g=Ob^S5vP z?rkt4iu!)fi~1hu?t;EiFactFd6bF$aN;-L52u)r{4R7Jcw!jf-S__R$H{ZQhSTt8 zxbOka^q&6{{NU;AkwZrZ2K$da14GB3{n44Ttpuea(4>#a|B)obP8&PHBZO@1f?0xU zV>grtLpCmg&EhH>7ej?uu<3!AV-y7Rk54I7Itz=uAUWT(6?`DMnr!TcYS%d%mxIUc zVF`DZpmrR49t%(b&)Zl8KTO!z0o8EH#!jdZZnv=uGC^UbEx7The$U25a9nuK#>G%2 z9=5Ru-WSi?xD=K-MH_ozxAQ?8`w)L*V?VUJOdFR&h3ivj0u^G|E$sV1$v^}Mb{Pj` zL}AcChh8xFvN(3QB7c8>qwH%!-OA1rk7$8dEKGpAsPv z*^)_E(|Zjy7PrVrQWpx<2T6;j#R3V1tkn!%Gh|EEQWDVl*_jKV$=0kkB$Xf`yOVI>t&3{pd;=rAMRsYd9MY)uAsD29nn$YLoJBAsb9 z5h05gFTqIBu7-g}pbdSb*eT$~^8Rnl5?o{AmKjkN-);nBOY*q(;Ftzcd{3u`*<%zZ zAtNKN1Uflu2HzAjXCYz|TcqM*mIF4z@{yOjaPur5CazI&w!pE;S>{ZsCXtC{$Ppze z8+(Wr<>Vr_aw{V*9A!10#Fn;4rdREkl}9+K@pMv7A#KFO zVkD+ws8M3|>Pl1&Bc+O|#!|>R~)C5*n)oiY7Au&qRUP>2w-Mp@cV= zg*+tww;Q%wd!vRa#jPZ+Z{<|7B0>N8A;zFP2ZfsT9S9m%+-Jbni?)Qw~*m=(tY@d}@l{N@{r5&Z8W9#q^`KrY( z-=x3XH|d*1+i}NEeEHk`Mxi-3`mf~M42hd#E?DdLR0VqMb|5fNXrBJSnBB(9sxkPh zW$VkvLP=$b8v);$9Cm* z9eUhFKYeBny>zs_M1+TK{uLg+d5ta_I7bHtU!lhbXVJ<*D<}Q1L7+G2Y9T=XMd#9Y zX%+1rdOElJ*oZ*A!y(!{`~tl;JVviPIXa&u#)JR1=7ErqtrQe4 delta 959 zcmYjQZD>Ut2^^DF1yc0~hvG!T9S?DcSdv}^Bxcin*v&A=f0#k-Imj>e`|Tw#3- zuqoj{ETMB@8y8>_j&NjOXiJ^r2R1JS`d0P?1Kn@mdZ&PX2kT$-FyXwbB)MRU^NOD6 zKs*|2H}yt9ZjSVI1#TrD9|rVpUSm8Oi0g8%U|nY&-ra#{(3AE*0S@wfQLFbYiw&Ir zV^JM2G|9vZy+5cQoL;^d*gpV+A`yJUyarHr@E!yXo~G}(@(-{JS`a)cJt^s zx+jJzsaE)mBB@2Dpr|O8EuV=L6co`lvBNwHi9_b_aBt4?kgmjyy!DMeVMC`$T8ddP ztqWrbt_3smGFG9yz+3+3QQ$*!fw!Ur@xs`Jb|le=e)ORi-SDCf0qjN$tq3BFJ&17L zkB!W9(Yr&H)H&?a{__{ylumZo%@5}`7ea{cDWyiok^7?5+<|!?W|QXka}R>PJN9J) z5BNHPkkEy$=A?3MS}K>~(i7)SX;yA^Rmqp+oNLyy%`#$nX?bm3YTazBFIrzT;i^(| zZWDQTuK3U5zg1Oro4Mv!%iVdkUERePRIjO%YR*2a=GDA?*iqrQ>D=j@ah~UxaaA!o z%r4?ee#Gpe)&j=nmtQEt%ewQLRDeVK^dtP*`T`oYTiP_^nf8p5=d!{_&qp`WjpGse z{KO9W@x&kvjg(PuYMg3*anaZ5q_J`2fI<&Xw$s|oq)lKozDFhfn^{i3of@F`nTy6( zr>g~Nsd~DcZ4f1NCHn~_vVKz18|YQ`C~Z%b(OkO0*ncJ?==RAnDjoGx-RMO{pl0qg Q&5T}{|KU`AfBXVJ0Duqx+yDRo diff --git a/rswag-ui/vendor/assets/components/swagger-ui/images/collapse.gif b/rswag-ui/vendor/assets/components/swagger-ui/images/collapse.gif new file mode 100644 index 0000000000000000000000000000000000000000..8843e8ce5a46781defd33d5304a0cb4191e72546 GIT binary patch literal 69 zcmZ?wbhEHbwd`J-OQ1PRqNMh0sDWgikt literal 0 HcmV?d00001 diff --git a/rswag-ui/vendor/assets/components/swagger-ui/images/expand.gif b/rswag-ui/vendor/assets/components/swagger-ui/images/expand.gif new file mode 100644 index 0000000000000000000000000000000000000000..477bf13718dc56928f313ef6eeb1c2b1a47db69a GIT binary patch literal 73 zcmZ?wbhEHbLn3(W(+??Lz0|1bY zBOI=)>)`s_)ziV%jYSO(XL0jzwRdzz0f5h3o&g$RuuUs-x^x25hzd&8aMhYm z20@az_*h9O)uUM7&eG_0-ceG*XB;Sq!i$d&ilfnklE#2$h}K!(#fQF+ioss2_`i3W zZaLi?xo-F-yH#UqVE z1N;q?mVUt2LDUA|_)LO{fex*lj)zQ!Mg?s~0usvsd?Dj7oe*-Ff(hcalkdY%c{fp%K0o%`ttPm z_qJ7zU}1}375u;7yM|9Riu!LCDleu+Ry9+JP{)`%zRG-zsfUVa3v$n zl|Ls*4FJL+mk}*0LaD*EdNN;08{n8y35GD`ci=B}&xNuRXbW<_c+@k<#EHM%`cm#d zF_$0bXAOIqSJ=BP!*r)3B8#0ZyWWi z^&(5DMPFn~j)c;xX%tnyOPgeHpmR8Ntp4%*^PR=Cd)gUaj3`^lz_#>tK2TOCvhs)*!D2mJA4g_idhs=9UNJYv%9MS0D+w1- z7cs8WKTfrmBU;OJ|C{?$u%2jq- z#jEy9@`_uPnoTZ($rbw5xy`v%&p$G@ut%AATA-!?0Hw?8X6 z$jooE49eGZEm`ty-8yFq5WGTMxty2$Rv|?a){$}$&XbPbmF^XDca>%q?4SRo2e;^! zhZk9=S!b+-C1J*;-%lyOR;Gw^>@#2ESaa!f>3h#b$i>Q~p|zuRkgczUDdsJf(v;Hd z9~mBbH!__imLntdO?WG3E9YCza?=x2A5&;!Y2zo9TJ5@NvB^PW%d>D(Ns}U^p{Yfk za%Em6=X7&rbIF|&jCP84dHyuIyXIHpVB?1-`kIXy!uhI3m&O2Vv<)4)skf^8Af+`W z;B&z1D;XlP$XW7oW>e-s&w3BYW247Q*`v7!asj<3>Dz*655W1d0I zqzal2jqwaUj*5-e2pJN{kQK;b>{}dYT~~kp+udTtZ{l}ePF+sde(8Mmf%t*rLFB@l zVbM3d`6eQsU%Ch2-}N@e<-l*!9y%>o1nR(bg3%3I?1Hv}Nye4mYMP3?%TBEi>16-N zdc0YaU6Ynr5?(T~8kXp28fY5Q>2}Sx3SWl5V!yGw@w*uZN`g3X(0I|o3c+T$7k~X; zvbM*|J)TGXsh!?KyRo^EB1CXG6hhwBFZnEpa!y=fiW z9Z1R@9vN;NAs6o!g=82P$`JD&Ux0fqE1oN+D=y`rg>)pV`PIevWXlAeO3z7r7X2*O zCtzsj$F-K1+V$jWeN^hxcR;v?K%lBq5U901$~?@ zuNkHNDD|fTC0{9R9On_WH)lM^w~M(8%_cZ5J{~m#H|M*jeWc2#<(Isd)FS4~(JN8} z-N1x#e@JZ6B-32Xd7Mw1?Zx6EEc3WkC^R`K(H&OCs+H1+-a&8Ldks~f!fUU4pT#x_ z-b*7IbgE0NATZ$vHNuL2AWV6_@e$RcQO5S|BhgsN^z!@%Bm2X3*!e_>+`~6F`&3<2 zE6I&T5gNw)%*f5wil>sb((bJg z{zG{W#%|1h!hU;mi9g?rJiRPqGlTpY+3Z|B*|Jgn{_j6Z^MV;8JoC+=jd0BScIi(o zsFK0a5h*!DA>ssaju1bXSZuKV_V?NN0i%D*AJM(3d9#+&E7`%ec|#mStttB{)P>qb z-KktvblLXb^a~e@N^KYS{eHX&yelGh^2TqNuT#tuG6?;I=?A+i$s zCX{Ysx1?I6M$L~tJwK|Q-=H@BWSm|7*ve(+`Hu8;%A`(zb8d5X6~^2NbKuw7;BSAr z&6qDSx_Hv+`-}EwdMaO1X0i3y&-JwT^!GCDVxR~2)#y<3tT*>TWs6RW)rtJR!Yq6- za!StZdST{p;nmX)?}Fp;1(j36@X)dAq~8e?aDs%agrNwsh~4~0vXYXkavoRPmnHBq zyD_eOy6bn>LsfLNf`NahcE62|XO6$kILb(AcX?TKwbQXnUp?Q~)pxIL&__hb^2F$3 zztelcoB8nlGKJ3iOO{tWHx6eXe;a%$xk#Z;Nx8wt%RIic72%%;01~vCq5{GPXD^q) znQ@3Vq68}TF6tNG)1J#rY9dhQ(D3juvN4tddpi#^@ziw>BceQ)4z0W^W@;Cqg>A_# z6WSU9>K3k1KKe%bc7@QZywcXEIO}^WYzfJPByMSfi>S}>L`eQhp%2vIONha9u z?m)Q8#bEdz5|c>7j;{5?rQuXhT_z7^PDPXr9Y2d1G9WIb&WE5cq$w#<7j^ZwqYp!1 zEO|d6h1{dfc}f2TeN7J41MT0gqt&>jJ!GBo*HQg=D0(QPxO3w8dzGOY>u#vf^B=lr zXIpPJGOPku#LwCKDopA)xVcpVEmGAo@i3Vp`rna9nX5B%9_B*ROr8PG71X`eNqa|^ zcs5V*sNxaE7Yplp6JQ=GH#En8vHUQMQr2K=P%U26&V5P)#>Ag?qiGAnmhxGs&JY4& z^e7X?Yd>35T-+2_GSg|LE8Jsqm_C0m$!57NKp2IENVA*EOUub6SudOrt7u+gnO4>V-rr{|*H1Zl~Z=xQ|{c#_Uo5{9_PS?n-gj4pGiFwnW zzN1F1+L;2sv?*=WhLlM9;h+eje( zJ-0?pZiNkPcnpr@NEFrAi?Tr=kb!V8-FiJREL?a z71DmL`NW(BVAUL1#N99*r44|I{(q4_wu0BVGEo~E1RR~ z*`kf};Zo}!IOr;tPLP~ovYqVvTM~^S^j^9j#=jljEqI0`U^gBi zT5sFD+f|!VTC>+}w%y}+b~l&myb6tjf_R(4m^N&+kuCWIsd{cQeDSM751{5_xL6ix z-fDA<&4ZP&h8Md=>V5v>O>J$KSS;4=@6Qzo($^QWx7_~b*K0-zVM}9U?uLd2eG3bI zjSKx=;3eTUZ)T{qJg*wP4cfJ0Z{~vL z^z-sTa~@R8LJ}~%tn`nJY=VSw!$jua)f7Hlyn}bECU4ss zqZX3cwi4u&XmE~;!Y4*U6Q6voMsm1FqE*Mi3E`0GeW;36K^$QHnvC~dOd6XO1}4zj z(?)qB=9xh`$# zZRygb&A=T<)JSa&m9jgQF1d3J%gb6Fmww;XextBj@Ab0j*mVh)p@a3WxoO6_a-Ons z)M4XgBCvl4FCri}>O;8gWNN>91p&$dc4=KT)Mr5ahp7tBT zh+{*7Q+e{z9vJ&wAfsP1NhVV{IXS(rc^*Ecro1rS)kR?2j`RkD^+81C1uxL1FQ>j* zMWlIHOnwz;7OdBQ!b^7Kod;=m^SXa>dF#rw2rL;`WW%tnkC@}(A>*LDi|`Zp|Mfl)KWn~ z*6#dddugi}DaI`21+DE87UKN+Ln=6%ATsBrkaOMPK4!|-}K!V>=D@Auk^qt#hU|oBROwTyu5r&DU5PL^1s)U|Qv=+sgW8NdD3-fy&V};#2&)JZ44Y>?<5BsSjZcUFG3oGn!~v%xe$1GzDg*3eZyT z7XKN$-N^MEDvH5igxyyaZ##W1)irI{@7@|g=lk4;GkB;xp0?NPAE|70O%w1?X=YhB zG2;_Epr%RY+a=B5NZjp;YD?rk5f^Au1ZxzxEN|+=7{=P_2)pPW1--THZ8&7>>dsji_z0Z&f%A;0LsK~dlK za1aOSmOPVgI^~KjVX1@5MGI-v-!0V7RpIupPktW@4|JLjILakSRsHv|=5#;VEhV!xMx5!-o<46-0DQPQK I!JdTtAI=+^)&Kwi literal 0 HcmV?d00001 diff --git a/rswag-ui/vendor/assets/components/swagger-ui/images/favicon-16x16.png b/rswag-ui/vendor/assets/components/swagger-ui/images/favicon-16x16.png new file mode 100755 index 0000000000000000000000000000000000000000..0f7e13b0d9903d27a9129950b1dad362361504e4 GIT binary patch literal 445 zcmV;u0Yd(XP)rNm2=6wQ7&2F}_`h_PI>(9Fx!5<0%l6W{u0OQ#*rglqx3__&vD?|#%fhn*Mn&YY1i+JQHqPvZ34FR@_E%P@x zzTL;Bw#nJXWY}D7^bC>-bx{t|^|R6Oci&MKvov8Op~S=}R=h^p-=vZ0uqG@LE6tP7 n92{cY$^db6>&z__iT?Z#Z8BG|DVcT0DjiaEd>Z!7_`J}8! zKk_$1lGm$vJOY&DjT-(&VGn0;R`iN9=1aOuG`H}BlY>&R3KbGER zB2$7euhH;y1C_LTQex%L6khZpkjFn!ajOUK)f3JLz+I;CE@(N)T)CM4AWjfl-(04= zrsMQ)#NG6nr^Y7!6LA;iHXh?UOFE%hhy>7dl=;I$J>g0BH_r|_4ctEsXx z2sDIQnwa*rcK=*3XUC$D{I@}DTNs@GCb7dB2%%nV%jR){xktt;Ah09op7x@l5D6B2 z0uBdt0YmcN!o?lMpu9Io(1&B1s{TUu*a>2&>Iycx__fbDRM8PYtLt+#G*xSt(cn}K zt!~W2{`9r)xkh^xodLS&FbYw`x$t&Vhl?)#f&k-lZIs<`$gTj{^#^HewuJz(WnUZZ z{Ty_aE;^93bhc-^^k6ZM!^e~$q5!Zz`XPta{a@651gPzaFx$&%IHL6hx$mSeAa#n6 zLkyc-M zs$qhBZhCNE^aIEV)H_~^IeqSRnvo!21Qc`Z;S9!IqXl4K(RUImejotzuG65LVuGS# zcqp@OA8~ln^4c^VihUew)IOX^E9KMtvSvnZ| zC@rl{f(B*PA26aFR`|X!!I(7x_|kq{rlqwhCia+CfNbOg_yYt0bDCc4g#h#`3jpCd zNAhr%4#Ye{i>ni$fzY%r0IS%l3HHZ4tTjOi=JW-t_iG~)oC!2C!52Cc|TAPaH zJ}l%m9yPmA-4#lJea@uf$a`(1;={rL2f*8;7%icbF}e^_`X#ndU=SI0nIn8hXPXHS zSN4rbF}jl0HWx(_`q`-SRa9jP8Ab!}sThNkQ634k=qXBVM4`o{M>qrLJD ze*%D)S;wpxG$d%FcDf-6%zMqWA+gw!C1~T5+|ys$G3Ksm&x59Lyd?0l+LWSk6hc4~ z+yC>|4f;X3#cq3!)>#Mvb-^co7LMrzqWeKB$21I>tJgaGFwu6eB%&j?@d*8GAx~In zI1p-lXVKtcvY7;$TX~wjYw|QhB%q!npQES%F~%Aqz~pJB%rNu!xAj;>xZt75!VHju zfFy%B-`3;Qf<{h94~I62zcHv}D5pS-QCN`M8K1>jN9mpbrFk=5no8j!00000NkvXX Hu0mjfOavUK literal 0 HcmV?d00001 diff --git a/vendor/assets/components/swagger-ui/images/favicon.ico b/rswag-ui/vendor/assets/components/swagger-ui/images/favicon.ico similarity index 100% rename from vendor/assets/components/swagger-ui/images/favicon.ico rename to rswag-ui/vendor/assets/components/swagger-ui/images/favicon.ico diff --git a/rswag-ui/vendor/assets/components/swagger-ui/images/logo_small.png b/rswag-ui/vendor/assets/components/swagger-ui/images/logo_small.png new file mode 100644 index 0000000000000000000000000000000000000000..ce3908e3f275ee790b38b28bdd03084cdf46bfe8 GIT binary patch literal 455 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG2?0JKu0T4Zo>){e-JpKHLwvhH zX1~Iguh_m+$ZAZ!g$?e*UTJ>#yA34>a)p^YgFX-=DHR;hT%5Ezl0x zk|4ie1_1|y`S<^yFHqRu|30Ass2u?UjxX+x1S