Compare commits
34 Commits
c37713af8f
...
5f419c64e3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5f419c64e3 | ||
300524956b | |||
80c1c9b99d | |||
1f81dabff4 | |||
f1d1ea825c | |||
7542c6361c | |||
7bdfb4f789 | |||
31d41ea2ea | |||
29c9764450 | |||
1b2c0f0d0a | |||
08f7c1e584 | |||
b3cfde445c | |||
![]() |
b008777c20 | ||
b0ee052b64 | |||
5f47b923d5 | |||
17c796c375 | |||
73e02a9d95 | |||
86b9d0b56c | |||
8e799bfd2b | |||
6e5bbb7b1b | |||
d73f59b05c | |||
5ba1591b02 | |||
b7714051e4 | |||
a8c9c051f0 | |||
6f6a6aaabf | |||
![]() |
6709c6b1f1 | ||
21d3aa5f4b | |||
cc3c8fdd63 | |||
94b1066c17 | |||
ca0b1b18d3 | |||
41cb719bf4 | |||
bcbcf9b469 | |||
2bbcdfbd98 | |||
8a3469447b |
3
.gitignore
vendored
3
.gitignore
vendored
@ -33,3 +33,6 @@
|
|||||||
|
|
||||||
# Ignore master key for decrypting credentials and more.
|
# Ignore master key for decrypting credentials and more.
|
||||||
/config/master.key
|
/config/master.key
|
||||||
|
|
||||||
|
# Ignore swagger generated documentation
|
||||||
|
swagger/v1/swagger.yaml
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# syntax = docker/dockerfile:1
|
# syntax = docker/dockerfile:1
|
||||||
|
|
||||||
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
|
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
|
||||||
ARG RUBY_VERSION=3.3.5
|
ARG RUBY_VERSION=3.3.6
|
||||||
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
|
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
|
||||||
|
|
||||||
# Rails app lives here
|
# Rails app lives here
|
||||||
|
1
Gemfile
1
Gemfile
@ -28,6 +28,7 @@ group :development, :test do
|
|||||||
gem 'license_finder'
|
gem 'license_finder'
|
||||||
gem 'pry'
|
gem 'pry'
|
||||||
gem 'rspec-rails', '~> 7.1.0'
|
gem 'rspec-rails', '~> 7.1.0'
|
||||||
|
gem 'rswag'
|
||||||
gem 'shoulda-matchers', '~> 6.0'
|
gem 'shoulda-matchers', '~> 6.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
29
Gemfile.lock
29
Gemfile.lock
@ -72,6 +72,8 @@ GEM
|
|||||||
securerandom (>= 0.3)
|
securerandom (>= 0.3)
|
||||||
tzinfo (~> 2.0, >= 2.0.5)
|
tzinfo (~> 2.0, >= 2.0.5)
|
||||||
uri (>= 0.13.1)
|
uri (>= 0.13.1)
|
||||||
|
addressable (2.8.7)
|
||||||
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
annotaterb (4.13.0)
|
annotaterb (4.13.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
babel-source (5.8.35)
|
babel-source (5.8.35)
|
||||||
@ -127,6 +129,8 @@ GEM
|
|||||||
actionview (>= 5.0.0)
|
actionview (>= 5.0.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
json (2.7.5)
|
json (2.7.5)
|
||||||
|
json-schema (5.0.1)
|
||||||
|
addressable (~> 2.8)
|
||||||
jsonapi-deserializable (0.2.0)
|
jsonapi-deserializable (0.2.0)
|
||||||
jsonapi-parser (0.1.1)
|
jsonapi-parser (0.1.1)
|
||||||
jsonapi-rails (0.4.1)
|
jsonapi-rails (0.4.1)
|
||||||
@ -157,9 +161,9 @@ GEM
|
|||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
marcel (1.0.4)
|
marcel (1.0.4)
|
||||||
method_source (1.0.0)
|
method_source (1.1.0)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
minitest (5.25.1)
|
minitest (5.25.2)
|
||||||
money (6.19.0)
|
money (6.19.0)
|
||||||
i18n (>= 0.6.4, <= 2)
|
i18n (>= 0.6.4, <= 2)
|
||||||
msgpack (1.7.2)
|
msgpack (1.7.2)
|
||||||
@ -190,11 +194,12 @@ GEM
|
|||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
pg (1.5.9)
|
pg (1.5.9)
|
||||||
pry (0.14.2)
|
pry (0.15.0)
|
||||||
coderay (~> 1.1)
|
coderay (~> 1.1)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
psych (5.2.0)
|
psych (5.2.0)
|
||||||
stringio
|
stringio
|
||||||
|
public_suffix (6.0.1)
|
||||||
puma (6.4.3)
|
puma (6.4.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
@ -272,6 +277,21 @@ GEM
|
|||||||
rspec-mocks (~> 3.13)
|
rspec-mocks (~> 3.13)
|
||||||
rspec-support (~> 3.13)
|
rspec-support (~> 3.13)
|
||||||
rspec-support (3.13.1)
|
rspec-support (3.13.1)
|
||||||
|
rswag (2.16.0)
|
||||||
|
rswag-api (= 2.16.0)
|
||||||
|
rswag-specs (= 2.16.0)
|
||||||
|
rswag-ui (= 2.16.0)
|
||||||
|
rswag-api (2.16.0)
|
||||||
|
activesupport (>= 5.2, < 8.1)
|
||||||
|
railties (>= 5.2, < 8.1)
|
||||||
|
rswag-specs (2.16.0)
|
||||||
|
activesupport (>= 5.2, < 8.1)
|
||||||
|
json-schema (>= 2.2, < 6.0)
|
||||||
|
railties (>= 5.2, < 8.1)
|
||||||
|
rspec-core (>= 2.14)
|
||||||
|
rswag-ui (2.16.0)
|
||||||
|
actionpack (>= 5.2, < 8.1)
|
||||||
|
railties (>= 5.2, < 8.1)
|
||||||
rubocop (1.68.0)
|
rubocop (1.68.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (>= 3.17.0)
|
language_server-protocol (>= 3.17.0)
|
||||||
@ -291,7 +311,7 @@ GEM
|
|||||||
securerandom (0.3.2)
|
securerandom (0.3.2)
|
||||||
shoulda-matchers (6.4.0)
|
shoulda-matchers (6.4.0)
|
||||||
activesupport (>= 5.2.0)
|
activesupport (>= 5.2.0)
|
||||||
solid_queue (1.0.1)
|
solid_queue (1.0.2)
|
||||||
activejob (>= 7.1)
|
activejob (>= 7.1)
|
||||||
activerecord (>= 7.1)
|
activerecord (>= 7.1)
|
||||||
concurrent-ruby (>= 1.3.1)
|
concurrent-ruby (>= 1.3.1)
|
||||||
@ -362,6 +382,7 @@ DEPENDENCIES
|
|||||||
react-rails
|
react-rails
|
||||||
redis (>= 4.0.1)
|
redis (>= 4.0.1)
|
||||||
rspec-rails (~> 7.1.0)
|
rspec-rails (~> 7.1.0)
|
||||||
|
rswag
|
||||||
rubocop
|
rubocop
|
||||||
rubytree
|
rubytree
|
||||||
shoulda-matchers (~> 6.0)
|
shoulda-matchers (~> 6.0)
|
||||||
|
13
README.md
13
README.md
@ -42,8 +42,7 @@ projects <or anything else>
|
|||||||
Docker compose is the recommended way to run Libre Wedding Planner for development purposes. After downloading both repositories, `cd` to the root of `wedding-planner` and run:
|
Docker compose is the recommended way to run Libre Wedding Planner for development purposes. After downloading both repositories, `cd` to the root of `wedding-planner` and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose build
|
docker compose up --build
|
||||||
docker compose up
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Several containers will be started:
|
Several containers will be started:
|
||||||
@ -68,6 +67,16 @@ Unit tests can be executed with
|
|||||||
bundle exec rspec
|
bundle exec rspec
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## API documentation
|
||||||
|
|
||||||
|
Generate the OpenAPI documentation with the command:
|
||||||
|
|
||||||
|
```
|
||||||
|
rake rswag:specs:swaggerize
|
||||||
|
```
|
||||||
|
|
||||||
|
The documentation is available in Swagger UI in http://libre-wedding-planner.app.localhost/api/api-docs/index.html. If testing the API through the UI, you will need to select the second server (which includes the `/api` path), intended for development.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributions of all kinds (code, UX/UI, testing, translations, etc.) are welcome. The procedures to contribute are still being defined, but don't hesitate to reach out in case you want to participate.
|
Contributions of all kinds (code, UX/UI, testing, translations, etc.) are welcome. The procedures to contribute are still being defined, but don't hesitate to reach out in case you want to participate.
|
||||||
|
@ -1,15 +1,43 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
after_action :set_csrf_cookie
|
after_action :set_csrf_cookie
|
||||||
|
|
||||||
private
|
skip_before_action :verify_authenticity_token, if: :development_swagger?
|
||||||
|
|
||||||
def set_csrf_cookie
|
rescue_from ActiveRecord::RecordInvalid do |exception|
|
||||||
cookies["csrf-token"] = {
|
render json: {
|
||||||
value: form_authenticity_token,
|
message: 'Record invalid',
|
||||||
secure: Rails.env.production?,
|
errors: exception.record.errors.full_messages
|
||||||
same_site: :strict,
|
}, status: :unprocessable_entity
|
||||||
}
|
end
|
||||||
end
|
|
||||||
|
rescue_from ActionController::ParameterMissing do |exception|
|
||||||
|
render json: {
|
||||||
|
message: 'Parameter missing',
|
||||||
|
errors: [exception.message]
|
||||||
|
}, status: :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue_from ActiveRecord::RecordNotFound do |exception|
|
||||||
|
render json: {
|
||||||
|
message: 'Record not found',
|
||||||
|
errors: [exception.message]
|
||||||
|
}, status: :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def development_swagger?
|
||||||
|
Rails.env.test? ||
|
||||||
|
Rails.env.development? && request.headers['referer'].include?('/api-docs/index.html')
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_csrf_cookie
|
||||||
|
cookies['csrf-token'] = {
|
||||||
|
value: form_authenticity_token,
|
||||||
|
secure: Rails.env.production?,
|
||||||
|
same_site: :strict
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,20 +4,30 @@ require 'csv'
|
|||||||
|
|
||||||
class GuestsController < ApplicationController
|
class GuestsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@guests = Guest.all.includes(:group)
|
render json: Guest.all.includes(:group)
|
||||||
.joins(:group)
|
.joins(:group)
|
||||||
.order('groups.name' => :asc, name: :asc)
|
.order('groups.name' => :asc, name: :asc)
|
||||||
|
.as_json(only: %i[id name status], include: { group: { only: %i[id name] } })
|
||||||
|
end
|
||||||
|
|
||||||
render jsonapi: @guests
|
def create
|
||||||
|
Guest.create!(guest_params)
|
||||||
|
render json: {}, status: :created
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
Guests::UpdateUseCase.new(guest_ids: [params[:id]], params: params.require(:guest).permit(:name)).call
|
Guest.find(params[:id]).update!(guest_params)
|
||||||
render json: {}, status: :ok
|
render json: {}, status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def bulk_update
|
def destroy
|
||||||
Guests::UpdateUseCase.new(guest_ids: params[:guest_ids], params: params.require(:properties).permit(:status)).call
|
Guest.find(params[:id]).destroy!
|
||||||
render json: {}, status: :ok
|
render json: {}, status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def guest_params
|
||||||
|
params.require(:guest).permit(:name, :group_id, :status)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,4 +12,11 @@
|
|||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
#
|
#
|
||||||
class Expense < ApplicationRecord
|
class Expense < ApplicationRecord
|
||||||
|
enum :pricing_type,
|
||||||
|
fixed: 'fixed',
|
||||||
|
per_person: 'per_person'
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
validates :amount, presence: true, numericality: { greater_than: 0 }
|
||||||
|
validates :pricing_type, presence: true
|
||||||
end
|
end
|
||||||
|
@ -29,7 +29,22 @@ class Guest < ApplicationRecord
|
|||||||
confirmed: 20,
|
confirmed: 20,
|
||||||
declined: 30,
|
declined: 30,
|
||||||
tentative: 40
|
tentative: 40
|
||||||
}
|
}, validate: true
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
|
||||||
scope :potential, -> { where.not(status: %i[declined considered]) }
|
scope :potential, -> { where.not(status: %i[declined considered]) }
|
||||||
|
|
||||||
|
after_save :recalculate_simulations, if: :saved_change_to_status?
|
||||||
|
after_destroy :recalculate_simulations
|
||||||
|
|
||||||
|
has_many :seats, dependent: :delete_all
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def recalculate_simulations
|
||||||
|
TablesArrangement.delete_all
|
||||||
|
|
||||||
|
ActiveJob.perform_all_later(50.times.map { TableSimulatorJob.new })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,7 +10,7 @@ module Groups
|
|||||||
|
|
||||||
def query
|
def query
|
||||||
<<~SQL.squish
|
<<~SQL.squish
|
||||||
SELECT#{' '}
|
SELECT
|
||||||
groups.id,
|
groups.id,
|
||||||
groups.name,
|
groups.name,
|
||||||
groups.icon,
|
groups.icon,
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
|
||||||
|
|
||||||
module Guests
|
|
||||||
class UpdateUseCase
|
|
||||||
private attr_reader :guest_ids, :params
|
|
||||||
def initialize(guest_ids:, params:)
|
|
||||||
@guest_ids = guest_ids
|
|
||||||
@params = params
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
Guest.where(id: guest_ids).update!(params)
|
|
||||||
|
|
||||||
# TODO: Not all status transitions may require a table re-arrangement
|
|
||||||
return unless params.key?(:status)
|
|
||||||
|
|
||||||
TablesArrangement.delete_all
|
|
||||||
|
|
||||||
ActiveJob.perform_all_later(50.times.map { TableSimulatorJob.new })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
16
config/initializers/rswag_api.rb
Normal file
16
config/initializers/rswag_api.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
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.openapi_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
|
18
config/initializers/rswag_ui.rb
Normal file
18
config/initializers/rswag_ui.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
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 openapi_root) as JSON or YAML endpoints, then the list below should
|
||||||
|
# correspond to the relative paths for those endpoints.
|
||||||
|
|
||||||
|
c.swagger_endpoint '/api/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
|
@ -1,8 +1,10 @@
|
|||||||
# Copyright (C) 2024 Manuel Bustillo
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
mount Rswag::Ui::Engine => '/api-docs'
|
||||||
|
mount Rswag::Api::Engine => '/api-docs'
|
||||||
resources :groups, only: :index
|
resources :groups, only: :index
|
||||||
resources :guests, only: %i[index update] do
|
resources :guests, only: %i[index create update destroy] do
|
||||||
post :bulk_update, on: :collection
|
post :bulk_update, on: :collection
|
||||||
end
|
end
|
||||||
resources :expenses, only: %i[index update] do
|
resources :expenses, only: %i[index update] do
|
||||||
|
@ -3,5 +3,10 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Expense, type: :model do
|
RSpec.describe Expense, type: :model do
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
describe 'validations' do
|
||||||
|
it { should validate_presence_of(:name) }
|
||||||
|
it { should validate_presence_of(:amount) }
|
||||||
|
it { should validate_numericality_of(:amount).is_greater_than(0) }
|
||||||
|
it { should validate_presence_of(:pricing_type) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,14 +3,17 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Guest, type: :model do
|
RSpec.describe Guest, type: :model do
|
||||||
it do
|
describe 'validations' do
|
||||||
should define_enum_for(:status).with_values(
|
it { should validate_presence_of(:name) }
|
||||||
considered: 0,
|
it do
|
||||||
invited: 10,
|
should define_enum_for(:status).with_values(
|
||||||
confirmed: 20,
|
considered: 0,
|
||||||
declined: 30,
|
invited: 10,
|
||||||
tentative: 40
|
confirmed: 20,
|
||||||
)
|
declined: 30,
|
||||||
|
tentative: 40
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it { should belong_to(:group) }
|
it { should belong_to(:group) }
|
||||||
|
49
spec/requests/expenses_spec.rb
Normal file
49
spec/requests/expenses_spec.rb
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
require 'swagger_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'expenses', type: :request do
|
||||||
|
path '/expenses' do
|
||||||
|
get('list expenses') do
|
||||||
|
tags 'Expenses'
|
||||||
|
produces 'application/json'
|
||||||
|
response(200, 'successful') do
|
||||||
|
schema type: :array,
|
||||||
|
items: {
|
||||||
|
type: :object,
|
||||||
|
required: %i[id name amount pricing_type],
|
||||||
|
properties: {
|
||||||
|
id: { type: :string, format: :uuid },
|
||||||
|
name: { type: :string },
|
||||||
|
amount: { type: :number },
|
||||||
|
pricing_type: { type: :string, enum: Expense.pricing_types.keys }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path '/expenses/{id}' do
|
||||||
|
|
||||||
|
patch('update expense') do
|
||||||
|
tags 'Expenses'
|
||||||
|
consumes 'application/json'
|
||||||
|
produces 'application/json'
|
||||||
|
parameter name: 'id', in: :path, type: :string, format: :uuid, description: 'id'
|
||||||
|
parameter name: :body, in: :body, schema: {
|
||||||
|
type: :object,
|
||||||
|
properties: {
|
||||||
|
name: { type: :string },
|
||||||
|
amount: { type: :number, minimum: 0 },
|
||||||
|
pricing_type: { type: :string, enum: Expense.pricing_types.keys }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response_empty_200
|
||||||
|
response_422
|
||||||
|
response_404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
33
spec/requests/groups_spec.rb
Normal file
33
spec/requests/groups_spec.rb
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
require 'swagger_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'groups', type: :request do
|
||||||
|
path '/groups' do
|
||||||
|
get('list groups') do
|
||||||
|
tags 'Groups'
|
||||||
|
produces 'application/json'
|
||||||
|
response(200, 'successful') do
|
||||||
|
schema type: :array,
|
||||||
|
items: {
|
||||||
|
type: :object,
|
||||||
|
required: %i[id name icon parent_id color total considered invited confirmed declined tentative],
|
||||||
|
properties: {
|
||||||
|
id: { type: :string, format: :uuid, required: true },
|
||||||
|
name: { type: :string },
|
||||||
|
icon: { type: :string, example: 'pi pi-crown', description: 'The CSS classes used by the icon' },
|
||||||
|
parent_id: { type: :string, format: :uuid },
|
||||||
|
color: { type: :string, pattern: '^#(?:[0-9a-fA-F]{3}){1,2}$' },
|
||||||
|
total: { type: :integer, minimum: 0, description: 'Total number of guests in any status' },
|
||||||
|
considered: { type: :integer, minimum: 0 },
|
||||||
|
invited: { type: :integer, minimum: 0 },
|
||||||
|
confirmed: { type: :integer, minimum: 0 },
|
||||||
|
declined: { type: :integer, minimum: 0 },
|
||||||
|
tentative: { type: :integer, minimum: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
91
spec/requests/guests_spec.rb
Normal file
91
spec/requests/guests_spec.rb
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
require 'swagger_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'guests', type: :request do
|
||||||
|
path '/guests' do
|
||||||
|
get('list guests') do
|
||||||
|
tags 'Guests'
|
||||||
|
produces 'application/json'
|
||||||
|
response(200, 'successful') do
|
||||||
|
schema type: :array,
|
||||||
|
items: {
|
||||||
|
type: :object,
|
||||||
|
required: %i[id name status group],
|
||||||
|
properties: {
|
||||||
|
id: { type: :string, format: :uuid },
|
||||||
|
name: { type: :string },
|
||||||
|
status: { type: :string, enum: Guest.statuses.keys },
|
||||||
|
group: { type: :object,
|
||||||
|
required: %i[id name],
|
||||||
|
properties: {
|
||||||
|
id: { type: :string, format: :uuid },
|
||||||
|
name: { type: :string }
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post('create guest') do
|
||||||
|
tags 'Guests'
|
||||||
|
consumes 'application/json'
|
||||||
|
produces 'application/json'
|
||||||
|
parameter name: :body, in: :body, schema: {
|
||||||
|
type: :object,
|
||||||
|
required: %i[guest],
|
||||||
|
properties: {
|
||||||
|
guest: {
|
||||||
|
type: :object,
|
||||||
|
required: %i[name group_id status],
|
||||||
|
properties: {
|
||||||
|
name: { type: :string },
|
||||||
|
group_id: { type: :string, format: :uuid },
|
||||||
|
status: { type: :string, enum: Guest.statuses.keys }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response_empty_201
|
||||||
|
response_422
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path '/guests/{id}' do
|
||||||
|
patch('update guest') do
|
||||||
|
tags 'Guests'
|
||||||
|
consumes 'application/json'
|
||||||
|
produces 'application/json'
|
||||||
|
parameter name: 'id', in: :path, type: :string, format: :uuid
|
||||||
|
parameter name: :body, in: :body, schema: {
|
||||||
|
type: :object,
|
||||||
|
required: %i[guest],
|
||||||
|
properties: {
|
||||||
|
guest: {
|
||||||
|
type: :object,
|
||||||
|
properties: {
|
||||||
|
name: { type: :string },
|
||||||
|
group_id: { type: :string, format: :uuid },
|
||||||
|
status: { type: :string, enum: Guest.statuses.keys }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response_empty_200
|
||||||
|
response_422
|
||||||
|
response_404
|
||||||
|
end
|
||||||
|
|
||||||
|
delete('delete guest') do
|
||||||
|
tags 'Guests'
|
||||||
|
produces 'application/json'
|
||||||
|
parameter name: 'id', in: :path, type: :string, format: :uuid
|
||||||
|
|
||||||
|
response_empty_200
|
||||||
|
response_404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
44
spec/swagger_helper.rb
Normal file
44
spec/swagger_helper.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
require_relative './swagger_response_helper'
|
||||||
|
|
||||||
|
include SwaggerResponseHelper
|
||||||
|
|
||||||
|
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.openapi_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 openapi_root
|
||||||
|
# By default, the operations defined in spec files are added to the first
|
||||||
|
# document below. You can override this behavior by adding a openapi_spec tag to the
|
||||||
|
# the root example_group in your specs, e.g. describe '...', openapi_spec: 'v2/swagger.json'
|
||||||
|
config.openapi_specs = {
|
||||||
|
'v1/swagger.yaml' => {
|
||||||
|
openapi: '3.0.1',
|
||||||
|
info: {
|
||||||
|
title: 'API V1',
|
||||||
|
version: 'v1'
|
||||||
|
},
|
||||||
|
paths: {},
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: 'http://libre-wedding-planner.app.localhost/api',
|
||||||
|
description: 'Suitable for development'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'.
|
||||||
|
# The openapi_specs 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.openapi_format = :yaml
|
||||||
|
end
|
46
spec/swagger_response_helper.rb
Normal file
46
spec/swagger_response_helper.rb
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright (C) 2024 Manuel Bustillo
|
||||||
|
|
||||||
|
module SwaggerResponseHelper
|
||||||
|
def response_422
|
||||||
|
response(422, 'Validation errors in input parameters') do
|
||||||
|
produces 'application/json'
|
||||||
|
error_schema
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def response_empty_200
|
||||||
|
response(200, 'Success') do
|
||||||
|
produces 'application/json'
|
||||||
|
schema type: :object
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def response_empty_201
|
||||||
|
response(201, 'Created') do
|
||||||
|
produces 'application/json'
|
||||||
|
schema type: :object
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def response_404
|
||||||
|
response(404, 'Record not found') do
|
||||||
|
produces 'application/json'
|
||||||
|
error_schema
|
||||||
|
xit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def error_schema
|
||||||
|
schema type: :object,
|
||||||
|
required: %i[message errors],
|
||||||
|
properties: {
|
||||||
|
message: { type: :string },
|
||||||
|
errors: { type: :array, items: { type: :string } }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user