From caaf377297d012e937eea664b2c54ebf5d89789a Mon Sep 17 00:00:00 2001 From: mule Date: Sat, 11 Feb 2023 23:22:13 +0300 Subject: [PATCH 1/6] add rswager gem --- GemFile | 4 +++- GemFile.lock | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/GemFile b/GemFile index bcf3166..e7aaf87 100644 --- a/GemFile +++ b/GemFile @@ -82,4 +82,6 @@ gem 'pagy', '~> 6.0' gem 'devise' -gem 'cancancan' \ No newline at end of file +gem 'cancancan' + +gem 'rswag' \ No newline at end of file diff --git a/GemFile.lock b/GemFile.lock index 0d6d1f6..8c7be83 100644 --- a/GemFile.lock +++ b/GemFile.lock @@ -117,6 +117,8 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) json (2.6.3) + json-schema (3.0.0) + addressable (>= 2.8) loofah (2.19.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) @@ -211,6 +213,20 @@ GEM rspec-mocks (~> 3.11) rspec-support (~> 3.11) rspec-support (3.12.0) + rswag (2.8.0) + rswag-api (= 2.8.0) + rswag-specs (= 2.8.0) + rswag-ui (= 2.8.0) + rswag-api (2.8.0) + railties (>= 3.1, < 7.1) + rswag-specs (2.8.0) + activesupport (>= 3.1, < 7.1) + json-schema (>= 2.2, < 4.0) + railties (>= 3.1, < 7.1) + rspec-core (>= 2.14) + rswag-ui (2.8.0) + actionpack (>= 3.1, < 7.1) + railties (>= 3.1, < 7.1) rubocop (1.43.0) json (~> 2.3) parallel (~> 1.10) @@ -286,6 +302,7 @@ DEPENDENCIES rails (~> 7.0.4) rails-controller-testing rspec-rails + rswag rubocop (>= 1.0, < 2.0) selenium-webdriver sprockets-rails From 5f98934e5670571d83e675a95429d1134709ab39 Mon Sep 17 00:00:00 2001 From: mule Date: Sat, 11 Feb 2023 23:23:56 +0300 Subject: [PATCH 2/6] Generate spec using rspec:swagger --- spec/requests/api/v1/comments_spec.rb | 163 ++++++++++++++++++++++++++ spec/requests/api/v1/posts_spec.rb | 151 ++++++++++++++++++++++++ spec/requests/api/v1/users_spec.rb | 137 ++++++++++++++++++++++ 3 files changed, 451 insertions(+) create mode 100644 spec/requests/api/v1/comments_spec.rb create mode 100644 spec/requests/api/v1/posts_spec.rb create mode 100644 spec/requests/api/v1/users_spec.rb diff --git a/spec/requests/api/v1/comments_spec.rb b/spec/requests/api/v1/comments_spec.rb new file mode 100644 index 0000000..e328118 --- /dev/null +++ b/spec/requests/api/v1/comments_spec.rb @@ -0,0 +1,163 @@ +require 'swagger_helper' + +RSpec.describe 'api/v1/comments', type: :request do + + path '/api/v1/users/{user_id}/posts/{post_id}/comments' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + parameter name: 'post_id', in: :path, type: :string, description: 'post_id' + + get('list comments') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + post('create comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{user_id}/posts/{post_id}/comments/new' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + parameter name: 'post_id', in: :path, type: :string, description: 'post_id' + + get('new comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{user_id}/posts/{post_id}/comments/{id}/edit' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + parameter name: 'post_id', in: :path, type: :string, description: 'post_id' + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('edit comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{user_id}/posts/{post_id}/comments/{id}' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + parameter name: 'post_id', in: :path, type: :string, description: 'post_id' + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('show comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + patch('update comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + put('update comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + delete('delete comment') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:post_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end +end diff --git a/spec/requests/api/v1/posts_spec.rb b/spec/requests/api/v1/posts_spec.rb new file mode 100644 index 0000000..5f2b09d --- /dev/null +++ b/spec/requests/api/v1/posts_spec.rb @@ -0,0 +1,151 @@ +require 'swagger_helper' + +RSpec.describe 'api/v1/posts', type: :request do + + path '/api/v1/users/{user_id}/posts' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + + get('list posts') do + response(200, 'successful') do + let(:user_id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + post('create post') do + response(200, 'successful') do + let(:user_id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{user_id}/posts/new' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + + get('new post') do + response(200, 'successful') do + let(:user_id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{user_id}/posts/{id}/edit' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('edit post') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{user_id}/posts/{id}' do + # You'll want to customize the parameter types... + parameter name: 'user_id', in: :path, type: :string, description: 'user_id' + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('show post') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + patch('update post') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + put('update post') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + delete('delete post') do + response(200, 'successful') do + let(:user_id) { '123' } + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end +end diff --git a/spec/requests/api/v1/users_spec.rb b/spec/requests/api/v1/users_spec.rb new file mode 100644 index 0000000..19a8dd1 --- /dev/null +++ b/spec/requests/api/v1/users_spec.rb @@ -0,0 +1,137 @@ +require 'swagger_helper' + +RSpec.describe 'api/v1/users', type: :request do + + path '/api/v1/users' do + + get('list users') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + post('create user') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/new' do + + get('new user') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{id}/edit' do + # You'll want to customize the parameter types... + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('edit user') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/users/{id}' do + # You'll want to customize the parameter types... + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('show user') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + patch('update user') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + put('update user') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + delete('delete user') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end +end From 8027b23b3c0af8b5833f6280f77f17c588f1c7df Mon Sep 17 00:00:00 2001 From: mule Date: Sat, 11 Feb 2023 23:26:03 +0300 Subject: [PATCH 3/6] Config default domain name to localhost --- spec/swagger_helper.rb | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 spec/swagger_helper.rb diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb new file mode 100644 index 0000000..8f71560 --- /dev/null +++ b/spec/swagger_helper.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.configure do |config| + # Specify a root folder where Swagger JSON files are generated + # NOTE: If you're using the rswag-api to serve API descriptions, you'll need + # to ensure that it's configured to serve Swagger from the same folder + config.swagger_root = Rails.root.join('swagger').to_s + + # Define one or more Swagger documents and provide global metadata for each one + # When you run the 'rswag:specs: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.yaml' => { + openapi: '3.0.1', + info: { + title: 'API V1', + version: 'v1' + }, + paths: {}, + servers: [ + { + url: 'https://{defaultHost}', + variables: { + defaultHost: { + default: 'www.example.com' + } + } + } + ] + } + } + + # Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'. + # The swagger_docs configuration option has the filename including format in + # the key, this may want to be changed to avoid putting yaml in json files. + # Defaults to json. Accepts ':json' and ':yaml'. + config.swagger_format = :yaml +end From 6dce835d94ebb3e84cf3f3af229dd92bbd4e0c65 Mon Sep 17 00:00:00 2001 From: mule Date: Sat, 11 Feb 2023 23:26:12 +0300 Subject: [PATCH 4/6] Config default domain name to localhost --- spec/swagger_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb index 8f71560..562397f 100644 --- a/spec/swagger_helper.rb +++ b/spec/swagger_helper.rb @@ -27,7 +27,7 @@ url: 'https://{defaultHost}', variables: { defaultHost: { - default: 'www.example.com' + default: '127.0.0.1:3000/' } } } From e56584162e7a370f0074d35d7994491a06a1b69f Mon Sep 17 00:00:00 2001 From: mule Date: Sat, 11 Feb 2023 23:26:49 +0300 Subject: [PATCH 5/6] updated by rswag --- config/routes.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 7498c2a..7f95739 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + mount Rswag::Ui::Engine => '/api-docs' + mount Rswag::Api::Engine => '/api-docs' #devise root devise_for :users devise_scope :user do From 9456513c3120ea6d35aad5647516b9544f87327e Mon Sep 17 00:00:00 2001 From: mule Date: Sat, 11 Feb 2023 23:27:31 +0300 Subject: [PATCH 6/6] Files added by rswager --- config/initializers/rswag_api.rb | 14 ++++++++++++++ config/initializers/rswag_ui.rb | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 config/initializers/rswag_api.rb create mode 100644 config/initializers/rswag_ui.rb diff --git a/config/initializers/rswag_api.rb b/config/initializers/rswag_api.rb new file mode 100644 index 0000000..4d72f68 --- /dev/null +++ b/config/initializers/rswag_api.rb @@ -0,0 +1,14 @@ +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 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 lambda function to alter the returned Swagger prior to serialization + # The function will have access to the rack env for the current request + # For example, you could leverage this to dynamically assign the "host" property + # + #c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } +end diff --git a/config/initializers/rswag_ui.rb b/config/initializers/rswag_ui.rb new file mode 100644 index 0000000..0a768c1 --- /dev/null +++ b/config/initializers/rswag_ui.rb @@ -0,0 +1,16 @@ +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 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 or YAML endpoints, then the list below should + # correspond to the relative paths for those endpoints. + + c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs' + + # Add Basic Auth in case your API is private + # c.basic_auth_enabled = true + # c.basic_auth_credentials 'username', 'password' +end