Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ gem 'devise', '~> 4.8', '>= 4.8.1'
# Add Cancan for authorization
gem 'cancancan'

# Add rswag to generate an API documentation
gem 'rswag'

# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"

Expand Down Expand Up @@ -86,4 +89,5 @@ end
group :development, :test do
gem 'database_cleaner'
gem 'rspec-rails', '~> 5.0.0'
gem 'rswag-specs'
end
17 changes: 17 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ GEM
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
json-schema (2.8.1)
addressable (>= 2.4)
loofah (2.15.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
Expand Down Expand Up @@ -212,6 +214,19 @@ GEM
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.11.0)
rswag (2.5.1)
rswag-api (= 2.5.1)
rswag-specs (= 2.5.1)
rswag-ui (= 2.5.1)
rswag-api (2.5.1)
railties (>= 3.1, < 7.1)
rswag-specs (2.5.1)
activesupport (>= 3.1, < 7.1)
json-schema (~> 2.2)
railties (>= 3.1, < 7.1)
rswag-ui (2.5.1)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubyzip (2.3.2)
selenium-webdriver (4.1.0)
childprocess (>= 0.5, < 5.0)
Expand Down Expand Up @@ -272,6 +287,8 @@ DEPENDENCIES
rails (~> 7.0.2, >= 7.0.2.3)
rails-controller-testing
rspec-rails (~> 5.0.0)
rswag
rswag-specs
selenium-webdriver
sprockets-rails
stimulus-rails
Expand Down
4 changes: 2 additions & 2 deletions config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ development:
username: postgres

# The password associated with the postgres role (username).
password: 1234
password: dirtycode


# Connect on a TCP socket. Omitted by default since the client uses a
Expand Down Expand Up @@ -60,7 +60,7 @@ test:
<<: *default
database: blog_test
username: postgres
password: 1234
password: dirtycode


# As with config/credentials.yml, you never want to store sensitive information,
Expand Down
14 changes: 14 additions & 0 deletions config/initializers/rswag-ui.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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
14 changes: 14 additions & 0 deletions config/initializers/rswag_api.rb
Original file line number Diff line number Diff line change
@@ -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 lamda 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
8 changes: 5 additions & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
Rails.application.routes.draw do
mount Rswag::Ui::Engine => '/api-docs'
mount Rswag::Api::Engine => '/api-docs'
devise_for :users
#get 'users'
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
Expand All @@ -10,11 +12,11 @@
namespace :v1 do
post 'users/sign_up' => 'users#register'
post 'users/sign_in' => 'users#login'
get 'posts' => 'posts#index'
post 'posts/create' => 'posts#create'
get 'users/:user_id/posts' => 'posts#index'
post 'users/:user_id/posts' => 'posts#create'
get 'comments' => 'comments#index'
post 'comments/create' => 'comments#create'

end
end

Expand Down
95 changes: 95 additions & 0 deletions spec/requests/api/v1/posts_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
require 'swagger_helper'

RSpec.describe 'api/v1/users/{user_id}/posts', type: :request do
path '/api/v1/users/{user_id}/posts' do
get('list posts') do
tags 'Posts'
produces 'application/json'
consumes 'application/json'
parameter name: :id, in: :path, type: :string
parameter name: 'ApiToken', in: :header, type: :string
# schema: {
# type: :object,
# properties: {
# user_id: {type: :integer}
# },
# required: ['user_id']
# }
response(200, 'successful') do
schema type: :array,
items: {
type: :object,
properties: {
id: {type: :integer},
title: {type: :string},
text: {type: :string},
comments_counter: {type: :integer},
likes_counter: {type: :integer},
author_id: {type: :integer},
created_at: {type: :string},
updated_at: {type: :string}
}
},
required: ['author_id', 'title', 'text', 'comments_counter', 'likes_counter', 'created_at', 'updated_at']

let(:id) do
user = User.create!(id: 5, name: 'Tom', email: 'tom@example.com', password: '1234566',
confirmed_at: Time.now, posts_counter: 0, bio: 'Developer from Mexico')
Post.create!(author_id: user.id, title: 'My first post', text: 'The text of my post', likes_counter: 0,
comments_counter: 0)
{ id: user.id }
end
run_test!
end

response '404', 'invalid request' do
schema type: :object,
properties: {
success: { type: :boolean },
message: {
type: :array,
items: { type: :string }
}
}

let(:user) { { user_id: '99' } }
run_test!
end
end
end

path '/api/v1/users/{user_id}/posts' do
post('create post') do
consumes 'application/json'
parameter name: :id, in: :path, type: :string
parameter name: 'ApiToken', in: :header, type: :string
parameter name: :post, in: :body, schema: {
type: :object,
properties: {
# user_id: {type: :integer},
title: { type: :string },
text: { type: :string }
},
required: [ 'user_id', 'title', 'text' ]
}
response(201, 'Post created') do
let(:post) { {title: 'foo', text: 'bar'} }
# user = User.create!(id: 5, name: 'Tom', email: 'tom@example.com', password: '1234566',
# confirmed_at: Time.now, posts_counter: 0, bio: 'Developer from Mexico')
# post = Post.create!(author: user, title: 'My first post', text: 'The text of my post', likes_counter: 0,
# comments_counter: 0)
# { id: user.id}


run_test!
end
response '422', 'invalid request' do
let(:post) { { title: 'foo' } }
run_test!
end
end
end
end



43 changes: 43 additions & 0 deletions spec/swagger_helper.rb
Original file line number Diff line number Diff line change
@@ -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: 'http://{defaultHost}',
variables: {
defaultHost: {
default: 'localhost:3000'
}
}
}
]
}
}

# 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
104 changes: 104 additions & 0 deletions swagger/v1/swagger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
openapi: 3.0.1
info:
title: API V1
version: v1
paths:
"/api/v1/users/{user_id}/posts":
get:
summary: list posts
tags:
- Posts
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: ApiToken
in: header
schema:
type: string
responses:
'200':
description: successful
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
title:
type: string
text:
type: string
comments_counter:
type: integer
likes_counter:
type: integer
author_id:
type: integer
created_at:
type: string
updated_at:
type: string
required:
- author_id
- title
- text
- comments_counter
- likes_counter
- created_at
- updated_at
'404':
description: invalid request
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
message:
type: array
items:
type: string
post:
summary: create post
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: ApiToken
in: header
schema:
type: string
responses:
'201':
description: Post created
'422':
description: invalid request
requestBody:
content:
application/json:
schema:
type: object
properties:
title:
type: string
text:
type: string
required:
- user_id
- title
- text
servers:
- url: http://{defaultHost}
variables:
defaultHost:
default: localhost:3000