From 379830d2bd8d7681f7159337ab51d7efc6e59e62 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Fri, 7 Sep 2018 10:28:05 +0300 Subject: [PATCH 01/26] Update Ruby to 2.4.4 --- .ruby-version | 2 +- Gemfile | 4 ++-- Gemfile.lock | 21 +++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.ruby-version b/.ruby-version index e75da3e..79a6144 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.6 +2.4.4 diff --git a/Gemfile b/Gemfile index f214d12..85148fc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -ruby "2.3.6" +ruby "2.4.4" # the most important stuff gem "rails", "4.2.8" @@ -9,7 +9,7 @@ gem "rails-api" gem "rails_api_format", git: "https://github.com/fs/rails-api-format.git" # all other gems -gem "active_model_serializers", git: "https://github.com/rails-api/active_model_serializers.git" +gem "active_model_serializers" gem "decent_exposure" gem "devise" gem "health_check" diff --git a/Gemfile.lock b/Gemfile.lock index 06b91fb..8599d32 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,13 +8,6 @@ GIT rails (~> 4.2.3) responders (~> 2.1.0) -GIT - remote: https://github.com/rails-api/active_model_serializers.git - revision: 6aba26049154712d65176708ffad55fcfe512ca0 - specs: - active_model_serializers (0.10.0.rc2) - activemodel (>= 4.0) - GEM remote: https://rubygems.org/ specs: @@ -37,6 +30,11 @@ GEM erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.3) + active_model_serializers (0.10.7) + actionpack (>= 4.1, < 6) + activemodel (>= 4.1, < 6) + case_transform (>= 0.2) + jsonapi-renderer (>= 0.1.1.beta1, < 0.3) activejob (4.2.8) activesupport (= 4.2.8) globalid (>= 0.3.0) @@ -79,6 +77,8 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (6.0.2) + case_transform (0.2) + activesupport concurrent-ruby (1.0.5) crack (0.4.2) safe_yaml (~> 1.0.0) @@ -129,6 +129,7 @@ GEM json_spec (1.1.4) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) + jsonapi-renderer (0.2.0) kaminari (0.16.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) @@ -285,7 +286,7 @@ PLATFORMS ruby DEPENDENCIES - active_model_serializers! + active_model_serializers apitome brakeman bullet @@ -325,7 +326,7 @@ DEPENDENCIES webmock RUBY VERSION - ruby 2.3.6p384 + ruby 2.4.4p296 BUNDLED WITH - 1.16.3 + 1.16.4 From 41b1a576f0d98680e708eced8e3ce5c2416e7ade Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Fri, 7 Sep 2018 12:20:29 +0300 Subject: [PATCH 02/26] WIP --- .env.example | 39 +- .ruby-version | 2 +- Gemfile | 17 +- Gemfile.lock | 429 ++++++++---------- app/models/user.rb | 8 +- bin/bundle | 2 +- bin/quality | 8 +- bin/rails | 7 +- bin/rake | 5 +- bin/rspec | 5 +- bin/server | 2 +- bin/setup | 7 +- bin/spring | 14 +- bin/update | 28 ++ bin/yarn | 11 + config/application.rb | 39 +- config/boot.rb | 6 +- config/cable.yml | 10 + config/environment.rb | 2 +- config/environments/development.rb | 32 +- config/environments/production.rb | 71 ++- config/environments/staging.rb | 61 +-- config/environments/test.rb | 15 +- config/initializers/01_config.rb | 7 - .../initializers/active_model_serializer.rb | 6 +- config/initializers/apitome.rb | 104 ++--- .../application_controller_renderer.rb | 8 + config/initializers/cookies_serializer.rb | 5 + config/initializers/cors.rb | 16 + config/initializers/devise.rb | 266 ----------- .../initializers/filter_parameter_logging.rb | 2 +- config/initializers/generators.rb | 2 +- config/initializers/mail_safe.rb | 3 - config/initializers/mailer.rb | 1 - config/initializers/mime_types.rb | 1 - config/initializers/rack_cors.rb | 14 - config/initializers/requires.rb | 5 - .../initializers/rspec_api_documentation.rb | 2 +- .../simple_token_authentication.rb | 64 --- config/initializers/wrap_parameters.rb | 10 +- config/locales/en.yml | 17 +- config/puma.rb | 34 ++ config/routes.rb | 10 +- config/secrets.yml | 33 +- config/spring.rb | 6 + config/storage.yml | 34 ++ db/migrate/20130319140714_create_users.rb | 11 + .../20130319140714_devise_create_users.rb | 46 -- db/schema.rb | 27 +- 49 files changed, 683 insertions(+), 871 deletions(-) create mode 100755 bin/update create mode 100755 bin/yarn create mode 100644 config/cable.yml delete mode 100644 config/initializers/01_config.rb create mode 100644 config/initializers/application_controller_renderer.rb create mode 100644 config/initializers/cookies_serializer.rb create mode 100644 config/initializers/cors.rb delete mode 100644 config/initializers/devise.rb delete mode 100644 config/initializers/mail_safe.rb delete mode 100644 config/initializers/mailer.rb delete mode 100644 config/initializers/rack_cors.rb delete mode 100644 config/initializers/requires.rb delete mode 100644 config/initializers/simple_token_authentication.rb create mode 100644 config/puma.rb create mode 100644 config/spring.rb create mode 100644 config/storage.yml create mode 100644 db/migrate/20130319140714_create_users.rb delete mode 100644 db/migrate/20130319140714_devise_create_users.rb diff --git a/.env.example b/.env.example index f2f5911..826aa65 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,37 @@ -# Add your API tokens here +# Port to serve application +PORT=5000 + +# Host name with PORT of the application +HOST=localhost:5000 + +# CORS hostnames based on https://github.com/cyu/rack-cors +# CORS_ORIGINS= + +# Current environment RACK_ENV=development -ALLOW_REQUESTS_FROM=localhost:4000 -SECRET_KEY_BASE=development -# ROLLBAR_ACCESS_TOKEN= +# Base secret key +SECRET_KEY_BASE=development_secret + +# Database name prefix +# DATABASE_NAME=paste_database_prefix_here + +# Specify assets server host name, eg.: d2oek0c5zwe48d.cloudfront.net +ASSET_HOST=lvh.me:5000 +S3_ASSET_HOST=https://d1ltghz9ehkb4n.cloudfront.net + +# Set Rollbar key for the app +# ROLLBAR_ACCESS_TOKEN=your_key_here + +# We send email using this "from" address +MAILER_SENDER_ADDRESS=noreply@example.com + +# Enable basic auth to close the app from unauthorized viewers +# AUTH_BASIC_REALM=your_application_name +# AUTH_BASIC_PASS=your_password_here + +# Set single hostname +# CANONICAL_HOST=paste_single_hostname_here + +# Comma separated list of IPs to access admin staff like RackMiniProfiler, Sidekiq Web, Flipper UI +IP_WHITELIST=127.0.0.1 diff --git a/.ruby-version b/.ruby-version index 79a6144..73462a5 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.4 +2.5.1 diff --git a/Gemfile b/Gemfile index 85148fc..6c33441 100644 --- a/Gemfile +++ b/Gemfile @@ -1,32 +1,30 @@ source "https://rubygems.org" -ruby "2.4.4" +ruby "2.5.1" # the most important stuff -gem "rails", "4.2.8" gem "pg" -gem "rails-api" -gem "rails_api_format", git: "https://github.com/fs/rails-api-format.git" +gem "rails", "5.2.1" # all other gems gem "active_model_serializers" +gem "bcrypt" +gem "bootsnap", require: false gem "decent_exposure" -gem "devise" gem "health_check" gem "interactor" gem "kaminari" -gem "rack-cors", require: "rack/cors" +gem "puma" gem "responders" gem "rollbar" gem "seedbank" -gem "simple_token_authentication" -gem "thin" group :development do gem "letter_opener" gem "foreman" gem "bullet" + gem "listen", ">= 3.0.5", "< 3.2" gem "spring" gem "spring-commands-rspec" end @@ -35,7 +33,6 @@ group :development, :test do gem "byebug" gem "dotenv-rails" gem "rspec-rails" - gem "mail_safe" gem "brakeman" gem "rubocop" @@ -56,5 +53,5 @@ group :development, :test, :staging do gem "faker" gem "factory_girl_rails" gem "rspec_api_documentation" - gem "apitome" + # gem "apitome" end diff --git a/Gemfile.lock b/Gemfile.lock index 8599d32..085258d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,300 +1,277 @@ -GIT - remote: https://github.com/fs/rails-api-format.git - revision: c3acffa0fb559c9bb9fa1f615ab60acc256f3c0b - specs: - rails_api_format (0.0.2) - active_model_serializers (~> 0.10.0.rc2) - devise (~> 3.5.1) - rails (~> 4.2.3) - responders (~> 2.1.0) - GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.8) - actionpack (= 4.2.8) - actionview (= 4.2.8) - activejob (= 4.2.8) + actioncable (5.2.1) + actionpack (= 5.2.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.1) + actionpack (= 5.2.1) + actionview (= 5.2.1) + activejob (= 5.2.1) mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.8) - actionview (= 4.2.8) - activesupport (= 4.2.8) - rack (~> 1.6) - rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.5) + rails-dom-testing (~> 2.0) + actionpack (5.2.1) + actionview (= 5.2.1) + activesupport (= 5.2.1) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.8) - activesupport (= 4.2.8) + actionview (5.2.1) + activesupport (= 5.2.1) builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.5) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) active_model_serializers (0.10.7) actionpack (>= 4.1, < 6) activemodel (>= 4.1, < 6) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (4.2.8) - activesupport (= 4.2.8) - globalid (>= 0.3.0) - activemodel (4.2.8) - activesupport (= 4.2.8) - builder (~> 3.1) - activerecord (4.2.8) - activemodel (= 4.2.8) - activesupport (= 4.2.8) - arel (~> 6.0) - activesupport (4.2.8) - i18n (~> 0.7) + activejob (5.2.1) + activesupport (= 5.2.1) + globalid (>= 0.3.6) + activemodel (5.2.1) + activesupport (= 5.2.1) + activerecord (5.2.1) + activemodel (= 5.2.1) + activesupport (= 5.2.1) + arel (>= 9.0) + activestorage (5.2.1) + actionpack (= 5.2.1) + activerecord (= 5.2.1) + marcel (~> 0.3.1) + activesupport (5.2.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.3.6) - apitome (0.0.7) - github-markdown - railties (>= 3.2.5, < 5) - rspec_api_documentation - arel (6.0.4) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + arel (9.0.0) ast (2.4.0) - bcrypt (3.1.10) - brakeman (2.4.3) - erubis (~> 2.6) - fastercsv (~> 1.5) - haml (>= 3.0, < 5.0) - highline (~> 1.6.20) - multi_json (~> 1.2) - ruby2ruby (~> 2.0.5) - ruby_parser (~> 3.4.0) - sass (~> 3.0) - slim (>= 1.3.6, < 3.0) - terminal-table (~> 1.4) + bcrypt (3.1.12) + bootsnap (1.3.1) + msgpack (~> 1.0) + brakeman (4.3.1) builder (3.2.3) - bullet (4.14.7) + bullet (5.7.6) activesupport (>= 3.0.0) - uniform_notifier (~> 1.9.0) - bundler-audit (0.3.1) + uniform_notifier (~> 1.11.0) + bundler-audit (0.6.0) bundler (~> 1.2) thor (~> 0.18) - byebug (6.0.2) + byebug (10.0.2) case_transform (0.2) activesupport concurrent-ruby (1.0.5) - crack (0.4.2) + crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.3) - daemons (1.1.9) - database_cleaner (1.2.0) - decent_exposure (3.0.0) + crass (1.0.4) + database_cleaner (1.7.0) + decent_exposure (3.0.2) activesupport (>= 4.0) - devise (3.5.6) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 3.2.6, < 5) - responders - thread_safe (~> 0.1) - warden (~> 1.2.3) - diff-lcs (1.2.5) - docile (1.1.3) - dotenv (0.10.0) - dotenv-rails (0.10.0) - dotenv (= 0.10.0) - email_spec (1.5.0) + diff-lcs (1.3) + docile (1.3.1) + dotenv (2.5.0) + dotenv-rails (2.5.0) + dotenv (= 2.5.0) + railties (>= 3.2, < 6.0) + email_spec (2.2.0) + htmlentities (~> 4.3.3) launchy (~> 2.1) - mail (~> 2.2) - erubis (2.7.0) - eventmachine (1.0.7) - factory_girl (4.4.0) + mail (~> 2.7) + erubi (1.7.1) + factory_girl (4.9.0) activesupport (>= 3.0.0) - factory_girl_rails (4.4.1) - factory_girl (~> 4.4.0) + factory_girl_rails (4.9.0) + factory_girl (~> 4.9.0) railties (>= 3.0.0) - faker (1.6.6) - i18n (~> 0.5) - fastercsv (1.5.5) - foreman (0.63.0) - dotenv (>= 0.7) - thor (>= 0.13.6) - github-markdown (0.6.6) - globalid (0.3.7) - activesupport (>= 4.1.0) - haml (4.0.5) - tilt - health_check (2.2.1) - rails (>= 4.0) - highline (1.6.21) - i18n (0.8.1) - interactor (3.1.0) - json (1.8.6) - json_spec (1.1.4) + faker (1.9.1) + i18n (>= 0.7) + ffi (1.9.25) + foreman (0.85.0) + thor (~> 0.19.1) + globalid (0.4.1) + activesupport (>= 4.2.0) + hashdiff (0.3.7) + health_check (3.0.0) + railties (>= 5.0) + htmlentities (4.3.4) + i18n (1.1.0) + concurrent-ruby (~> 1.0) + interactor (3.1.1) + jaro_winkler (1.5.1) + json (2.1.0) + json_spec (1.1.5) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) jsonapi-renderer (0.2.0) - kaminari (0.16.1) - actionpack (>= 3.0.0) - activesupport (>= 3.0.0) - launchy (2.4.2) + kaminari (1.1.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) + actionview + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) + activerecord + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) + launchy (2.4.3) addressable (~> 2.3) - letter_opener (1.2.0) + letter_opener (1.6.0) launchy (~> 2.2) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.6.4) - mime-types (>= 1.16, < 4) - mail_safe (0.3.1) - actionmailer (>= 1.3.6) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) + mail (2.7.0) + mini_mime (>= 0.1.1) + marcel (0.3.2) + mimemagic (~> 0.3.2) + method_source (0.9.0) + mimemagic (0.3.2) + mini_mime (1.0.1) mini_portile2 (2.3.0) - minitest (5.10.1) - multi_json (1.12.1) - mustache (0.99.8) + minitest (5.11.3) + msgpack (1.2.4) + multi_json (1.13.1) + mustache (1.0.5) + nio4r (2.3.1) nokogiri (1.8.4) mini_portile2 (~> 2.3.0) - orm_adapter (0.5.0) parallel (1.12.1) - parser (2.5.0.5) + parser (2.5.1.2) ast (~> 2.4.0) - pg (0.17.1) - powerpack (0.1.1) - rack (1.6.10) - rack-cors (1.0.2) - rack-test (0.6.3) - rack (>= 1.0) - rails (4.2.8) - actionmailer (= 4.2.8) - actionpack (= 4.2.8) - actionview (= 4.2.8) - activejob (= 4.2.8) - activemodel (= 4.2.8) - activerecord (= 4.2.8) - activesupport (= 4.2.8) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.8) - sprockets-rails - rails-api (0.4.1) - actionpack (>= 3.2.11) - railties (>= 3.2.11) - rails-deprecated_sanitizer (1.0.3) - activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.8) - activesupport (>= 4.2.0.beta, < 5.0) - nokogiri (~> 1.6) - rails-deprecated_sanitizer (>= 1.0.1) + pg (1.1.3) + powerpack (0.1.2) + public_suffix (3.0.3) + puma (3.12.0) + rack (2.0.5) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.2.1) + actioncable (= 5.2.1) + actionmailer (= 5.2.1) + actionpack (= 5.2.1) + actionview (= 5.2.1) + activejob (= 5.2.1) + activemodel (= 5.2.1) + activerecord (= 5.2.1) + activestorage (= 5.2.1) + activesupport (= 5.2.1) + bundler (>= 1.3.0) + railties (= 5.2.1) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) rails-html-sanitizer (1.0.4) loofah (~> 2.2, >= 2.2.2) - railties (4.2.8) - actionpack (= 4.2.8) - activesupport (= 4.2.8) + railties (5.2.1) + actionpack (= 5.2.1) + activesupport (= 5.2.1) + method_source rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) + thor (>= 0.19.0, < 2.0) rainbow (3.0.0) - rake (11.3.0) - responders (2.1.1) - railties (>= 4.2.0, < 5.1) - rollbar (2.12.0) + rake (12.3.1) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) + rollbar (2.17.0) multi_json - rspec (3.3.0) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-core (3.3.2) - rspec-support (~> 3.3.0) - rspec-expectations (3.3.1) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.0) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-mocks (3.3.2) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-rails (3.3.3) - actionpack (>= 3.0, < 4.3) - activesupport (>= 3.0, < 4.3) - railties (>= 3.0, < 4.3) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-support (~> 3.3.0) - rspec-support (3.3.0) - rspec_api_documentation (4.4.0) + rspec-support (~> 3.8.0) + rspec-rails (3.8.0) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) + rspec_api_documentation (6.0.0) activesupport (>= 3.0.0) - json (~> 1.4, >= 1.4.6) - mustache (~> 0.99, >= 0.99.4) - rspec (>= 3.0.0) - rubocop (0.54.0) + mustache (~> 1.0, >= 0.99.4) + rspec (~> 3.0) + rubocop (0.58.2) + jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.5) + parser (>= 2.5, != 2.5.1.1) powerpack (~> 0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - ruby-progressbar (1.7.5) - ruby2ruby (2.0.8) - ruby_parser (~> 3.1) - sexp_processor (~> 4.0) - ruby_parser (3.4.1) - sexp_processor (~> 4.1) - safe_yaml (1.0.2) - sass (3.3.4) - seedbank (0.3.0) - sexp_processor (4.4.3) - shoulda-matchers (2.8.0) - activesupport (>= 3.0.0) - simple_token_authentication (1.10.0) - actionmailer (>= 3.2.6, < 5) - actionpack (>= 3.2.6, < 5) - devise (~> 3.2) - simplecov (0.8.2) - docile (~> 1.1.0) - multi_json - simplecov-html (~> 0.8.0) - simplecov-html (0.8.0) - slim (2.0.2) - temple (~> 0.6.6) - tilt (>= 1.3.3, < 2.1) - spring (1.3.6) + ruby-progressbar (1.10.0) + ruby_dep (1.5.0) + safe_yaml (1.0.4) + seedbank (0.4.0) + shoulda-matchers (3.1.2) + activesupport (>= 4.0.0) + simplecov (0.16.1) + docile (~> 1.1) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.2) + spring (2.0.2) + activesupport (>= 4.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - temple (0.6.7) - terminal-table (1.4.5) - thin (1.6.2) - daemons (>= 1.0.9) - eventmachine (>= 1.0.0) - rack (>= 1.0.0) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.1) - tzinfo (1.2.2) + tzinfo (1.2.5) thread_safe (~> 0.1) - unicode-display_width (1.3.0) - uniform_notifier (1.9.0) - warden (1.2.6) - rack (>= 1.0) - webmock (1.17.4) - addressable (>= 2.2.7) + unicode-display_width (1.4.0) + uniform_notifier (1.11.0) + webmock (3.4.2) + addressable (>= 2.3.6) crack (>= 0.3.2) + hashdiff + websocket-driver (0.7.0) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.3) PLATFORMS ruby DEPENDENCIES active_model_serializers - apitome + bcrypt + bootsnap brakeman bullet bundler-audit byebug database_cleaner decent_exposure - devise dotenv-rails email_spec factory_girl_rails @@ -305,12 +282,10 @@ DEPENDENCIES json_spec kaminari letter_opener - mail_safe + listen (>= 3.0.5, < 3.2) pg - rack-cors - rails (= 4.2.8) - rails-api - rails_api_format! + puma + rails (= 5.2.1) responders rollbar rspec-rails @@ -318,15 +293,13 @@ DEPENDENCIES rubocop seedbank shoulda-matchers - simple_token_authentication simplecov spring spring-commands-rspec - thin webmock RUBY VERSION - ruby 2.4.4p296 + ruby 2.5.1p57 BUNDLED WITH 1.16.4 diff --git a/app/models/user.rb b/app/models/user.rb index 6e1e916..d82164e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,8 @@ class User < ActiveRecord::Base - acts_as_token_authenticatable + has_secure_password - devise :database_authenticatable, :registerable, - :recoverable, :trackable, :validatable + # acts_as_token_authenticatable + + # devise :database_authenticatable, :registerable, + # :recoverable, :trackable, :validatable end diff --git a/bin/bundle b/bin/bundle index 66e9889..f19acf5 100755 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) load Gem.bin_path('bundler', 'bundle') diff --git a/bin/quality b/bin/quality index 2b7c8c6..56d31dd 100755 --- a/bin/quality +++ b/bin/quality @@ -2,8 +2,14 @@ set -e +bin/rubocop bin/brakeman --quiet --skip-libs --exit-on-warn -bin/rubocop --config config/rubocop.yml + +# Using rake-task here since coffeelint.rb cmd doesn't exit with non-zero status +# when code contains errors +bin/rails coffeelint +# bin/scss-lint +bin/slim-lint app/views bin/bundle-audit update bin/bundle-audit diff --git a/bin/rails b/bin/rails index 7feb6a3..5badb2f 100755 --- a/bin/rails +++ b/bin/rails @@ -1,8 +1,9 @@ #!/usr/bin/env ruby begin - load File.expand_path("../spring", __FILE__) -rescue LoadError + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') end -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/rake b/bin/rake index 8017a02..d87d5f5 100755 --- a/bin/rake +++ b/bin/rake @@ -1,7 +1,8 @@ #!/usr/bin/env ruby begin - load File.expand_path("../spring", __FILE__) -rescue LoadError + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') end require_relative '../config/boot' require 'rake' diff --git a/bin/rspec b/bin/rspec index 5318d0c..5a3c87c 100755 --- a/bin/rspec +++ b/bin/rspec @@ -1,7 +1,8 @@ #!/usr/bin/env ruby begin - load File.expand_path("../spring", __FILE__) -rescue LoadError + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') end # # This file was generated by Bundler. diff --git a/bin/server b/bin/server index 495e9a4..df39613 100755 --- a/bin/server +++ b/bin/server @@ -1,3 +1,3 @@ #!/usr/bin/env sh -exec bin/foreman start +bin/rails server --port 5000 --binding lvh.me diff --git a/bin/setup b/bin/setup index a500856..1c58d77 100755 --- a/bin/setup +++ b/bin/setup @@ -22,4 +22,9 @@ if [ ! -f .env ]; then fi # Set up database and add any development seed data -bundle exec rake db:setup +bundle exec rails db:create +bundle exec rails db:schema:load + +if [ -z "$CI" ]; then + bundle exec rails db:seed +fi diff --git a/bin/spring b/bin/spring index 7b45d37..fb2ec2e 100755 --- a/bin/spring +++ b/bin/spring @@ -4,12 +4,14 @@ # It gets overwritten when you run the `spring binstub` command. unless defined?(Spring) - require "rubygems" - require "bundler" + require 'rubygems' + require 'bundler' - if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) - Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq } - gem "spring", match[1] - require "spring/binstub" + lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) + spring = lockfile.specs.detect { |spec| spec.name == "spring" } + if spring + Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path + gem 'spring', spring.version + require 'spring/binstub' end end diff --git a/bin/update b/bin/update new file mode 100755 index 0000000..67d0d49 --- /dev/null +++ b/bin/update @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = File.expand_path('..', __dir__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 0000000..460dd56 --- /dev/null +++ b/bin/yarn @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do + begin + exec "yarnpkg", *ARGV + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/config/application.rb b/config/application.rb index afa0773..fcadd4d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,9 +1,16 @@ -require File.expand_path("boot", __dir__) +require_relative 'boot' +require "rails" # Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" require "active_record/railtie" +require "active_storage/engine" require "action_controller/railtie" require "action_mailer/railtie" +require "action_view/railtie" +# require "action_cable/engine" +# require "sprockets/railtie" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -11,28 +18,34 @@ module RailsBaseApi class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 5.2 + # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. + # Application configuration can go into files in config/initializers + # -- all .rb files in that directory are automatically loaded after loading + # the framework and any gems in your application. + + # Only loads a smaller set of middleware suitable for API only apps. + # Middleware like session, flash, cookies can be added back manually. + # Skip views, helpers and assets when generating a new resource. + config.api_only = true # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. - # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")] # config.i18n.default_locale = :de - # Default e-mail address which will be shown in the "from" devise emails, initializers/devise.rb. - config.noreply = "noreply@fs-rails-base-api.heroku.com" - - # Default host for action mailer, initializers/mailer.rb - config.host = "localhost:5000" + # Enable deflate / gzip compression of controller-generated responses + config.middleware.use Rack::Deflater - config.serve_static_files = false + # Set default From address for all Mailers + config.action_mailer.default_options = { from: ENV.fetch("MAILER_SENDER_ADDRESS") } - # Disable default Rails headers which do not make sense in - # API-only project (X-Frame-Options, X-XSS-Protection, X-Content-Type-Options) - config.action_dispatch.default_headers = {} + # Set URL options to be able to use url_for helpers + config.action_mailer.default_url_options = { host: ENV.fetch("HOST") } end end diff --git a/config/boot.rb b/config/boot.rb index 12c0a73..b9e460c 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,4 @@ -# Set up gems listed in the Gemfile. -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) +require 'bundler/setup' # Set up gems listed in the Gemfile. +require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000..b808b8d --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 + channel_prefix: rails_base_api_production diff --git a/config/environment.rb b/config/environment.rb index e4429b4..426333b 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require File.expand_path("application", __dir__) +require_relative 'application' # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 5375800..1a8dab7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -9,12 +9,30 @@ # Do not eager load code on boot. config.eager_load = false - # Show full error reports and disable caching. - config.consider_all_requests_local = true - config.action_controller.perform_caching = false + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join('tmp', 'caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options) + config.active_storage.service = :local # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + config.action_mailer.perform_caching = false # Preview email in the browser instead of sending it. config.action_mailer.delivery_method = :letter_opener @@ -25,6 +43,14 @@ # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker end diff --git a/config/environments/production.rb b/config/environments/production.rb index faed79e..42486c6 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -14,34 +14,66 @@ config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Enable Rack::Cache to put a simple HTTP cache in front of your application - # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. - # config.action_dispatch.rack_cache = true + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + if ENV["ASSET_HOST"] + config.action_mailer.asset_host = ENV["ASSET_HOST"] + # config.action_controller.asset_host = ENV["ASSET_HOST"] + end # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Store uploaded files on the local file system (see config/storage.yml for options) + config.active_storage.service = :local # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true + config.force_ssl = true - # Set to :debug to see everything in the log. - config.log_level = :info + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug # Prepend all log lines with the following tags. - # config.log_tags = [ :subdomain, :uuid ] - - # Use a different logger for distributed setups. - # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + config.log_tags = [ :request_id ] # Use a different cache store in production. # config.cache_store = :mem_cache_store + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "rails_base_api_#{Rails.env}" + + config.action_mailer.perform_caching = false + # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false + # Enable Email delivery via custom SMTP server or via SendGrid by default + if ENV["SMTP_USERNAME"] || ENV["SENDGRID_USERNAME"] + config.action_mailer.delivery_method = :smtp + + config.action_mailer.smtp_settings = { + authentication: :plain, + enable_starttls_auto: true, + openssl_verify_mode: ENV.fetch("SMTP_OPENSSL_VERIFY_MODE", nil), + address: ENV.fetch("SMTP_ADDRESS", "smtp.sendgrid.net"), + port: ENV.fetch("SMTP_PORT", 587), + domain: ENV.fetch("SMTP_DOMAIN", "heroku.com"), + user_name: ENV.fetch("SMTP_USERNAME") { ENV.fetch("SENDGRID_USERNAME") }, + password: ENV.fetch("SMTP_PASSWORD") { ENV.fetch("SENDGRID_PASSWORD") } + } + end + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true @@ -49,12 +81,19 @@ # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify - # Disable automatic flushing of the log to improve performance. - # config.autoflush_log = false - # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false end diff --git a/config/environments/staging.rb b/config/environments/staging.rb index faed79e..dd14317 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -1,60 +1,5 @@ -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # Code is not reloaded between requests. - config.cache_classes = true - - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. - config.eager_load = true - - # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Enable Rack::Cache to put a simple HTTP cache in front of your application - # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. - # config.action_dispatch.rack_cache = true - - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx - - # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true - - # Set to :debug to see everything in the log. - config.log_level = :info - - # Prepend all log lines with the following tags. - # config.log_tags = [ :subdomain, :uuid ] +require_relative "production" - # Use a different logger for distributed setups. - # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) - - # Use a different cache store in production. - # config.cache_store = :mem_cache_store - - # Ignore bad email addresses and do not raise email delivery errors. - # Set this to true and configure the email server for immediate delivery to raise delivery errors. - # config.action_mailer.raise_delivery_errors = false - - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = true - - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - # Disable automatic flushing of the log to improve performance. - # config.autoflush_log = false - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - - # Do not dump schema after migrations. - config.active_record.dump_schema_after_migration = false +Rails.application.configure do + config.force_ssl = false end diff --git a/config/environments/test.rb b/config/environments/test.rb index a6c05de..0a38fd3 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -12,16 +12,27 @@ # preloads Rails for running tests, you may have to set it to true. config.eager_load = false + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + } + # Show full error reports and disable caching. - config.consider_all_requests_local = false + config.consider_all_requests_local = true config.action_controller.perform_caching = false # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = true + config.action_dispatch.show_exceptions = false # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + # Store uploaded files on the local file system in a temporary directory + config.active_storage.service = :test + + config.action_mailer.perform_caching = false + # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. diff --git a/config/initializers/01_config.rb b/config/initializers/01_config.rb deleted file mode 100644 index df16477..0000000 --- a/config/initializers/01_config.rb +++ /dev/null @@ -1,7 +0,0 @@ -# The shortcut for getting application config -# Application config should be stored in the config/application.rb, config/environments/*.rb -module Kernel - def app_config - Rails.application.config - end -end diff --git a/config/initializers/active_model_serializer.rb b/config/initializers/active_model_serializer.rb index 5c20a90..74f6cba 100644 --- a/config/initializers/active_model_serializer.rb +++ b/config/initializers/active_model_serializer.rb @@ -1,3 +1,5 @@ -ActiveModel::Serializer.configure do |config| - config.adapter = :json +ActiveModelSerializers.config.adapter = :json_api + +ActiveSupport.on_load(:action_controller) do + require "active_model_serializers/register_jsonapi_renderer" end diff --git a/config/initializers/apitome.rb b/config/initializers/apitome.rb index e52d4b4..0107477 100644 --- a/config/initializers/apitome.rb +++ b/config/initializers/apitome.rb @@ -1,52 +1,52 @@ -Apitome.setup do |config| - # This determines where the Apitome routes will be mounted. Changing this to "/api/documentation" for instance would - # allow you to browse to http://localhost:3000/api/documentation to see your api documentation. Set to nil and mount - # it yourself if you need to. - config.mount_at = "/docs" - - # This defaults to Rails.root if left nil. If you're providing documentation for an engine using a dummy application - # it can be useful to set this to your engines root.. E.g. Application::Engine.root - config.root = nil - - # This is where rspec_api_documentation outputs the JSON files. This is configurable within RAD, and so is - # configurable here. - config.doc_path = "doc/api/v1" - - # The title of the documentation -- If your project has a name, you'll want to put it here. - config.title = "#{Rails.application.class.parent_name.titleize} V1" - - # The main layout view for all documentation pages. By default this is pretty basic, but you may want to use your own - # application layout. - config.layout = "apitome/application" - - # We're using highlight.js (https://github.com/isagalaev/highlight.js) for code highlighting, and it comes with some - # great themes. You can check http://softwaremaniacs.org/media/soft/highlight/test.html for themes, and enter the - # theme as lowercase/underscore. - config.code_theme = "default" - - # This allows you to override the css manually. You typically want to require `apitome/application` within the - # override, but if you want to override it entirely you can do so. - config.css_override = nil - - # This allows you to override the javascript manually. You typically want to require `apitome/application` within the - # override, but if you want to override it entirely you can do so. - config.js_override = nil - - # You can provide a "README" style markdown file for the documentation, which is a useful place to include general - # information. This path is relative to your doc_path configuration. - config.readme = "../../../README.md" - - # Apitome can render the documentation into a single page that uses scrollspy, or it can render the documentation on - # individual pages on demand. This allows you to specify which one you want, as a single page may impact performance. - config.single_page = true - - # Restrict access to documentation - # - if ENV["APITOME_USER"] && ENV["APITOME_PASSWORD"] - Apitome::DocsController.http_basic_authenticate_with( - name: ENV["APITOME_USER"], - password: ENV["APITOME_PASSWORD"], - only: [:index] - ) - end -end if defined? Apitome +# Apitome.setup do |config| +# # This determines where the Apitome routes will be mounted. Changing this to "/api/documentation" for instance would +# # allow you to browse to http://localhost:3000/api/documentation to see your api documentation. Set to nil and mount +# # it yourself if you need to. +# config.mount_at = "/docs" + +# # This defaults to Rails.root if left nil. If you're providing documentation for an engine using a dummy application +# # it can be useful to set this to your engines root.. E.g. Application::Engine.root +# config.root = nil + +# # This is where rspec_api_documentation outputs the JSON files. This is configurable within RAD, and so is +# # configurable here. +# config.doc_path = "doc/api/v1" + +# # The title of the documentation -- If your project has a name, you'll want to put it here. +# config.title = "#{Rails.application.class.parent_name.titleize} V1" + +# # The main layout view for all documentation pages. By default this is pretty basic, but you may want to use your own +# # application layout. +# config.layout = "apitome/application" + +# # We're using highlight.js (https://github.com/isagalaev/highlight.js) for code highlighting, and it comes with some +# # great themes. You can check http://softwaremaniacs.org/media/soft/highlight/test.html for themes, and enter the +# # theme as lowercase/underscore. +# config.code_theme = "default" + +# # This allows you to override the css manually. You typically want to require `apitome/application` within the +# # override, but if you want to override it entirely you can do so. +# config.css_override = nil + +# # This allows you to override the javascript manually. You typically want to require `apitome/application` within the +# # override, but if you want to override it entirely you can do so. +# config.js_override = nil + +# # You can provide a "README" style markdown file for the documentation, which is a useful place to include general +# # information. This path is relative to your doc_path configuration. +# config.readme = "../../../README.md" + +# # Apitome can render the documentation into a single page that uses scrollspy, or it can render the documentation on +# # individual pages on demand. This allows you to specify which one you want, as a single page may impact performance. +# config.single_page = true + +# # Restrict access to documentation +# # +# if ENV["APITOME_USER"] && ENV["APITOME_PASSWORD"] +# Apitome::DocsController.http_basic_authenticate_with( +# name: ENV["APITOME_USER"], +# password: ENV["APITOME_PASSWORD"], +# only: [:index] +# ) +# end +# end if defined? Apitome diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 0000000..89d2efa --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 0000000..5a6a32d --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb new file mode 100644 index 0000000..f234a07 --- /dev/null +++ b/config/initializers/cors.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins(*ENV.fetch("CORS_ORIGINS").split(",")) + + resource '*', + headers: :any, + methods: [:get, :post, :put, :patch, :delete, :options, :head] + end +end if ENV["CORS_ORIGINS"].present? diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb deleted file mode 100644 index 2f42a74..0000000 --- a/config/initializers/devise.rb +++ /dev/null @@ -1,266 +0,0 @@ -# Use this hook to configure devise mailer, warden hooks and so forth. -# Many of these configuration options can be set straight in your model. -Devise.setup do |config| - # The secret key used by Devise. Devise uses this key to generate - # random tokens. Changing this key will render invalid all existing - # confirmation, reset password and unlock tokens in the database. - # Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key` - # by default. You can change it below and use your own secret key. - - config.secret_key = ENV["SECRET_KEY_BASE"] - - # ==> Mailer Configuration - # Configure the e-mail address which will be shown in Devise::Mailer, - # note that it will be overwritten if you use your own mailer class - # with default "from" parameter. - config.mailer_sender = app_config.noreply - - # Configure the class responsible to send e-mails. - # config.mailer = 'Devise::Mailer' - - # ==> ORM configuration - # Load and configure the ORM. Supports :active_record (default) and - # :mongoid (bson_ext recommended) by default. Other ORMs may be - # available as additional gems. - require "devise/orm/active_record" - - # ==> Configuration for any authentication mechanism - # Configure which keys are used when authenticating a user. The default is - # just :email. You can configure it to use [:username, :subdomain], so for - # authenticating a user, both parameters are required. Remember that those - # parameters are used only when authenticating and not when retrieving from - # session. If you need permissions, you should implement that in a before filter. - # You can also supply a hash where the value is a boolean determining whether - # or not authentication should be aborted when the value is not present. - # config.authentication_keys = [:email] - - # Configure parameters from the request object used for authentication. Each entry - # given should be a request method and it will automatically be passed to the - # find_for_authentication method and considered in your model lookup. For instance, - # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. - # The same considerations mentioned for authentication_keys also apply to request_keys. - # config.request_keys = [] - - # Configure which authentication keys should be case-insensitive. - # These keys will be downcased upon creating or modifying a user and when used - # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] - - # Configure which authentication keys should have whitespace stripped. - # These keys will have whitespace before and after removed upon creating or - # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [:email] - - # Tell if authentication through request.params is enabled. True by default. - # It can be set to an array that will enable params authentication only for the - # given strategies, for example, `config.params_authenticatable = [:database]` will - # enable it only for database (email + password) authentication. - # config.params_authenticatable = true - - # Tell if authentication through HTTP Auth is enabled. False by default. - # It can be set to an array that will enable http authentication only for the - # given strategies, for example, `config.http_authenticatable = [:database]` will - # enable it only for database authentication. The supported strategies are: - # :database = Support basic authentication with authentication key + password - # config.http_authenticatable = false - - # If 401 status code should be returned for AJAX requests. True by default. - # config.http_authenticatable_on_xhr = true - - # The realm used in Http Basic Authentication. 'Application' by default. - # config.http_authentication_realm = 'Application' - - # It will change confirmation, password recovery and other workflows - # to behave the same regardless if the e-mail provided was right or wrong. - # Does not affect registerable. - # config.paranoid = true - - # By default Devise will store the user in session. You can skip storage for - # particular strategies by setting this option. - # Notice that if you are skipping storage for all authentication paths, you - # may want to disable generating routes to Devise's sessions controller by - # passing skip: :sessions to `devise_for` in your config/routes.rb - config.skip_session_storage = [:http_auth] - - # By default, Devise cleans up the CSRF token on authentication to - # avoid CSRF token fixation attacks. This means that, when using AJAX - # requests for sign in and sign up, you need to get a new CSRF token - # from the server. You can disable this option at your own risk. - # config.clean_up_csrf_token_on_authentication = true - - # ==> Configuration for :database_authenticatable - # For bcrypt, this is the cost for hashing the password and defaults to 10. If - # using other encryptors, it sets how many times you want the password re-encrypted. - # - # Limiting the stretches to just one in testing will increase the performance of - # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use - # a value less than 10 in other environments. Note that, for bcrypt (the default - # encryptor), the cost increases exponentially with the number of stretches (e.g. - # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). - config.stretches = Rails.env.test? ? 1 : 10 - - # Setup a pepper to generate the encrypted password. - # config.pepper = '' - - # ==> Configuration for :confirmable - # A period that the user is allowed to access the website even without - # confirming their account. For instance, if set to 2.days, the user will be - # able to access the website for two days without confirming their account, - # access will be blocked just in the third day. Default is 0.days, meaning - # the user cannot access the website without confirming their account. - # config.allow_unconfirmed_access_for = 2.days - - # A period that the user is allowed to confirm their account before their - # token becomes invalid. For example, if set to 3.days, the user can confirm - # their account within 3 days after the mail was sent, but on the fourth day - # their account can't be confirmed with the token any more. - # Default is nil, meaning there is no restriction on how long a user can take - # before confirming their account. - # config.confirm_within = 3.days - - # If true, requires any email changes to be confirmed (exactly the same way as - # initial account confirmation) to be applied. Requires additional unconfirmed_email - # db field (see migrations). Until confirmed, new email is stored in - # unconfirmed_email column, and copied to email column on successful confirmation. - config.reconfirmable = true - - # Defines which key will be used when confirming an account - # config.confirmation_keys = [:email] - - # ==> Configuration for :rememberable - # The time the user will be remembered without asking for credentials again. - # config.remember_for = 2.weeks - - # Invalidates all the remember me tokens when the user signs out. - config.expire_all_remember_me_on_sign_out = true - - # If true, extends the user's remember period when remembered via cookie. - # config.extend_remember_period = false - - # Options to be passed to the created cookie. For instance, you can set - # secure: true in order to force SSL only cookies. - # config.rememberable_options = {} - - # ==> Configuration for :validatable - # Range for password length. - config.password_length = 6..128 - - # Email regex used to validate email formats. It simply asserts that - # one (and only one) @ exists in the given string. This is mainly - # to give user feedback and not to assert the e-mail validity. - # config.email_regexp = /\A[^@]+@[^@]+\z/ - - # ==> Configuration for :timeoutable - # The time you want to timeout the user session without activity. After this - # time the user will be asked for credentials again. Default is 30 minutes. - # config.timeout_in = 30.minutes - - # If true, expires auth token on session timeout. - # config.expire_auth_token_on_timeout = false - - # ==> Configuration for :lockable - # Defines which strategy will be used to lock an account. - # :failed_attempts = Locks an account after a number of failed attempts to sign in. - # :none = No lock strategy. You should handle locking by yourself. - # config.lock_strategy = :failed_attempts - - # Defines which key will be used when locking and unlocking an account - # config.unlock_keys = [:email] - - # Defines which strategy will be used to unlock an account. - # :email = Sends an unlock link to the user email - # :time = Re-enables login after a certain amount of time (see :unlock_in below) - # :both = Enables both strategies - # :none = No unlock strategy. You should handle unlocking by yourself. - # config.unlock_strategy = :both - - # Number of authentication tries before locking an account if lock_strategy - # is failed attempts. - # config.maximum_attempts = 20 - - # Time interval to unlock the account if :time is enabled as unlock_strategy. - # config.unlock_in = 1.hour - - # Warn on the last attempt before the account is locked. - # config.last_attempt_warning = true - - # ==> Configuration for :recoverable - # - # Defines which key will be used when recovering the password for an account - # config.reset_password_keys = [:email] - - # Time interval you can reset your password with a reset password key. - # Don't put a too small interval or your users won't have the time to - # change their passwords. - config.reset_password_within = 6.hours - - # When set to false, does not sign a user in automatically after their password is - # reset. Defaults to true, so a user is signed in automatically after a reset. - # config.sign_in_after_reset_password = true - - # ==> Configuration for :encryptable - # Allow you to use another encryption algorithm besides bcrypt (default). You can use - # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, - # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) - # and :restful_authentication_sha1 (then you should set stretches to 10, and copy - # REST_AUTH_SITE_KEY to pepper). - # - # Require the `devise-encryptable` gem when using anything other than bcrypt - # config.encryptor = :sha512 - - # ==> Scopes configuration - # Turn scoped views on. Before rendering "sessions/new", it will first check for - # "users/sessions/new". It's turned off by default because it's slower if you - # are using only default views. - # config.scoped_views = false - - # Configure the default scope given to Warden. By default it's the first - # devise role declared in your routes (usually :user). - # config.default_scope = :user - - # Set this configuration to false if you want /users/sign_out to sign out - # only the current scope. By default, Devise signs out all scopes. - # config.sign_out_all_scopes = true - - # ==> Navigation configuration - # Lists the formats that should be treated as navigational. Formats like - # :html, should redirect to the sign in page when the user does not have - # access, but formats like :xml or :json, should return 401. - # - # If you have any extra navigational formats, like :iphone or :mobile, you - # should add them to the navigational formats lists. - # - # The "*/*" below is required to match Internet Explorer requests. - # config.navigational_formats = ['*/*', :html] - - # The default HTTP method used to sign out a resource. Default is :delete. - config.sign_out_via = :delete - - # ==> OmniAuth - # Add a new OmniAuth provider. Check the wiki for more information on setting - # up on your models and hooks. - # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' - - # ==> Warden configuration - # If you want to use other strategies, that are not supported by Devise, or - # change the failure app, you can configure them inside the config.warden block. - # - # config.warden do |manager| - # manager.intercept_401 = false - # manager.default_strategies(scope: :user).unshift :some_external_strategy - # end - - # ==> Mountable engine configurations - # When using Devise inside an engine, let's call it `MyEngine`, and this engine - # is mountable, there are some extra configurations to be taken into account. - # The following options are available, assuming the engine is mounted as: - # - # mount MyEngine, at: '/my_engine' - # - # The router that invoked `devise_for`, in the example above, would be: - # config.router_name = :my_engine - # - # When using OmniAuth, Devise cannot automatically set OmniAuth path, - # so you need to do it manually. For the users scope, it would be: - # config.omniauth_path_prefix = '/my_engine/users/auth' -end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 2494b78..4a994e1 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,4 +1,4 @@ # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += %i(password auth_token) +Rails.application.config.filter_parameters += [:password] diff --git a/config/initializers/generators.rb b/config/initializers/generators.rb index d62e9a9..ec08c65 100644 --- a/config/initializers/generators.rb +++ b/config/initializers/generators.rb @@ -1,4 +1,4 @@ -app_config.app_generators do |g| +Rails.application.config.app_generators do |g| g.fixture_replacement :factory_girl, dir: "spec/factories" g.stylesheets false g.javascripts false diff --git a/config/initializers/mail_safe.rb b/config/initializers/mail_safe.rb deleted file mode 100644 index 8643c23..0000000 --- a/config/initializers/mail_safe.rb +++ /dev/null @@ -1,3 +0,0 @@ -# allow send emails to the @example.com -# -MailSafe::Config.internal_address_definition = /^.*@example\.com$/i if defined?(MailSafe::Config) diff --git a/config/initializers/mailer.rb b/config/initializers/mailer.rb deleted file mode 100644 index 767c357..0000000 --- a/config/initializers/mailer.rb +++ /dev/null @@ -1 +0,0 @@ -ActionMailer::Base.default_url_options[:host] = app_config.host diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index 72aca7e..dc18996 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -2,4 +2,3 @@ # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf -# Mime::Type.register_alias "text/html", :iphone diff --git a/config/initializers/rack_cors.rb b/config/initializers/rack_cors.rb deleted file mode 100644 index 86219c0..0000000 --- a/config/initializers/rack_cors.rb +++ /dev/null @@ -1,14 +0,0 @@ -app_config.middleware.insert_before "Warden::Manager", "Rack::Cors" do - allow do - # Allow requests from domains: - # e.g. origins('api.example.com', 'next.example.com') - # - origins(*ENV.fetch("ALLOW_REQUESTS_FROM", app_config.host).split(",")) - - resource( - "*", - headers: :any, - methods: %i(get post put delete patch options) - ) - end -end diff --git a/config/initializers/requires.rb b/config/initializers/requires.rb deleted file mode 100644 index a7177d2..0000000 --- a/config/initializers/requires.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Require recursively all files in the lib/extensions -Dir[Rails.root.join("lib/extensions/**/*.rb")].each { |f| require(f) } - -# Require only files placed in the lib folder -Dir[Rails.root.join("lib/*.rb")].each { |f| require(f) } diff --git a/config/initializers/rspec_api_documentation.rb b/config/initializers/rspec_api_documentation.rb index ab70c4c..9009e37 100644 --- a/config/initializers/rspec_api_documentation.rb +++ b/config/initializers/rspec_api_documentation.rb @@ -3,7 +3,7 @@ config.docs_dir = Rails.root.join("doc", "api", "v1") config.request_headers_to_include = ["Accept", "X-Auth-Token"] config.response_headers_to_include = ["Content-Type"] - config.curl_host = "http://#{app_config.host}" + config.curl_host = "http://#{ENV.fetch('HOST')}" config.curl_headers_to_filter = ["Cookie", "Host", "Content-Type", "Origin"] config.keep_source_order = true end if defined? RspecApiDocumentation diff --git a/config/initializers/simple_token_authentication.rb b/config/initializers/simple_token_authentication.rb deleted file mode 100644 index 3388396..0000000 --- a/config/initializers/simple_token_authentication.rb +++ /dev/null @@ -1,64 +0,0 @@ -SimpleTokenAuthentication.configure do |config| - # Configure the session persistence policy after a successful sign in, - # in other words, if the authentication token acts as a signin token. - # If true, user is stored in the session and the authentication token and - # email may be provided only once. - # If false, users must provide their authentication token and email at every request. - # config.sign_in_token = false - - # Configure the name of the HTTP headers watched for authentication. - # - # Default header names for a given token authenticatable entity follow the pattern: - # { entity: { authentication_token: 'X-Entity-Token', email: 'X-Entity-Email'} } - # - # When several token authenticatable models are defined, custom header names - # can be specified for none, any, or all of them. - # - # Note: when using the identifiers options, this option behaviour is modified. - # Please see the example below. - # - # Examples - # - # Given User and SuperAdmin are token authenticatable, - # When the following configuration is used: - # `config.header_names = { super_admin: { authentication_token: 'X-Admin-Auth-Token' } }` - # Then the token authentification handler for User watches the following headers: - # `X-User-Token, X-User-Email` - # And the token authentification handler for SuperAdmin watches the following headers: - # `X-Admin-Auth-Token, X-SuperAdmin-Email` - # - # When the identifiers option is set: - # `config.identifiers = { super_admin: :phone_number }` - # Then both the header names identifier key and default value are modified accordingly: - # `config.header_names = { super_admin: { phone_number: 'X-SuperAdmin-PhoneNumber' } }` - # - # config.header_names = { user: { authentication_token: 'X-User-Token', email: 'X-User-Email' } } - - # Configure the name of the attribute used to identify the user for authentication. - # That attribute must exist in your model. - # - # The default identifiers follow the pattern: - # { entity: 'email' } - # - # Note: the identifer must match your Devise configuration, - # see https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address#tell-devise-to-use-username-in-the-authentication_keys - # - # Note: setting this option does modify the header_names behaviour, - # see the header_names section above. - # - # Example: - # - # `config.identifiers = { super_admin: 'phone_number', user: 'uuid' }` - # - # config.identifiers = { user: 'email' } - - # Configure the Devise trackable strategy integration. - # - # If true, tracking is disabled for token authentication: signing in through - # token authentication won't modify the Devise trackable statistics. - # - # If false, given Devise trackable is configured for the relevant model, - # then signing in through token authentication will be tracked as any other sign in. - # - # config.skip_devise_trackable = true -end diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index 7cafc93..bbfc396 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -5,8 +5,10 @@ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. ActiveSupport.on_load(:action_controller) do - include ActionController::ParamsWrapper - - # Enable parameter wrapping for JSON - wrap_parameters(format: %i(json url_encoded_form)) + wrap_parameters format: [:json] end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/locales/en.yml b/config/locales/en.yml index d526dea..decc5a8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -16,15 +16,18 @@ # # This would use the information in config/locales/es.yml. # +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# 'true': 'foo' +# # To learn more, please read the Rails Internationalization guide # available at http://guides.rubyonrails.org/i18n.html. en: hello: "Hello world" - time: - formats: - short_date: "%x" - long_date: "%a, %b %d, %Y" - us: "%m/%d/%Y %I:%M %p" - us_date: "%m/%d/%Y" - us_time: "%I:%M %p" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..a5eccf8 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,34 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. +# +# preload_app! + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb index 2d0f9e4..787824f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,11 +1,3 @@ Rails.application.routes.draw do - scope defaults: { format: :json } do - devise_for :users, only: [] - end - - namespace :v1, defaults: { format: "json" } do - devise_scope :user do - post "users/sign_in", to: "sessions#create" - end - end + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/config/secrets.yml b/config/secrets.yml index f98bc51..13c1ec8 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -1,15 +1,32 @@ -default: &default - secret_token: <%= ENV['SECRET_TOKEN'] %> - secret_key_base: <%= ENV['SECRET_KEY_BASE'] %> +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +# Shared secrets are available across all environments. + +# shared: +# api_key: a1B2c3D4e5F6 + +# Environmental secrets are only available for that specific environment. development: - <<: *default + secret_key_base: fe10da2c2f8cac7ef34a508d284832dfec21a019f5fb01872bd8b5162f9f34dac43d28fda64be8fa0c0c5c1cdc01e51e7f588130de59ac13d53e1002c44983c9 test: - <<: *default + secret_key_base: 3565ea3c4f8cd650441c6137af6203803f6c7848105d3cbb84e469b9454e5a6635fa9327554f30ab27ab17c1d268df53035f7022a53f3858a6f6412b1331aa98 -staging: - <<: *default +# Do not keep production secrets in the unencrypted secrets file. +# Instead, either read values from the environment. +# Or, use `bin/rails secrets:setup` to configure encrypted secrets +# and move the `production:` environment over there. production: - <<: *default + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 0000000..9fa7863 --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w[ + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +].each { |path| Spring.watch(path) } diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000..d32f76e --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket + +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/migrate/20130319140714_create_users.rb b/db/migrate/20130319140714_create_users.rb new file mode 100644 index 0000000..f606a7b --- /dev/null +++ b/db/migrate/20130319140714_create_users.rb @@ -0,0 +1,11 @@ +class CreateUsers < ActiveRecord::Migration[5.2] + def change + create_table :users do |t| + t.string :email, null: false + t.string :password_digest, null: false + t.timestamps + end + + add_index :users, :email, unique: true + end +end diff --git a/db/migrate/20130319140714_devise_create_users.rb b/db/migrate/20130319140714_devise_create_users.rb deleted file mode 100644 index b91e775..0000000 --- a/db/migrate/20130319140714_devise_create_users.rb +++ /dev/null @@ -1,46 +0,0 @@ -class DeviseCreateUsers < ActiveRecord::Migration - def change - create_table(:users) do |t| - ## Database authenticatable - t.string :email, null: false, default: '' - t.string :encrypted_password, null: false, default: '' - - ## Recoverable - t.string :reset_password_token - t.datetime :reset_password_sent_at - - ## Rememberable - t.datetime :remember_created_at - - ## Trackable - t.integer :sign_in_count, default: 0 - t.datetime :current_sign_in_at - t.datetime :last_sign_in_at - t.string :current_sign_in_ip - t.string :last_sign_in_ip - - ## Confirmable - # t.string :confirmation_token - # t.datetime :confirmed_at - # t.datetime :confirmation_sent_at - # t.string :unconfirmed_email # Only if using reconfirmable - - ## Lockable - # t.integer :failed_attempts, default: 0 # Only if lock strategy is :failed_attempts - # t.string :unlock_token # Only if unlock strategy is :email or :both - # t.datetime :locked_at - - ## Token authenticatable - t.string :authentication_token - - - t.timestamps - end - - add_index :users, :email, unique: true - add_index :users, :reset_password_token, unique: true - # add_index :users, :confirmation_token, unique: true - # add_index :users, :unlock_token, unique: true - add_index :users, :authentication_token, unique: true - end -end diff --git a/db/schema.rb b/db/schema.rb index 02ac22c..65a2842 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -11,29 +10,17 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130319140714) do +ActiveRecord::Schema.define(version: 2013_03_19_140714) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "users", force: true do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.string "authentication_token" - t.datetime "created_at" - t.datetime "updated_at" + create_table "users", force: :cascade do |t| + t.string "email", null: false + t.string "password_digest", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_users_on_email", unique: true end - add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree - add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree - add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree - end From 511ba5e5a5779653e0e3bee622c4e05aa3fe03c6 Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Fri, 7 Sep 2018 12:52:22 +0300 Subject: [PATCH 03/26] Replace factory_girl -> factory_bot --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- README.md | 2 +- db/seeds/development/all.seeds.rb | 2 +- spec/factories/sequences.rb | 2 +- spec/factories/users.rb | 2 +- spec/rails_helper.rb | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 6c33441..cdccc91 100644 --- a/Gemfile +++ b/Gemfile @@ -50,8 +50,8 @@ group :test do end group :development, :test, :staging do + gem "factory_bot_rails" gem "faker" - gem "factory_girl_rails" gem "rspec_api_documentation" # gem "apitome" end diff --git a/Gemfile.lock b/Gemfile.lock index 085258d..7e2d2f6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,10 +83,10 @@ GEM launchy (~> 2.1) mail (~> 2.7) erubi (1.7.1) - factory_girl (4.9.0) + factory_bot (4.11.0) activesupport (>= 3.0.0) - factory_girl_rails (4.9.0) - factory_girl (~> 4.9.0) + factory_bot_rails (4.11.0) + factory_bot (~> 4.11.0) railties (>= 3.0.0) faker (1.9.1) i18n (>= 0.7) @@ -274,7 +274,7 @@ DEPENDENCIES decent_exposure dotenv-rails email_spec - factory_girl_rails + factory_bot_rails faker foreman health_check diff --git a/README.md b/README.md index e137cb4..c5001ea 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Status of the API could be checked at [http://localhost:5000/docs](http://localh ### Testing gems -* [Factory Girl](https://github.com/thoughtbot/factory_girl) for easier creation of test data +* [Factory Bot](https://github.com/thoughtbot/factory_bot) for easier creation of test data * [RSpec](https://github.com/rspec/rspec) for awesome, readable isolation testing * [Shoulda Matchers](http://github.com/thoughtbot/shoulda-matchers) for frequently needed Rails and RSpec matchers * [Email Spec](https://github.com/bmabey/email-spec) Collection of rspec matchers and cucumber steps for testing emails diff --git a/db/seeds/development/all.seeds.rb b/db/seeds/development/all.seeds.rb index 6afebab..7f90429 100644 --- a/db/seeds/development/all.seeds.rb +++ b/db/seeds/development/all.seeds.rb @@ -1 +1 @@ -FactoryGirl.create :user +FactoryBot.create :user diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb index 029f4ec..0b6c758 100644 --- a/spec/factories/sequences.rb +++ b/spec/factories/sequences.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do sequence(:email) { |n| "user#{n}@example.com" } sequence(:password) { "123456" } end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 10a95cd..84f35c5 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :user do email password diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index c7544c8..6f04e21 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -31,7 +31,7 @@ config.include EmailSpec::Helpers config.include EmailSpec::Matchers config.include Devise::TestHelpers, type: :controller - config.include FactoryGirl::Syntax::Methods + config.include FactoryBot::Syntax::Methods config.include Helpers config.include JsonSpec::Helpers config.include RailsApiFormat::Matchers From 46abf7848595d8364ec83adacb5bc2bf97727430 Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Fri, 7 Sep 2018 15:02:32 +0300 Subject: [PATCH 04/26] add jwt --- .byebug_history | 20 + Gemfile | 4 +- Gemfile.lock | 10 + app/controllers/application_controller.rb | 5 - app/controllers/v1/base_controller.rb | 41 ++ app/controllers/v1/sessions_controller.rb | 10 - app/controllers/v1/tokens_controller.rb | 21 + app/interactors/authenticate_user.rb | 16 - app/interactors/create_jwt.rb | 24 ++ app/models/application_record.rb | 3 + app/models/jwt_token.rb | 7 + app/models/user.rb | 7 +- app/serializers/paginated_array_serializer.rb | 12 - .../initializers/rspec_api_documentation.rb | 9 - config/routes.rb | 4 +- db/migrate/20130319140714_create_users.rb | 1 + db/schema.rb | 1 + spec/acceptance/v1/sessions_spec.rb | 26 -- spec/{acceptance => api}/errors_spec.rb | 0 spec/api/v1/tokens_spec.rb | 34 ++ spec/factories/users.rb | 1 + spec/interactors/create_jwt_spec.rb | 25 ++ spec/models/jwt_token_spec.rb | 10 + spec/rails_helper.rb | 2 - spec/schemas/definitions.json | 29 ++ spec/schemas/error.json1 | 3 + spec/schemas/jsonapi.json | 378 ++++++++++++++++++ spec/schemas/jwt_token.json | 5 + spec/schemas/users.json | 12 + .../paginated_array_serializer_spec.rb | 19 - spec/support/json_matchers.rb | 3 + spec/support/rspec_api_documentation.rb | 14 + .../shared_contexts/json_api_headers.rb | 17 + .../api_endpoint_with_authorization.rb | 12 + .../shared_examples/failure_interactor.rb | 7 + .../shared_examples/success_interactor.rb | 6 + 36 files changed, 691 insertions(+), 107 deletions(-) create mode 100644 .byebug_history delete mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/v1/base_controller.rb delete mode 100644 app/controllers/v1/sessions_controller.rb create mode 100644 app/controllers/v1/tokens_controller.rb delete mode 100644 app/interactors/authenticate_user.rb create mode 100644 app/interactors/create_jwt.rb create mode 100644 app/models/application_record.rb create mode 100644 app/models/jwt_token.rb delete mode 100644 app/serializers/paginated_array_serializer.rb delete mode 100644 config/initializers/rspec_api_documentation.rb delete mode 100644 spec/acceptance/v1/sessions_spec.rb rename spec/{acceptance => api}/errors_spec.rb (100%) create mode 100644 spec/api/v1/tokens_spec.rb create mode 100644 spec/interactors/create_jwt_spec.rb create mode 100644 spec/models/jwt_token_spec.rb create mode 100644 spec/schemas/definitions.json create mode 100644 spec/schemas/error.json1 create mode 100644 spec/schemas/jsonapi.json create mode 100644 spec/schemas/jwt_token.json create mode 100644 spec/schemas/users.json delete mode 100644 spec/serializers/paginated_array_serializer_spec.rb create mode 100644 spec/support/json_matchers.rb create mode 100644 spec/support/rspec_api_documentation.rb create mode 100644 spec/support/shared_contexts/json_api_headers.rb create mode 100644 spec/support/shared_examples/api_endpoint_with_authorization.rb create mode 100644 spec/support/shared_examples/failure_interactor.rb create mode 100644 spec/support/shared_examples/success_interactor.rb diff --git a/.byebug_history b/.byebug_history new file mode 100644 index 0000000..e9a22a0 --- /dev/null +++ b/.byebug_history @@ -0,0 +1,20 @@ +continue +JsonSchema.parse!(schema_data).uri +continue +JsonSchema.parse!(schema_data).uri +JsonSchema.parse!(schema_data) +schema_path +continue +schema_path +schema_data +continue +schema.uri +continue +schema.uri +continue +schema.uri +continue +schema.uri.nil? +schema.uri +schema +continue diff --git a/Gemfile b/Gemfile index cdccc91..318ad59 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,7 @@ gem "decent_exposure" gem "health_check" gem "interactor" gem "kaminari" +gem "knock" gem "puma" gem "responders" gem "rollbar" @@ -45,8 +46,9 @@ group :test do gem "database_cleaner" gem "email_spec" - gem "shoulda-matchers", require: false + gem "json_matchers" gem "json_spec" + gem "shoulda-matchers", require: false end group :development, :test, :staging do diff --git a/Gemfile.lock b/Gemfile.lock index 7e2d2f6..fb76039 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -104,10 +104,14 @@ GEM interactor (3.1.1) jaro_winkler (1.5.1) json (2.1.0) + json_matchers (0.10.0) + json_schema + json_schema (0.19.1) json_spec (1.1.5) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) jsonapi-renderer (0.2.0) + jwt (1.5.6) kaminari (1.1.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.1.1) @@ -120,6 +124,10 @@ GEM activerecord kaminari-core (= 1.1.1) kaminari-core (1.1.1) + knock (2.1.1) + bcrypt (~> 3.1) + jwt (~> 1.5) + rails (>= 4.2) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.6.0) @@ -279,8 +287,10 @@ DEPENDENCIES foreman health_check interactor + json_matchers json_spec kaminari + knock letter_opener listen (>= 3.0.5, < 3.2) pg diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb deleted file mode 100644 index fcc34a2..0000000 --- a/app/controllers/application_controller.rb +++ /dev/null @@ -1,5 +0,0 @@ -class ApplicationController < ActionController::API - include ActionController::ImplicitRender - - respond_to :json -end diff --git a/app/controllers/v1/base_controller.rb b/app/controllers/v1/base_controller.rb new file mode 100644 index 0000000..dcf2ff4 --- /dev/null +++ b/app/controllers/v1/base_controller.rb @@ -0,0 +1,41 @@ +module V1 + class BaseController < ActionController::API + include Knock::Authenticable + + before_action :authenticate_user + + private + + def authenticate_user + respond_with_unauthorized if current_user.blank? + end + + def current_user + @current_user ||= token && authenticate_for(User) + end + + def respond_with_resource(resource, status: :ok, location: resource, include: nil, fields: nil) + render jsonapi: resource, include: include, status: status, location: location, fields: fields + end + + def respond_with_resources(resources, include: nil, fields: nil) + respond_with_resource(resources, include: include, location: nil, fields: fields) + end + + def respond_with_resource_errors(resource) + render jsonapi: resource, serializer: ActiveModel::Serializer::ErrorSerializer, status: :unprocessable_entity + end + + def respond_with_error(message, status: :unprocessable_entity) + render jsonapi: Error.new(base: message), serializer: ActiveModel::Serializer::ErrorSerializer, status: status + end + + def respond_with_unauthorized + respond_with_error(I18n.t("interactors.authenticate.invalid_credentials"), status: :unauthorized) + end + + def jsonapi_params(options) + ActiveModelSerializers::Deserialization.jsonapi_parse!(params, options) + end + end +end diff --git a/app/controllers/v1/sessions_controller.rb b/app/controllers/v1/sessions_controller.rb deleted file mode 100644 index e4657df..0000000 --- a/app/controllers/v1/sessions_controller.rb +++ /dev/null @@ -1,10 +0,0 @@ -module V1 - class SessionsController < Devise::SessionsController - wrap_parameters :user - - def create - user = AuthenticateUser.call(warden: warden).user - respond_with(user, serializer: SessionSerializer) - end - end -end diff --git a/app/controllers/v1/tokens_controller.rb b/app/controllers/v1/tokens_controller.rb new file mode 100644 index 0000000..dce4cc0 --- /dev/null +++ b/app/controllers/v1/tokens_controller.rb @@ -0,0 +1,21 @@ +module V1 + class TokensController < V1::BaseController + skip_before_action :authenticate_user + + def create + result = CreateJwt.call(authentication_params) + + if result.success? + respond_with_resource(result.jwt_token, status: :created, location: nil) + else + respond_with_error(result.message) + end + end + + private + + def authentication_params + jsonapi_params(only: %i[email password]).merge(entity_name: :user) + end + end +end diff --git a/app/interactors/authenticate_user.rb b/app/interactors/authenticate_user.rb deleted file mode 100644 index b404b1d..0000000 --- a/app/interactors/authenticate_user.rb +++ /dev/null @@ -1,16 +0,0 @@ -class AuthenticateUser - include Interactor - - OPTIONS = { store: false, scope: :user }.freeze - - def call - context.user = authenticated_user! - end - - private - - def authenticated_user! - context.warden.request.env["devise.skip_trackable"] = false - context.warden.authenticate!(OPTIONS) - end -end diff --git a/app/interactors/create_jwt.rb b/app/interactors/create_jwt.rb new file mode 100644 index 0000000..c137f01 --- /dev/null +++ b/app/interactors/create_jwt.rb @@ -0,0 +1,24 @@ +class CreateJwt + include Interactor + + delegate :email, :password, to: :context + + def call + context.fail!(message: I18n.t("interactors.create_jwt.invalid_credentials")) unless authenticated? + context.jwt_token = jwt_token + end + + private + + def authenticated? + user.present? && user.authenticate(password) + end + + def jwt_token + JwtToken.new(payload: { sub: user.id }) + end + + def user + @user ||= User.find_by(email: email) + end +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 0000000..10a4cba --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/jwt_token.rb b/app/models/jwt_token.rb new file mode 100644 index 0000000..0ec7894 --- /dev/null +++ b/app/models/jwt_token.rb @@ -0,0 +1,7 @@ +class JwtToken < Knock::AuthToken + include ActiveModel::Serialization + + def id + token + end +end diff --git a/app/models/user.rb b/app/models/user.rb index d82164e..d67da20 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,8 +1,3 @@ -class User < ActiveRecord::Base +class User < ApplicationRecord has_secure_password - - # acts_as_token_authenticatable - - # devise :database_authenticatable, :registerable, - # :recoverable, :trackable, :validatable end diff --git a/app/serializers/paginated_array_serializer.rb b/app/serializers/paginated_array_serializer.rb deleted file mode 100644 index c183d22..0000000 --- a/app/serializers/paginated_array_serializer.rb +++ /dev/null @@ -1,12 +0,0 @@ -class PaginatedArraySerializer < ActiveModel::Serializer::ArraySerializer - def initialize(objects, options = {}) - options[:meta] ||= {} - options[:meta][:pagination] = { - total: objects.total_count, - per_page: objects.limit_value, - page: objects.current_page - } - - super(objects, options) - end -end diff --git a/config/initializers/rspec_api_documentation.rb b/config/initializers/rspec_api_documentation.rb deleted file mode 100644 index 9009e37..0000000 --- a/config/initializers/rspec_api_documentation.rb +++ /dev/null @@ -1,9 +0,0 @@ -RspecApiDocumentation.configure do |config| - config.format = :json - config.docs_dir = Rails.root.join("doc", "api", "v1") - config.request_headers_to_include = ["Accept", "X-Auth-Token"] - config.response_headers_to_include = ["Content-Type"] - config.curl_host = "http://#{ENV.fetch('HOST')}" - config.curl_headers_to_filter = ["Cookie", "Host", "Content-Type", "Origin"] - config.keep_source_order = true -end if defined? RspecApiDocumentation diff --git a/config/routes.rb b/config/routes.rb index 787824f..ab6191f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ Rails.application.routes.draw do - # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + namespace :v1, defaults: { format: "json" } do + resources :tokens, only: :create + end end diff --git a/db/migrate/20130319140714_create_users.rb b/db/migrate/20130319140714_create_users.rb index f606a7b..010540a 100644 --- a/db/migrate/20130319140714_create_users.rb +++ b/db/migrate/20130319140714_create_users.rb @@ -1,6 +1,7 @@ class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| + t.string :full_name t.string :email, null: false t.string :password_digest, null: false t.timestamps diff --git a/db/schema.rb b/db/schema.rb index 65a2842..d3eaba1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,6 +16,7 @@ enable_extension "plpgsql" create_table "users", force: :cascade do |t| + t.string "full_name" t.string "email", null: false t.string "password_digest", null: false t.datetime "created_at", null: false diff --git a/spec/acceptance/v1/sessions_spec.rb b/spec/acceptance/v1/sessions_spec.rb deleted file mode 100644 index 1692f80..0000000 --- a/spec/acceptance/v1/sessions_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require "rails_helper" -require "rspec_api_documentation/dsl" - -resource "Sessions" do - header "Accept", "application/json" - - subject(:response) { json_response_body } - - post "/v1/users/sign_in" do - let(:user) { create :user, password: "123456" } - - parameter :email, "Email", required: true - parameter :password, "Password", required: true - - let(:email) { user.email } - - example_request "Sign in with valid password", password: "123456" do - expect(response["user"]).to be_a_session_representation - end - - example_request "Sign in with invalid password", password: "" do - expect(response_status).to eq 401 - expect(response).to be_an_error_representation(:unauthorized, "Invalid email or password.") - end - end -end diff --git a/spec/acceptance/errors_spec.rb b/spec/api/errors_spec.rb similarity index 100% rename from spec/acceptance/errors_spec.rb rename to spec/api/errors_spec.rb diff --git a/spec/api/v1/tokens_spec.rb b/spec/api/v1/tokens_spec.rb new file mode 100644 index 0000000..2facc0d --- /dev/null +++ b/spec/api/v1/tokens_spec.rb @@ -0,0 +1,34 @@ +require "rails_helper" + +class TokenRequest < ActiveModelSerializers::Model + attributes :id, :email, :password +end + +class TokenRequestSerializer < ApplicationSerializer + attributes :email, :password +end + +resource "Tokens" do + include_context "with JSON API Headers" + + post "/v1/tokens" do + let(:request_class) { "token_request" } + + let(:email) { "user@example.com" } + parameter :email, "email", required: true + + let(:password) { "123456" } + parameter :password, "password", required: true + + include_context "with JSON API post body from request class" + + before do + create :user, email: email, password: "123456" + end + + example_request "Create Token" do + expect(response_status).to eq(201) + expect(response_body).to match_response_schema("jwt_token") + end + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 84f35c5..6da016c 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -1,5 +1,6 @@ FactoryBot.define do factory :user do + full_name { Faker::Name.name } email password end diff --git a/spec/interactors/create_jwt_spec.rb b/spec/interactors/create_jwt_spec.rb new file mode 100644 index 0000000..6e214c2 --- /dev/null +++ b/spec/interactors/create_jwt_spec.rb @@ -0,0 +1,25 @@ +require "rails_helper" + +describe CreateJwt do + let(:interactor) { described_class.new(user_attributes) } + let(:context) { interactor.context } + + let(:user_attributes) { attributes_for(:user).slice(:email, :password) } + + context "when user does not exist" do + it_behaves_like "failure interactor" + end + + context "when user exists" do + before do + create(:user, user_attributes) + end + + it_behaves_like "success interactor" + + it "sets token in context" do + interactor.run + expect(context.jwt_token).to be + end + end +end diff --git a/spec/models/jwt_token_spec.rb b/spec/models/jwt_token_spec.rb new file mode 100644 index 0000000..b4e7d41 --- /dev/null +++ b/spec/models/jwt_token_spec.rb @@ -0,0 +1,10 @@ +require "rails_helper" + +describe JwtToken do + subject(:jwt_token) { described_class.new(payload: { "sub" => "1" }) } + + it "serializable resource" do + expect { ActiveModelSerializers::SerializableResource.new(jwt_token).to_json } + .not_to raise_error + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 6f04e21..c555871 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -30,11 +30,9 @@ config.include Rails.application.routes.url_helpers config.include EmailSpec::Helpers config.include EmailSpec::Matchers - config.include Devise::TestHelpers, type: :controller config.include FactoryBot::Syntax::Methods config.include Helpers config.include JsonSpec::Helpers - config.include RailsApiFormat::Matchers config.before :suite do DatabaseCleaner.strategy = :transaction diff --git a/spec/schemas/definitions.json b/spec/schemas/definitions.json new file mode 100644 index 0000000..86233bf --- /dev/null +++ b/spec/schemas/definitions.json @@ -0,0 +1,29 @@ +{ + "definitions": { + "user": { + "required": ["attributes"], + "properties": { + "attributes": { + "required": ["email", "full-name"], + "properties": { + "email": { "type": "string" }, + "full-name": { "type": "string" } + } + } + } + }, + + + "jwt_token": { + "required": ["attributes"], + "properties": { + "attributes": { + "required": ["token"], + "properties": { + "token": { "type": "string" } + } + } + } + } + } +} diff --git a/spec/schemas/error.json1 b/spec/schemas/error.json1 new file mode 100644 index 0000000..45dd5c1 --- /dev/null +++ b/spec/schemas/error.json1 @@ -0,0 +1,3 @@ +{ + "$ref": "file:/jsonapi.json#/definitions/failure" +} diff --git a/spec/schemas/jsonapi.json b/spec/schemas/jsonapi.json new file mode 100644 index 0000000..c19d092 --- /dev/null +++ b/spec/schemas/jsonapi.json @@ -0,0 +1,378 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "JSON API Schema", + "description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org", + "oneOf": [ + { + "$ref": "#/definitions/success" + }, + { + "$ref": "#/definitions/failure" + }, + { + "$ref": "#/definitions/info" + } + ], + + "definitions": { + "success": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/data" + }, + "included": { + "description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".", + "type": "array", + "items": { + "$ref": "#/definitions/resource" + }, + "uniqueItems": true + }, + "meta": { + "$ref": "#/definitions/meta" + }, + "links": { + "description": "Link members related to the primary data.", + "allOf": [ + { + "$ref": "#/definitions/links" + }, + { + "$ref": "#/definitions/pagination" + } + ] + }, + "jsonapi": { + "$ref": "#/definitions/jsonapi" + } + }, + "additionalProperties": false + }, + "failure": { + "type": "object", + "required": [ + "errors" + ], + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/error" + }, + "uniqueItems": true + }, + "meta": { + "$ref": "#/definitions/meta" + }, + "jsonapi": { + "$ref": "#/definitions/jsonapi" + }, + "links": { + "$ref": "#/definitions/links" + } + }, + "additionalProperties": false + }, + "info": { + "type": "object", + "required": [ + "meta" + ], + "properties": { + "meta": { + "$ref": "#/definitions/meta" + }, + "links": { + "$ref": "#/definitions/links" + }, + "jsonapi": { + "$ref": "#/definitions/jsonapi" + } + }, + "additionalProperties": false + }, + + "meta": { + "description": "Non-standard meta-information that can not be represented as an attribute or relationship.", + "type": "object", + "additionalProperties": true + }, + "data": { + "description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.", + "oneOf": [ + { + "$ref": "#/definitions/resource" + }, + { + "description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.", + "type": "array", + "items": { + "$ref": "#/definitions/resource" + }, + "uniqueItems": true + }, + { + "description": "null if the request is one that might correspond to a single resource, but doesn't currently.", + "type": "null" + } + ] + }, + "resource": { + "description": "\"Resource objects\" appear in a JSON API document to represent resources.", + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/definitions/attributes" + }, + "relationships": { + "$ref": "#/definitions/relationships" + }, + "links": { + "$ref": "#/definitions/links" + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + }, + + "links": { + "description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.", + "type": "object", + "properties": { + "self": { + "description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.", + "type": "string", + "format": "uri-reference" + }, + "related": { + "$ref": "#/definitions/link" + } + }, + "additionalProperties": true + }, + "link": { + "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.", + "oneOf": [ + { + "description": "A string containing the link's URL.", + "type": "string", + "format": "uri-reference" + }, + { + "type": "object", + "required": [ + "href" + ], + "properties": { + "href": { + "description": "A string containing the link's URL.", + "type": "string", + "format": "uri-reference" + }, + "meta": { + "$ref": "#/definitions/meta" + } + } + } + ] + }, + + "attributes": { + "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.", + "type": "object", + "patternProperties": { + "^(?!relationships$|links$|id$|type$)\\w[-\\w_]*$": { + "description": "Attributes may contain any valid JSON value." + } + }, + "additionalProperties": false + }, + + "relationships": { + "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.", + "type": "object", + "patternProperties": { + "^(?!id$|type$)\\w[-\\w_]*$": { + "properties": { + "links": { + "$ref": "#/definitions/links" + }, + "data": { + "description": "Member, whose value represents \"resource linkage\".", + "oneOf": [ + { + "$ref": "#/definitions/relationshipToOne" + }, + { + "$ref": "#/definitions/relationshipToMany" + } + ] + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "anyOf": [ + {"required": ["data"]}, + {"required": ["meta"]}, + {"required": ["links"]} + ], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "relationshipToOne": { + "description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.", + "anyOf": [ + { + "$ref": "#/definitions/empty" + }, + { + "$ref": "#/definitions/linkage" + } + ] + }, + "relationshipToMany": { + "description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.", + "type": "array", + "items": { + "$ref": "#/definitions/linkage" + }, + "uniqueItems": true + }, + "empty": { + "description": "Describes an empty to-one relationship.", + "type": "null" + }, + "linkage": { + "description": "The \"type\" and \"id\" to non-empty members.", + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + }, + "pagination": { + "type": "object", + "properties": { + "first": { + "description": "The first page of data", + "oneOf": [ + { "type": "string", "format": "uri-reference" }, + { "type": "null" } + ] + }, + "last": { + "description": "The last page of data", + "oneOf": [ + { "type": "string", "format": "uri-reference" }, + { "type": "null" } + ] + }, + "prev": { + "description": "The previous page of data", + "oneOf": [ + { "type": "string", "format": "uri-reference" }, + { "type": "null" } + ] + }, + "next": { + "description": "The next page of data", + "oneOf": [ + { "type": "string", "format": "uri-reference" }, + { "type": "null" } + ] + } + } + }, + + "jsonapi": { + "description": "An object describing the server's implementation", + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + }, + + "error": { + "type": "object", + "properties": { + "id": { + "description": "A unique identifier for this particular occurrence of the problem.", + "type": "string" + }, + "links": { + "$ref": "#/definitions/links" + }, + "status": { + "description": "The HTTP status code applicable to this problem, expressed as a string value.", + "type": "string" + }, + "code": { + "description": "An application-specific error code, expressed as a string value.", + "type": "string" + }, + "title": { + "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.", + "type": "string" + }, + "detail": { + "description": "A human-readable explanation specific to this occurrence of the problem.", + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "pointer": { + "description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].", + "type": "string" + }, + "parameter": { + "description": "A string indicating which query parameter caused the error.", + "type": "string" + } + } + }, + "meta": { + "$ref": "#/definitions/meta" + } + }, + "additionalProperties": false + } + } +} diff --git a/spec/schemas/jwt_token.json b/spec/schemas/jwt_token.json new file mode 100644 index 0000000..9c0c5f5 --- /dev/null +++ b/spec/schemas/jwt_token.json @@ -0,0 +1,5 @@ +{ + "allOf": [ + { "$ref": "jsonapi.json#/definitions/success" } + ] +} diff --git a/spec/schemas/users.json b/spec/schemas/users.json new file mode 100644 index 0000000..2dbf186 --- /dev/null +++ b/spec/schemas/users.json @@ -0,0 +1,12 @@ +{ + "allOf": [ + { "$ref": "jsonapi.json#/definitions/success" }, + { + "properties": { + "data": { + "items": { "$ref": "definitions.json#/definitions/user" } + } + } + } + ] +} diff --git a/spec/serializers/paginated_array_serializer_spec.rb b/spec/serializers/paginated_array_serializer_spec.rb deleted file mode 100644 index 2e8771a..0000000 --- a/spec/serializers/paginated_array_serializer_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "rails_helper" - -describe PaginatedArraySerializer do - let(:users) { Kaminari.paginate_array(build_list(:user, 3)).page(1) } - let(:json) { ActiveModel::SerializableResource.serialize(users, serializer: PaginatedArraySerializer).to_json } - let(:parsed_json) { parse_json(json) } - - it "returns json with meta" do - expect(parsed_json).to include("meta") - end - - it "returns meta with pagination info" do - expect(parsed_json["meta"]).to include("pagination") - - expect(parsed_json["meta"]["pagination"]["total"]).to eq 3 - expect(parsed_json["meta"]["pagination"]["per_page"]).to eq 25 - expect(parsed_json["meta"]["pagination"]["page"]).to eq 1 - end -end diff --git a/spec/support/json_matchers.rb b/spec/support/json_matchers.rb new file mode 100644 index 0000000..d80a0e4 --- /dev/null +++ b/spec/support/json_matchers.rb @@ -0,0 +1,3 @@ +require "json_matchers/rspec" + +JsonMatchers.schema_root = "spec/schemas" diff --git a/spec/support/rspec_api_documentation.rb b/spec/support/rspec_api_documentation.rb new file mode 100644 index 0000000..3707899 --- /dev/null +++ b/spec/support/rspec_api_documentation.rb @@ -0,0 +1,14 @@ +require "rspec_api_documentation" +require "rspec_api_documentation/dsl" + +# rubocop:disable Style/WordArray +RspecApiDocumentation.configure do |config| + config.format = :markdown + config.docs_dir = Rails.root.join("doc", "api", "v1") + config.request_headers_to_include = ["Accept", "Content-Type", "Authorization"] + config.response_headers_to_include = ["Content-Type"] + config.curl_host = "http://#{ENV.fetch('HOST')}" + config.curl_headers_to_filter = ["Cookie", "Host", "Origin"] + config.keep_source_order = true +end +# rubocop:enable Style/WordArray diff --git a/spec/support/shared_contexts/json_api_headers.rb b/spec/support/shared_contexts/json_api_headers.rb new file mode 100644 index 0000000..6f1b70e --- /dev/null +++ b/spec/support/shared_contexts/json_api_headers.rb @@ -0,0 +1,17 @@ +shared_context "with JSON API Headers" do + header "Content-Type", "application/vnd.api+json" + header "Accept", "application/vnd.api+json" +end + +shared_context "with JSON API Authorization header" do + let(:current_user) { create(:user) } + let(:jwt_token) { build(:jwt_token, subject: current_user) } + let(:authorization) { "Bearer #{jwt_token.token}" } + + header "Authorization", :authorization +end + +shared_context "with JSON API post body from request class" do + let(:request_resource) { request_class.classify.constantize.new(params) } + let(:raw_post) { ActiveModelSerializers::SerializableResource.new(request_resource).to_json } +end diff --git a/spec/support/shared_examples/api_endpoint_with_authorization.rb b/spec/support/shared_examples/api_endpoint_with_authorization.rb new file mode 100644 index 0000000..4c6d59b --- /dev/null +++ b/spec/support/shared_examples/api_endpoint_with_authorization.rb @@ -0,0 +1,12 @@ +# rubocop:disable RSpec/EmptyExampleGroup +shared_examples "API endpoint with authorization" do + context "without authorization headers", document: false do + header "Authorization", "" + + example_request "Request without authorization header" do + expect(response_status).to eq(401) + expect(response_body).to match_response_schema("error") + end + end +end +# rubocop:enable RSpec/EmptyExampleGroup diff --git a/spec/support/shared_examples/failure_interactor.rb b/spec/support/shared_examples/failure_interactor.rb new file mode 100644 index 0000000..dd5fb27 --- /dev/null +++ b/spec/support/shared_examples/failure_interactor.rb @@ -0,0 +1,7 @@ +shared_examples "failure interactor" do |params| + it "fails" do + interactor.run + expect(context).to be_failure + expect(context.message).to eql(params[:message]) if params + end +end diff --git a/spec/support/shared_examples/success_interactor.rb b/spec/support/shared_examples/success_interactor.rb new file mode 100644 index 0000000..e4cf2b4 --- /dev/null +++ b/spec/support/shared_examples/success_interactor.rb @@ -0,0 +1,6 @@ +shared_examples "success interactor" do + it "success" do + interactor.run + expect(context).to be_success + end +end From 58ef02765474778effbbd293c4cbe3330d45f5a3 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Fri, 7 Sep 2018 16:35:09 +0300 Subject: [PATCH 05/26] WIP --- app/controllers/v1/tokens_controller.rb | 2 +- app/models/error.rb | 8 +++ app/models/json_api_request.rb | 2 + app/serializers/application_serializer.rb | 3 ++ .../json_api_request_serializer.rb | 21 ++++++++ app/serializers/jwt_token_serializer.rb | 3 ++ app/serializers/session_serializer.rb | 3 -- app/serializers/user_serializer.rb | 2 +- spec/api/v1/tokens_spec.rb | 16 ++++-- spec/rails_helper.rb | 50 ++----------------- spec/schemas/definitions.json | 2 + spec/schemas/error.json | 5 ++ spec/schemas/error.json1 | 3 -- spec/schemas/jsonapi.json | 12 +++-- spec/schemas/jwt_token.json | 7 ++- spec/spec_helper.rb | 28 ++++------- spec/support/database_cleaner.rb | 21 ++++++++ spec/support/email.rb | 8 +++ .../extensions/active_record/sliced.rb | 5 -- spec/support/factory_bot.rb | 3 ++ spec/support/helpers.rb | 9 ---- .../matchers/be_a_meta_representation_of.rb | 12 ----- .../matchers/be_a_session_representation.rb | 12 ----- .../matchers/be_a_user_representation.rb | 11 ---- spec/support/matchers/include_attributes.rb | 5 -- 25 files changed, 119 insertions(+), 134 deletions(-) create mode 100644 app/models/error.rb create mode 100644 app/models/json_api_request.rb create mode 100644 app/serializers/json_api_request_serializer.rb create mode 100644 app/serializers/jwt_token_serializer.rb delete mode 100644 app/serializers/session_serializer.rb create mode 100644 spec/schemas/error.json delete mode 100644 spec/schemas/error.json1 create mode 100644 spec/support/database_cleaner.rb create mode 100644 spec/support/email.rb delete mode 100644 spec/support/extensions/active_record/sliced.rb create mode 100644 spec/support/factory_bot.rb delete mode 100644 spec/support/helpers.rb delete mode 100644 spec/support/matchers/be_a_meta_representation_of.rb delete mode 100644 spec/support/matchers/be_a_session_representation.rb delete mode 100644 spec/support/matchers/be_a_user_representation.rb delete mode 100644 spec/support/matchers/include_attributes.rb diff --git a/app/controllers/v1/tokens_controller.rb b/app/controllers/v1/tokens_controller.rb index dce4cc0..f6b8d21 100644 --- a/app/controllers/v1/tokens_controller.rb +++ b/app/controllers/v1/tokens_controller.rb @@ -15,7 +15,7 @@ def create private def authentication_params - jsonapi_params(only: %i[email password]).merge(entity_name: :user) + jsonapi_params(only: %i[email password]) end end end diff --git a/app/models/error.rb b/app/models/error.rb new file mode 100644 index 0000000..8a2600e --- /dev/null +++ b/app/models/error.rb @@ -0,0 +1,8 @@ +class Error + attr_reader :errors + + def initialize(messages = {}) + @errors = ActiveModel::Errors.new(self) + messages.each { |attribute, message| @errors.add(attribute, message) } + end +end diff --git a/app/models/json_api_request.rb b/app/models/json_api_request.rb new file mode 100644 index 0000000..bff3166 --- /dev/null +++ b/app/models/json_api_request.rb @@ -0,0 +1,2 @@ +class JsonApiRequest < ActiveModelSerializers::Model +end diff --git a/app/serializers/application_serializer.rb b/app/serializers/application_serializer.rb index cfeb424..5e940b5 100644 --- a/app/serializers/application_serializer.rb +++ b/app/serializers/application_serializer.rb @@ -1,2 +1,5 @@ class ApplicationSerializer < ActiveModel::Serializer + def self.decorate_collection(collection) + ActiveModel::Serializer::CollectionSerializer.new(collection) + end end diff --git a/app/serializers/json_api_request_serializer.rb b/app/serializers/json_api_request_serializer.rb new file mode 100644 index 0000000..b7cf968 --- /dev/null +++ b/app/serializers/json_api_request_serializer.rb @@ -0,0 +1,21 @@ +params = { + type: "token", + email: "me@timurv.ru", + password: "123456" +} + +attributes = params.keys + +request_class = ActiveModel::Name.new(nil, nil, "token_request") do + include ActiveModel::Serialization + + attributes(*attributes) +end + +request = request_class.new(params) + +serializer_class = Class.new(ActiveModel::Serializer) do + attributes(*attributes) +end + +json = ActiveModelSerializers::SerializableResource.new(request, serializer: serializer_class).to_json diff --git a/app/serializers/jwt_token_serializer.rb b/app/serializers/jwt_token_serializer.rb new file mode 100644 index 0000000..9ea7a9c --- /dev/null +++ b/app/serializers/jwt_token_serializer.rb @@ -0,0 +1,3 @@ +class JwtTokenSerializer < ApplicationSerializer + attributes :token +end diff --git a/app/serializers/session_serializer.rb b/app/serializers/session_serializer.rb deleted file mode 100644 index 42d78ce..0000000 --- a/app/serializers/session_serializer.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SessionSerializer < UserSerializer - attributes :authentication_token -end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 4e58950..8b2302a 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -1,3 +1,3 @@ class UserSerializer < ApplicationSerializer - attributes :id, :email + attributes :email, :full_name end diff --git a/spec/api/v1/tokens_spec.rb b/spec/api/v1/tokens_spec.rb index 2facc0d..7d99381 100644 --- a/spec/api/v1/tokens_spec.rb +++ b/spec/api/v1/tokens_spec.rb @@ -12,13 +12,12 @@ class TokenRequestSerializer < ApplicationSerializer include_context "with JSON API Headers" post "/v1/tokens" do - let(:request_class) { "token_request" } - - let(:email) { "user@example.com" } parameter :email, "email", required: true + parameter :password, "password", required: true + let(:email) { "user@example.com" } let(:password) { "123456" } - parameter :password, "password", required: true + let(:request_class) { "token_request" } include_context "with JSON API post body from request class" @@ -30,5 +29,14 @@ class TokenRequestSerializer < ApplicationSerializer expect(response_status).to eq(201) expect(response_body).to match_response_schema("jwt_token") end + + context "with invalid password", document: false do + let(:password) { "invalid" } + + example_request "Create Token with invalid password" do + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index c555871..faed999 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,53 +1,13 @@ -ENV["RAILS_ENV"] = "test" +ENV["RAILS_ENV"] ||= "test" + require "spec_helper" -require File.expand_path("../config/environment", __dir__) +require File.expand_path("../../config/environment", __FILE__) require "rspec/rails" require "shoulda/matchers" -Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +Dir[Rails.root.join("spec", "support", "**", "*.rb")].each { |f| require f } RSpec.configure do |config| - # If you're not using ActiveRecord, or you'd prefer not to run each of your - # examples within a transaction, remove the following line or assign false - # instead of true. - config.use_transactional_fixtures = true - - # RSpec Rails can automatically mix in different behaviours to your tests - # based on their file location, for example enabling you to call `get` and - # `post` in specs under `spec/controllers`. - # - # You can disable this behaviour by removing the line below, and instead - # explicitly tag your specs with their type, e.g.: - # - # RSpec.describe UsersController, :type => :controller do - # # ... - # end - # - # The different available types are documented in the features, such as in - # https://relishapp.com/rspec/rspec-rails/docs + config.use_transactional_fixtures = false config.infer_spec_type_from_file_location! - - config.include Rails.application.routes.url_helpers - config.include EmailSpec::Helpers - config.include EmailSpec::Matchers - config.include FactoryBot::Syntax::Methods - config.include Helpers - config.include JsonSpec::Helpers - - config.before :suite do - DatabaseCleaner.strategy = :transaction - DatabaseCleaner.clean_with :truncation - end - - config.before do - ActionMailer::Base.deliveries.clear - end - - config.before(:each) do - DatabaseCleaner.start - end - - config.after(:each) do - DatabaseCleaner.clean - end end diff --git a/spec/schemas/definitions.json b/spec/schemas/definitions.json index 86233bf..1041386 100644 --- a/spec/schemas/definitions.json +++ b/spec/schemas/definitions.json @@ -1,4 +1,6 @@ { + "id": "file:/definitions.json#", + "definitions": { "user": { "required": ["attributes"], diff --git a/spec/schemas/error.json b/spec/schemas/error.json new file mode 100644 index 0000000..2f127dd --- /dev/null +++ b/spec/schemas/error.json @@ -0,0 +1,5 @@ +{ + "allOf": [ + { "$ref": "file:/jsonapi.json#/definitions/failure" } + ] +} diff --git a/spec/schemas/error.json1 b/spec/schemas/error.json1 deleted file mode 100644 index 45dd5c1..0000000 --- a/spec/schemas/error.json1 +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$ref": "file:/jsonapi.json#/definitions/failure" -} diff --git a/spec/schemas/jsonapi.json b/spec/schemas/jsonapi.json index c19d092..201a691 100644 --- a/spec/schemas/jsonapi.json +++ b/spec/schemas/jsonapi.json @@ -1,4 +1,6 @@ { + "id": "file:/jsonapi.json#", + "$schema": "http://json-schema.org/draft-04/schema#", "title": "JSON API Schema", "description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org", @@ -13,7 +15,7 @@ "$ref": "#/definitions/info" } ], - + "definitions": { "success": { "type": "object", @@ -95,7 +97,7 @@ }, "additionalProperties": false }, - + "meta": { "description": "Non-standard meta-information that can not be represented as an attribute or relationship.", "type": "object", @@ -150,7 +152,7 @@ }, "additionalProperties": false }, - + "links": { "description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.", "type": "object", @@ -314,7 +316,7 @@ } } }, - + "jsonapi": { "description": "An object describing the server's implementation", "type": "object", @@ -328,7 +330,7 @@ }, "additionalProperties": false }, - + "error": { "type": "object", "properties": { diff --git a/spec/schemas/jwt_token.json b/spec/schemas/jwt_token.json index 9c0c5f5..812e119 100644 --- a/spec/schemas/jwt_token.json +++ b/spec/schemas/jwt_token.json @@ -1,5 +1,10 @@ { "allOf": [ - { "$ref": "jsonapi.json#/definitions/success" } + { "$ref": "file:/jsonapi.json#/definitions/success" }, + { + "properties": { + "data": { "$ref": "file:/definitions.json#/definitions/jwt_token" } + } + } ] } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eecfb1d..9c3435d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,19 +1,13 @@ -# This file was generated by the `rails generate rspec:install` command. Conventionally, all -# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. -# The generated `.rspec` file contains `--require spec_helper` which will cause this -# file to always be loaded, without a need to explicitly require it in any files. -# -# Given that it is always loaded, you are encouraged to keep this file as -# light-weight as possible. Requiring heavyweight dependencies from this file -# will add to the boot time of your test suite on EVERY test run, even for an -# individual file that may not need all of that loaded. Instead, make a -# separate helper file that requires this one and then use it only in the specs -# that actually need it. -# -# The `.rspec` file also contains a few flags that are not defaults but that -# users commonly want. -# -# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| - config.order = :random + config.backtrace_exclusion_patterns << /\.bundle/ + + config.expect_with :rspec do |c| + c.syntax = :expect + end + + config.mock_with :rspec do |mocks| + mocks.syntax = :expect + mocks.verify_partial_doubles = true + mocks.verify_doubled_constant_names = true + end end diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb new file mode 100644 index 0000000..232e01d --- /dev/null +++ b/spec/support/database_cleaner.rb @@ -0,0 +1,21 @@ +RSpec.configure do |config| + config.before(:suite) do + DatabaseCleaner.clean_with(:deletion) + end + + config.before do + DatabaseCleaner.strategy = :transaction + end + + config.before(:each, js: true) do + DatabaseCleaner.strategy = :deletion + end + + config.before do + DatabaseCleaner.start + end + + config.after do + DatabaseCleaner.clean + end +end diff --git a/spec/support/email.rb b/spec/support/email.rb new file mode 100644 index 0000000..f95815b --- /dev/null +++ b/spec/support/email.rb @@ -0,0 +1,8 @@ +RSpec.configure do |config| + config.include EmailSpec::Helpers + config.include EmailSpec::Matchers + + config.before do + ActionMailer::Base.deliveries.clear + end +end diff --git a/spec/support/extensions/active_record/sliced.rb b/spec/support/extensions/active_record/sliced.rb deleted file mode 100644 index d78678b..0000000 --- a/spec/support/extensions/active_record/sliced.rb +++ /dev/null @@ -1,5 +0,0 @@ -ActiveRecord::Base.class_eval do - def sliced_attributes(keys) - attributes.slice(*keys) - end -end diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb new file mode 100644 index 0000000..c7890e4 --- /dev/null +++ b/spec/support/factory_bot.rb @@ -0,0 +1,3 @@ +RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods +end diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb deleted file mode 100644 index 0d34a7d..0000000 --- a/spec/support/helpers.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Helpers - def setup_devise_mapping(mapping_name = :user) - @request.env["devise.mapping"] = Devise.mappings[mapping_name] - end - - def json_response_body - parse_json(response_body) - end -end diff --git a/spec/support/matchers/be_a_meta_representation_of.rb b/spec/support/matchers/be_a_meta_representation_of.rb deleted file mode 100644 index 43f4c95..0000000 --- a/spec/support/matchers/be_a_meta_representation_of.rb +++ /dev/null @@ -1,12 +0,0 @@ -RSpec::Matchers.define :be_a_meta_representation_of do |posts, params| - match do |json| - params = params.stringify_keys - - expect(json).to be - expect(json).to include("pagination") - - expect(json["pagination"]["page"]).to eq params["page"] - expect(json["pagination"]["per_page"]).to eq params["per_page"] - expect(json["pagination"]["total"]).to eq posts.size - end -end diff --git a/spec/support/matchers/be_a_session_representation.rb b/spec/support/matchers/be_a_session_representation.rb deleted file mode 100644 index e874869..0000000 --- a/spec/support/matchers/be_a_session_representation.rb +++ /dev/null @@ -1,12 +0,0 @@ -RSpec::Matchers.define :be_a_session_representation do - match do |json| - response_attributes = %w( - id - authentication_token - email - ) - - expect(json).to be - expect(json.keys).to match_array(response_attributes) - end -end diff --git a/spec/support/matchers/be_a_user_representation.rb b/spec/support/matchers/be_a_user_representation.rb deleted file mode 100644 index 1f3c29f..0000000 --- a/spec/support/matchers/be_a_user_representation.rb +++ /dev/null @@ -1,11 +0,0 @@ -RSpec::Matchers.define :be_a_user_representation do |user| - match do |json| - response_attributes = user.sliced_attributes %w( - id - email - ) - - expect(json).to be - expect(json).to include_attributes(response_attributes) - end -end diff --git a/spec/support/matchers/include_attributes.rb b/spec/support/matchers/include_attributes.rb deleted file mode 100644 index a6e7a70..0000000 --- a/spec/support/matchers/include_attributes.rb +++ /dev/null @@ -1,5 +0,0 @@ -RSpec::Matchers.define :include_attributes do |expected| - match do |actual| - expect(actual).to RSpec::Matchers::BuiltIn::Include.new(expected.as_json) - end -end From c368f2d217d509e7e41ea92da54222fbb57ecedc Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Fri, 7 Sep 2018 18:00:35 +0300 Subject: [PATCH 06/26] Add create user endpoint --- .byebug_history | 6 +++ app/controllers/v1/users_controller.rb | 21 +++++++++ app/interactors/create_user.rb | 24 ++++++++++ app/models/user.rb | 3 ++ config/locales/interactors.en.yml | 9 ++++ config/routes.rb | 1 + spec/api/errors_spec.rb | 5 +- spec/api/v1/tokens_spec.rb | 5 +- spec/api/v1/users_spec.rb | 51 +++++++++++++++++++++ spec/schemas/definitions.json | 2 +- spec/schemas/user.json | 10 ++++ spec/schemas/users.json | 4 +- spec/serializers/session_serializer_spec.rb | 11 ----- spec/serializers/user_serializer_spec.rb | 11 ----- 14 files changed, 133 insertions(+), 30 deletions(-) create mode 100644 app/controllers/v1/users_controller.rb create mode 100644 app/interactors/create_user.rb create mode 100644 config/locales/interactors.en.yml create mode 100644 spec/api/v1/users_spec.rb create mode 100644 spec/schemas/user.json delete mode 100644 spec/serializers/session_serializer_spec.rb delete mode 100644 spec/serializers/user_serializer_spec.rb diff --git a/.byebug_history b/.byebug_history index e9a22a0..d1ead27 100644 --- a/.byebug_history +++ b/.byebug_history @@ -1,4 +1,10 @@ continue +params +continue +params +continue +params +continue JsonSchema.parse!(schema_data).uri continue JsonSchema.parse!(schema_data).uri diff --git a/app/controllers/v1/users_controller.rb b/app/controllers/v1/users_controller.rb new file mode 100644 index 0000000..ff2a17a --- /dev/null +++ b/app/controllers/v1/users_controller.rb @@ -0,0 +1,21 @@ +module V1 + class UsersController < V1::BaseController + skip_before_action :authenticate_user + + expose :user + + def create + if user.save + respond_with_resource(user, status: :created, location: nil) + else + respond_with_resource_errors(user) + end + end + + private + + def user_params + jsonapi_params(only: %i[email password]) + end + end +end diff --git a/app/interactors/create_user.rb b/app/interactors/create_user.rb new file mode 100644 index 0000000..2a8ab68 --- /dev/null +++ b/app/interactors/create_user.rb @@ -0,0 +1,24 @@ +class CreateUser + include Interactor + + delegate :email, :password, to: :context + + def call + context.fail!(message: I18n.t("interactors.create_user.invalid_credentials")) unless authenticated? + context.jwt_token = jwt_token + end + + private + + def authenticated? + user.present? && user.authenticate(password) + end + + def jwt_token + JwtToken.new(payload: { sub: user.id }) + end + + def user + @user ||= User.find_by(email: email) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index d67da20..4b95478 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,3 +1,6 @@ class User < ApplicationRecord has_secure_password + + validates :email, presence: true + validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } end diff --git a/config/locales/interactors.en.yml b/config/locales/interactors.en.yml new file mode 100644 index 0000000..6e1e1b9 --- /dev/null +++ b/config/locales/interactors.en.yml @@ -0,0 +1,9 @@ +en: + interactors: + authenticate_user: + success: Successfully authenticated. + create_jwt: + success: Successfully authenticated. + invalid_credentials: Invalid credentials. + authenticate: + invalid_credentials: Invalid credentials. diff --git a/config/routes.rb b/config/routes.rb index ab6191f..50b08c4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do namespace :v1, defaults: { format: "json" } do resources :tokens, only: :create + resources :users, only: :create end end diff --git a/spec/api/errors_spec.rb b/spec/api/errors_spec.rb index 592dfa5..55e8318 100644 --- a/spec/api/errors_spec.rb +++ b/spec/api/errors_spec.rb @@ -2,13 +2,12 @@ require "rspec_api_documentation/dsl" resource "Errors" do - header "Accept", "application/json" - subject(:response) { json_response_body } + include_context "with JSON API Headers" get "/not-found" do example_request "Request to unexisting page" do expect(response_status).to eq 404 - expect(response).to be_an_error_representation(:not_found, "Not Found") + expect(response_body).to match_response_schema("error") end end end diff --git a/spec/api/v1/tokens_spec.rb b/spec/api/v1/tokens_spec.rb index 7d99381..82885e3 100644 --- a/spec/api/v1/tokens_spec.rb +++ b/spec/api/v1/tokens_spec.rb @@ -30,10 +30,11 @@ class TokenRequestSerializer < ApplicationSerializer expect(response_body).to match_response_schema("jwt_token") end - context "with invalid password", document: false do + context "with invalid password" do let(:password) { "invalid" } - example_request "Create Token with invalid password" do + example "Create Token with invalid password", document: false do + do_request expect(response_status).to eq(422) expect(response_body).to match_response_schema("error") end diff --git a/spec/api/v1/users_spec.rb b/spec/api/v1/users_spec.rb new file mode 100644 index 0000000..451f50f --- /dev/null +++ b/spec/api/v1/users_spec.rb @@ -0,0 +1,51 @@ +require "rails_helper" + +class UserRequest < ActiveModelSerializers::Model + attributes :id, :full_name, :email, :password +end + +class UserRequestSerializer < ApplicationSerializer + attributes :full_name, :email, :password +end + +resource "Users" do + include_context "with JSON API Headers" + + post "/v1/users" do + parameter :full_name, "full name" + parameter :email, "email", required: true + parameter :password, "password", required: true + + let(:full_name) { "Example User" } + let(:email) { "user@example.com" } + let(:password) { "123456" } + let(:request_class) { "user_request" } + + include_context "with JSON API post body from request class" + + example_request "Create User" do + expect(response_status).to eq(201) + expect(response_body).to match_response_schema("user") + end + + context "with invalid email" do + let(:email) { "invalid" } + + example "Create User with invalid email", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + + context "with blank password" do + let(:password) { "" } + + example "Create User with invalid password", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + end +end diff --git a/spec/schemas/definitions.json b/spec/schemas/definitions.json index 1041386..2ca26a4 100644 --- a/spec/schemas/definitions.json +++ b/spec/schemas/definitions.json @@ -9,7 +9,7 @@ "required": ["email", "full-name"], "properties": { "email": { "type": "string" }, - "full-name": { "type": "string" } + "full-name": { "type": ["string", "null"] } } } } diff --git a/spec/schemas/user.json b/spec/schemas/user.json new file mode 100644 index 0000000..b1b19fe --- /dev/null +++ b/spec/schemas/user.json @@ -0,0 +1,10 @@ +{ + "allOf": [ + { "$ref": "file:/jsonapi.json#/definitions/success" }, + { + "properties": { + "data": { "$ref": "file:/definitions.json#/definitions/user" } + } + } + ] +} diff --git a/spec/schemas/users.json b/spec/schemas/users.json index 2dbf186..21fe9e8 100644 --- a/spec/schemas/users.json +++ b/spec/schemas/users.json @@ -1,10 +1,10 @@ { "allOf": [ - { "$ref": "jsonapi.json#/definitions/success" }, + { "$ref": "file:/jsonapi.json#/definitions/success" }, { "properties": { "data": { - "items": { "$ref": "definitions.json#/definitions/user" } + "items": { "$ref": "file:/definitions.json#/definitions/user" } } } } diff --git a/spec/serializers/session_serializer_spec.rb b/spec/serializers/session_serializer_spec.rb deleted file mode 100644 index 5fae7b9..0000000 --- a/spec/serializers/session_serializer_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "rails_helper" - -describe SessionSerializer do - let(:user) { build(:user) } - let(:json) { ActiveModel::SerializableResource.serialize(user, serializer: described_class).to_json } - let(:user_json) { parse_json(json)["user"] } - - it "returns user with session data" do - expect(user_json).to be_a_session_representation - end -end diff --git a/spec/serializers/user_serializer_spec.rb b/spec/serializers/user_serializer_spec.rb deleted file mode 100644 index 6a2342b..0000000 --- a/spec/serializers/user_serializer_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "rails_helper" - -describe UserSerializer do - let(:user) { build :user, id: 1, authentication_token: "token" } - let(:json) { ActiveModel::SerializableResource.serialize(user).to_json } - let(:user_json) { parse_json(json)["user"] } - - it "returns user" do - expect(user_json).to be_a_user_representation(user) - end -end From 4bcb008f6f6153c76ff58a1017a0d46dcfae2f15 Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Fri, 7 Sep 2018 19:57:54 +0300 Subject: [PATCH 07/26] wip with profiles --- .byebug_history | 19 ++++++ app/controllers/v1/base_controller.rb | 11 ++++ app/controllers/v1/profiles_controller.rb | 26 ++++++++ app/controllers/v1/users_controller.rb | 15 ++++- app/models/user.rb | 2 +- config/locales/devise.en.yml | 60 ----------------- config/locales/errors.en.yml | 5 ++ config/routes.rb | 5 +- spec/api/errors_spec.rb | 2 +- spec/api/v1/profiles_spec.rb | 80 +++++++++++++++++++++++ spec/api/v1/users_spec.rb | 55 +++++++++++++++- spec/factories/jwt_tokens.rb | 13 ++++ 12 files changed, 224 insertions(+), 69 deletions(-) create mode 100644 app/controllers/v1/profiles_controller.rb delete mode 100644 config/locales/devise.en.yml create mode 100644 config/locales/errors.en.yml create mode 100644 spec/api/v1/profiles_spec.rb create mode 100644 spec/factories/jwt_tokens.rb diff --git a/.byebug_history b/.byebug_history index d1ead27..7929837 100644 --- a/.byebug_history +++ b/.byebug_history @@ -1,5 +1,24 @@ continue params +user_params +continue +respond_with_resource_errors(current_user) +current_user.update(user_params) +continue +params +respond_with_resource_errors(current_user) +current_user.update(user_params) +current_user +user_params +continue +users +User.all.count +User.all +continue +User.all +users +continue +params continue params continue diff --git a/app/controllers/v1/base_controller.rb b/app/controllers/v1/base_controller.rb index dcf2ff4..82011c1 100644 --- a/app/controllers/v1/base_controller.rb +++ b/app/controllers/v1/base_controller.rb @@ -4,6 +4,9 @@ class BaseController < ActionController::API before_action :authenticate_user + rescue_from ActiveRecord::RecordNotFound, with: :respond_with_record_not_found + rescue_from ActionController::RoutingError, with: :respond_with_route_not_found + private def authenticate_user @@ -34,6 +37,14 @@ def respond_with_unauthorized respond_with_error(I18n.t("interactors.authenticate.invalid_credentials"), status: :unauthorized) end + def respond_with_record_not_found + respond_with_error(I18n.t("generic_errors.record_not_found"), status: :not_found) + end + + def respond_with_route_not_found + respond_with_error(I18n.t("generic_errors.route_not_found"), status: :not_found) + end + def jsonapi_params(options) ActiveModelSerializers::Deserialization.jsonapi_parse!(params, options) end diff --git a/app/controllers/v1/profiles_controller.rb b/app/controllers/v1/profiles_controller.rb new file mode 100644 index 0000000..9b5bfb7 --- /dev/null +++ b/app/controllers/v1/profiles_controller.rb @@ -0,0 +1,26 @@ +module V1 + class ProfilesController < V1::BaseController + def show + respond_with_resource(current_user, location: :v1_profile) + end + + def update + if current_user.update(user_params) + respond_with_resource(current_user, status: :ok, location: :v1_profile) + else + respond_with_resource_errors(current_user) + end + end + + def destroy + current_user.destroy + respond_with_resource(current_user, location: nil) + end + + private + + def user_params + jsonapi_params(only: %i[full_name email password]) + end + end +end diff --git a/app/controllers/v1/users_controller.rb b/app/controllers/v1/users_controller.rb index ff2a17a..b658091 100644 --- a/app/controllers/v1/users_controller.rb +++ b/app/controllers/v1/users_controller.rb @@ -1,12 +1,21 @@ module V1 class UsersController < V1::BaseController - skip_before_action :authenticate_user + skip_before_action :authenticate_user, only: :create expose :user + expose :users, -> { User.all } + + def index + respond_with_resources(users) + end + + def show + respond_with_resource(user, location: :v1_user) + end def create if user.save - respond_with_resource(user, status: :created, location: nil) + respond_with_resource(user, status: :created, location: :v1_profile) else respond_with_resource_errors(user) end @@ -15,7 +24,7 @@ def create private def user_params - jsonapi_params(only: %i[email password]) + jsonapi_params(only: %i[full_name email password]) end end end diff --git a/app/models/user.rb b/app/models/user.rb index 4b95478..962ce70 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,6 @@ class User < ApplicationRecord has_secure_password - validates :email, presence: true + validates :email, presence: true, uniqueness: true validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml deleted file mode 100644 index 26a10f2..0000000 --- a/config/locales/devise.en.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Additional translations at https://github.com/plataformatec/devise/wiki/I18n - -en: - devise: - confirmations: - confirmed: "Your email address has been successfully confirmed." - send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." - failure: - already_authenticated: "You are already signed in." - inactive: "Your account is not activated yet." - invalid: "Invalid %{authentication_keys} or password." - locked: "Your account is locked." - last_attempt: "You have one more attempt before your account is locked." - not_found_in_database: "Invalid %{authentication_keys} or password." - timeout: "Your session expired. Please sign in again to continue." - unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your email address before continuing." - mailer: - confirmation_instructions: - subject: "Confirmation instructions" - reset_password_instructions: - subject: "Reset password instructions" - unlock_instructions: - subject: "Unlock instructions" - omniauth_callbacks: - failure: "Could not authenticate you from %{kind} because \"%{reason}\"." - success: "Successfully authenticated from %{kind} account." - passwords: - no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password has been changed successfully. You are now signed in." - updated_not_active: "Your password has been changed successfully." - registrations: - destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." - signed_up: "Welcome! You have signed up successfully." - signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." - signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." - updated: "Your account has been updated successfully." - sessions: - signed_in: "Signed in successfully." - signed_out: "Signed out successfully." - already_signed_out: "Signed out successfully." - unlocks: - send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." - unlocked: "Your account has been unlocked successfully. Please sign in to continue." - errors: - messages: - already_confirmed: "was already confirmed, please try signing in" - confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" - expired: "has expired, please request a new one" - not_found: "not found" - not_locked: "was not locked" - not_saved: - one: "1 error prohibited this %{resource} from being saved:" - other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/config/locales/errors.en.yml b/config/locales/errors.en.yml new file mode 100644 index 0000000..6e2faab --- /dev/null +++ b/config/locales/errors.en.yml @@ -0,0 +1,5 @@ +en: + generic_errors: + record_not_found: "Record not found" + route_not_found: "Route not found" + diff --git a/config/routes.rb b/config/routes.rb index 50b08c4..b1acc6a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ Rails.application.routes.draw do - namespace :v1, defaults: { format: "json" } do + namespace :v1, defaults: { format: "jsonapi" } do resources :tokens, only: :create - resources :users, only: :create + resources :users, only: %i(index show create) + resource :profile, only: %i(show update destroy) end end diff --git a/spec/api/errors_spec.rb b/spec/api/errors_spec.rb index 55e8318..8697cfa 100644 --- a/spec/api/errors_spec.rb +++ b/spec/api/errors_spec.rb @@ -6,7 +6,7 @@ get "/not-found" do example_request "Request to unexisting page" do - expect(response_status).to eq 404 + expect(response_status).to eq(404) expect(response_body).to match_response_schema("error") end end diff --git a/spec/api/v1/profiles_spec.rb b/spec/api/v1/profiles_spec.rb new file mode 100644 index 0000000..6ce9540 --- /dev/null +++ b/spec/api/v1/profiles_spec.rb @@ -0,0 +1,80 @@ +require "rails_helper" + +class ProfileRequest < ActiveModelSerializers::Model + attributes :id, :full_name, :email, :password +end + +class ProfileRequestSerializer < ApplicationSerializer + attributes :full_name, :email, :password +end + +resource "Profiles" do + include_context "with JSON API Headers" + include_context "with JSON API Authorization header" + include_context "with JSON API post body from request class" + + get "/v1/profile" do + let(:request_class) { "profile_request" } + + example_request "Retrive Profile" do + expect(response_status).to eq(200) + expect(response_body).to match_response_schema("users") + end + end + + patch "/v1/profile" do + parameter :full_name, "full name" + parameter :email, "email" + parameter :password, "password" + + let(:full_name) { "Example User Updated" } + let(:email) { "user_updated@example.com" } + let(:request_class) { "profile_request" } + + example_request "Update Profile" do + expect(response_status).to eq(200) + expect(response_body).to match_response_schema("user") + end + + context "with invalid email" do + let(:email) { "invalid" } + + example "Update Profile with invalid email", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + + context "with blank password" do + let(:password) { "" } + + example "Update Profile with invalid password", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + + context "when user already exists" do + before do + create :user, email: email + end + + example "Update Profile with existing email", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + end + + delete "/v1/profile" do + let(:request_class) { "profile_request" } + + example_request "Delete Profile" do + expect(response_status).to eq(200) + expect(response_body).to match_response_schema("user") + end + end +end diff --git a/spec/api/v1/users_spec.rb b/spec/api/v1/users_spec.rb index 451f50f..9b22360 100644 --- a/spec/api/v1/users_spec.rb +++ b/spec/api/v1/users_spec.rb @@ -10,6 +10,47 @@ class UserRequestSerializer < ApplicationSerializer resource "Users" do include_context "with JSON API Headers" + include_context "with JSON API post body from request class" + + get "/v1/users" do + include_context "with JSON API Authorization header" + + let(:request_class) { "user_request" } + + before do + create_list :user, 3 + end + + example_request "List Users" do + expect(response_status).to eq(200) + expect(response_body).to match_response_schema("users") + end + end + + get "/v1/users/:id" do + include_context "with JSON API Authorization header" + + parameter :id, "user id", required: true + + let(:user) { create :user } + let(:id) { user.id } + let(:request_class) { "user_request" } + + example_request "Retrive User" do + expect(response_status).to eq(200) + expect(response_body).to match_response_schema("user") + end + + context "with invalid id" do + let(:id) { 332 } + + example "Retrive User with invalid id", document: false do + do_request + expect(response_status).to eq(404) + expect(response_body).to match_response_schema("error") + end + end + end post "/v1/users" do parameter :full_name, "full name" @@ -21,8 +62,6 @@ class UserRequestSerializer < ApplicationSerializer let(:password) { "123456" } let(:request_class) { "user_request" } - include_context "with JSON API post body from request class" - example_request "Create User" do expect(response_status).to eq(201) expect(response_body).to match_response_schema("user") @@ -47,5 +86,17 @@ class UserRequestSerializer < ApplicationSerializer expect(response_body).to match_response_schema("error") end end + + context "when user already exists" do + before do + create :user, email: email + end + + example "Create User with existing email", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end end end diff --git a/spec/factories/jwt_tokens.rb b/spec/factories/jwt_tokens.rb new file mode 100644 index 0000000..06f3349 --- /dev/null +++ b/spec/factories/jwt_tokens.rb @@ -0,0 +1,13 @@ +FactoryBot.define do + factory :jwt_token do + transient do + subject { create :user } + + id { subject.id } + end + + payload { { "sub" => id } } + + initialize_with { new(payload: payload) } + end +end From d06e28a3bb024e0b7b08f5dbfd1af9f2f4be3b0d Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Fri, 7 Sep 2018 22:59:01 +0300 Subject: [PATCH 08/26] generate docs --- .byebug_history | 30 +++++ .rubocop.yml | 104 ++++++++++++++++ Gemfile | 9 +- Gemfile.lock | 8 ++ Rakefile | 1 - app/controllers/v1/sign_ups_controller.rb | 21 ++++ app/controllers/v1/users_controller.rb | 16 --- app/interactors/create_user.rb | 24 ---- app/models/user.rb | 5 +- .../json_api_request_serializer.rb | 21 ---- bin/quality | 7 -- config/application.rb | 4 +- config/boot.rb | 6 +- config/environment.rb | 2 +- config/environments/development.rb | 5 +- config/environments/production.rb | 12 +- config/environments/test.rb | 2 +- config/initializers/apitome.rb | 107 +++++++++-------- config/initializers/bullet.rb | 16 +-- config/initializers/cors.rb | 16 +-- config/initializers/health_check.rb | 4 +- config/routes.rb | 5 +- config/rubocop.yml | 58 --------- doc/api/index.html | 113 ++++++++++++++++++ .../v1/errors/request_to_unexisting_page.json | 34 ------ doc/api/v1/index.json | 29 ----- doc/api/v1/index.md | 22 ++++ doc/api/v1/profiles/delete_profile.md | 50 ++++++++ doc/api/v1/profiles/retrive_profile.md | 50 ++++++++ doc/api/v1/profiles/update_profile.md | 59 +++++++++ .../sign_in_with_invalid_password.json | 43 ------- .../sessions/sign_in_with_valid_password.json | 43 ------- doc/api/v1/signup/create_user.md | 57 +++++++++ doc/api/v1/tokens/create_token.md | 55 +++++++++ doc/api/v1/users/list_users.md | 76 ++++++++++++ doc/api/v1/users/retrive_user.md | 57 +++++++++ lib/tasks/docs.rake | 16 +++ spec/api/errors_spec.rb | 13 +- spec/api/v1/profiles_spec.rb | 7 +- spec/api/v1/sign_ups_spec.rb | 62 ++++++++++ spec/api/v1/users_spec.rb | 53 +------- spec/interactors/create_jwt_spec.rb | 2 +- spec/rails_helper.rb | 2 +- 43 files changed, 894 insertions(+), 432 deletions(-) create mode 100644 .rubocop.yml create mode 100644 app/controllers/v1/sign_ups_controller.rb delete mode 100644 app/interactors/create_user.rb delete mode 100644 app/serializers/json_api_request_serializer.rb delete mode 100644 config/rubocop.yml create mode 100644 doc/api/index.html delete mode 100644 doc/api/v1/errors/request_to_unexisting_page.json delete mode 100644 doc/api/v1/index.json create mode 100644 doc/api/v1/index.md create mode 100644 doc/api/v1/profiles/delete_profile.md create mode 100644 doc/api/v1/profiles/retrive_profile.md create mode 100644 doc/api/v1/profiles/update_profile.md delete mode 100644 doc/api/v1/sessions/sign_in_with_invalid_password.json delete mode 100644 doc/api/v1/sessions/sign_in_with_valid_password.json create mode 100644 doc/api/v1/signup/create_user.md create mode 100644 doc/api/v1/tokens/create_token.md create mode 100644 doc/api/v1/users/list_users.md create mode 100644 doc/api/v1/users/retrive_user.md create mode 100644 lib/tasks/docs.rake create mode 100644 spec/api/v1/sign_ups_spec.rb diff --git a/.byebug_history b/.byebug_history index 7929837..3eae956 100644 --- a/.byebug_history +++ b/.byebug_history @@ -1,4 +1,34 @@ continue +ActiveModelSerializers::SerializableResource.new(t).to_json +t=request_class.classify.constantize.new(params) +request_class.classify.constantize.new(params) +params +continue +params +request_class.classify.constantize.new(params) +continue +request_class.classify.constantize.new(params) +params.publicrequest_class.classify.constantize.new(params) +params.data +ProfileRequest.new(params.as_json) +ProfileRequest.new(params.to_json) +params.to_json +params +ProfileRequest.new(params) +ProfileRequest.constantize.new(params) +params +user_params +continue +user_params +params +continue +params +continue +params +continue +user_params +params +continue params user_params continue diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..fb62bd7 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,104 @@ +require: rubocop-rspec + +Rails: + Enabled: true + +AllCops: + TargetRubyVersion: 2.3 + DisplayCopNames: true + Exclude: + - bin/**/* + - db/**/* + - vendor/**/* + - node_modules/**/* + +Rails/UnknownEnv: + Environments: + - production + - development + - test + - staging + +Rails/HasManyOrHasOneDependent: + Enabled: true + +Rails/HasAndBelongsToMany: + Enabled: false + +Rails/OutputSafety: + Enabled: false + +RSpec/MultipleExpectations: + Enabled: false + +RSpec/ExampleLength: + Enabled: false + +RSpec/RepeatedDescription: + Enabled: false + +Capybara/FeatureMethods: + Enabled: false + +RSpec/MessageSpies: + EnforcedStyle: receive + +RSpec/EmptyExampleGroup: + Exclude: + - spec/api/**/* + +Style/AndOr: + Enabled: false + +Style/Documentation: + Enabled: false + +Style/MethodCalledOnDoEndBlock: + Enabled: true + +Style/CollectionMethods: + Enabled: true + +Style/SymbolArray: + Enabled: true + +Style/StringLiterals: + EnforcedStyle: double_quotes + ConsistentQuotesInMultiline: true + +Style/EmptyMethod: + EnforcedStyle: expanded + SupportedStyles: + - compact + - expanded + +Style/FrozenStringLiteralComment: + Enabled: false + +Style/StringMethods: + Enabled: true + +Layout/MultilineMethodCallIndentation: + EnforcedStyle: indented + +Layout/AlignParameters: + EnforcedStyle: with_fixed_indentation + SupportedStyles: + - with_first_parameter + - with_fixed_indentation + +Metrics/LineLength: + Max: 120 + +Metrics/BlockLength: + Enabled: false + +Layout/EndAlignment: + EnforcedStyleAlignWith: variable + SupportedStylesAlignWith: + - keyword + - variable + +Lint/AmbiguousBlockAssociation: + Exclude: + - spec/**/* diff --git a/Gemfile b/Gemfile index 318ad59..e53454f 100644 --- a/Gemfile +++ b/Gemfile @@ -21,9 +21,9 @@ gem "rollbar" gem "seedbank" group :development do - gem "letter_opener" - gem "foreman" gem "bullet" + gem "foreman" + gem "letter_opener" gem "listen", ">= 3.0.5", "< 3.2" gem "spring" @@ -36,8 +36,9 @@ group :development, :test do gem "rspec-rails" gem "brakeman" - gem "rubocop" gem "bundler-audit" + gem "rubocop" + gem "rubocop-rspec" end group :test do @@ -52,8 +53,8 @@ group :test do end group :development, :test, :staging do + gem "apitome", require: false gem "factory_bot_rails" gem "faker" gem "rspec_api_documentation" - # gem "apitome" end diff --git a/Gemfile.lock b/Gemfile.lock index fb76039..80af8ec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,6 +49,9 @@ GEM tzinfo (~> 1.1) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) + apitome (0.2.0) + kramdown + railties arel (9.0.0) ast (2.4.0) bcrypt (3.1.12) @@ -128,6 +131,7 @@ GEM bcrypt (~> 3.1) jwt (~> 1.5) rails (>= 4.2) + kramdown (1.17.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.6.0) @@ -231,6 +235,8 @@ GEM rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) + rubocop-rspec (1.29.1) + rubocop (>= 0.58.0) ruby-progressbar (1.10.0) ruby_dep (1.5.0) safe_yaml (1.0.4) @@ -272,6 +278,7 @@ PLATFORMS DEPENDENCIES active_model_serializers + apitome bcrypt bootsnap brakeman @@ -301,6 +308,7 @@ DEPENDENCIES rspec-rails rspec_api_documentation rubocop + rubocop-rspec seedbank shoulda-matchers simplecov diff --git a/Rakefile b/Rakefile index 041dae9..99e6b8a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,3 @@ -#!/usr/bin/env rake # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. diff --git a/app/controllers/v1/sign_ups_controller.rb b/app/controllers/v1/sign_ups_controller.rb new file mode 100644 index 0000000..482e188 --- /dev/null +++ b/app/controllers/v1/sign_ups_controller.rb @@ -0,0 +1,21 @@ +module V1 + class SignUpsController < V1::BaseController + skip_before_action :authenticate_user + + expose :user + + def create + if user.save + respond_with_resource(user, status: :created, location: :v1_profile) + else + respond_with_resource_errors(user) + end + end + + private + + def user_params + jsonapi_params(only: %i[full_name email password]) + end + end +end diff --git a/app/controllers/v1/users_controller.rb b/app/controllers/v1/users_controller.rb index b658091..87657d7 100644 --- a/app/controllers/v1/users_controller.rb +++ b/app/controllers/v1/users_controller.rb @@ -1,7 +1,5 @@ module V1 class UsersController < V1::BaseController - skip_before_action :authenticate_user, only: :create - expose :user expose :users, -> { User.all } @@ -12,19 +10,5 @@ def index def show respond_with_resource(user, location: :v1_user) end - - def create - if user.save - respond_with_resource(user, status: :created, location: :v1_profile) - else - respond_with_resource_errors(user) - end - end - - private - - def user_params - jsonapi_params(only: %i[full_name email password]) - end end end diff --git a/app/interactors/create_user.rb b/app/interactors/create_user.rb deleted file mode 100644 index 2a8ab68..0000000 --- a/app/interactors/create_user.rb +++ /dev/null @@ -1,24 +0,0 @@ -class CreateUser - include Interactor - - delegate :email, :password, to: :context - - def call - context.fail!(message: I18n.t("interactors.create_user.invalid_credentials")) unless authenticated? - context.jwt_token = jwt_token - end - - private - - def authenticated? - user.present? && user.authenticate(password) - end - - def jwt_token - JwtToken.new(payload: { sub: user.id }) - end - - def user - @user ||= User.find_by(email: email) - end -end diff --git a/app/models/user.rb b/app/models/user.rb index 962ce70..16282c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,7 @@ class User < ApplicationRecord has_secure_password - validates :email, presence: true, uniqueness: true - validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } + validates :email, presence: true + validates :password, length: { minimum: 6 } + validates :email, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } end diff --git a/app/serializers/json_api_request_serializer.rb b/app/serializers/json_api_request_serializer.rb deleted file mode 100644 index b7cf968..0000000 --- a/app/serializers/json_api_request_serializer.rb +++ /dev/null @@ -1,21 +0,0 @@ -params = { - type: "token", - email: "me@timurv.ru", - password: "123456" -} - -attributes = params.keys - -request_class = ActiveModel::Name.new(nil, nil, "token_request") do - include ActiveModel::Serialization - - attributes(*attributes) -end - -request = request_class.new(params) - -serializer_class = Class.new(ActiveModel::Serializer) do - attributes(*attributes) -end - -json = ActiveModelSerializers::SerializableResource.new(request, serializer: serializer_class).to_json diff --git a/bin/quality b/bin/quality index 56d31dd..2abcac7 100755 --- a/bin/quality +++ b/bin/quality @@ -4,12 +4,5 @@ set -e bin/rubocop bin/brakeman --quiet --skip-libs --exit-on-warn - -# Using rake-task here since coffeelint.rb cmd doesn't exit with non-zero status -# when code contains errors -bin/rails coffeelint -# bin/scss-lint -bin/slim-lint app/views - bin/bundle-audit update bin/bundle-audit diff --git a/config/application.rb b/config/application.rb index fcadd4d..1c5d1c9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,4 +1,4 @@ -require_relative 'boot' +require_relative "boot" require "rails" # Pick the frameworks you want: @@ -33,7 +33,7 @@ class Application < Rails::Application # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. - # config.time_zone = 'Central Time (US & Canada)' + # config.time_zone = "Central Time (US & Canada)" # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")] diff --git a/config/boot.rb b/config/boot.rb index b9e460c..988a5dd 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,4 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) -require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap/setup' # Speed up boot time by caching expensive operations. +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/environment.rb b/config/environment.rb index 426333b..cac5315 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require_relative 'application' +require_relative "application" # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 1a8dab7..66cbe42 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,12 +14,12 @@ # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? + if Rails.root.join("tmp", "caching-dev.txt").exist? config.action_controller.perform_caching = true config.cache_store = :memory_store config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.to_i}" + "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -46,7 +46,6 @@ # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true - # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/environments/production.rb b/config/environments/production.rb index 42486c6..02034e9 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -20,7 +20,7 @@ # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? # Enable serving of images, stylesheets, and JavaScripts from an asset server. if ENV["ASSET_HOST"] @@ -29,8 +29,8 @@ end # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local @@ -43,7 +43,7 @@ config.log_level = :debug # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -85,8 +85,8 @@ config.log_formatter = ::Logger::Formatter.new # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + # require "syslog/logger" + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") if ENV["RAILS_LOG_TO_STDOUT"].present? logger = ActiveSupport::Logger.new(STDOUT) diff --git a/config/environments/test.rb b/config/environments/test.rb index 0a38fd3..17d1fb7 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -15,7 +15,7 @@ # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. diff --git a/config/initializers/apitome.rb b/config/initializers/apitome.rb index 0107477..72897c7 100644 --- a/config/initializers/apitome.rb +++ b/config/initializers/apitome.rb @@ -1,52 +1,55 @@ -# Apitome.setup do |config| -# # This determines where the Apitome routes will be mounted. Changing this to "/api/documentation" for instance would -# # allow you to browse to http://localhost:3000/api/documentation to see your api documentation. Set to nil and mount -# # it yourself if you need to. -# config.mount_at = "/docs" - -# # This defaults to Rails.root if left nil. If you're providing documentation for an engine using a dummy application -# # it can be useful to set this to your engines root.. E.g. Application::Engine.root -# config.root = nil - -# # This is where rspec_api_documentation outputs the JSON files. This is configurable within RAD, and so is -# # configurable here. -# config.doc_path = "doc/api/v1" - -# # The title of the documentation -- If your project has a name, you'll want to put it here. -# config.title = "#{Rails.application.class.parent_name.titleize} V1" - -# # The main layout view for all documentation pages. By default this is pretty basic, but you may want to use your own -# # application layout. -# config.layout = "apitome/application" - -# # We're using highlight.js (https://github.com/isagalaev/highlight.js) for code highlighting, and it comes with some -# # great themes. You can check http://softwaremaniacs.org/media/soft/highlight/test.html for themes, and enter the -# # theme as lowercase/underscore. -# config.code_theme = "default" - -# # This allows you to override the css manually. You typically want to require `apitome/application` within the -# # override, but if you want to override it entirely you can do so. -# config.css_override = nil - -# # This allows you to override the javascript manually. You typically want to require `apitome/application` within the -# # override, but if you want to override it entirely you can do so. -# config.js_override = nil - -# # You can provide a "README" style markdown file for the documentation, which is a useful place to include general -# # information. This path is relative to your doc_path configuration. -# config.readme = "../../../README.md" - -# # Apitome can render the documentation into a single page that uses scrollspy, or it can render the documentation on -# # individual pages on demand. This allows you to specify which one you want, as a single page may impact performance. -# config.single_page = true - -# # Restrict access to documentation -# # -# if ENV["APITOME_USER"] && ENV["APITOME_PASSWORD"] -# Apitome::DocsController.http_basic_authenticate_with( -# name: ENV["APITOME_USER"], -# password: ENV["APITOME_PASSWORD"], -# only: [:index] -# ) -# end -# end if defined? Apitome +if defined? Apitome + Apitome.setup do |config| + # This determines where the Apitome routes will be mounted. Changing this to "/api/documentation" for instance would + # allow you to browse to http://localhost:3000/api/documentation to see your api documentation. Set to nil and mount + # it yourself if you need to. + config.mount_at = "/docs" + + # This defaults to Rails.root if left nil. If you're providing documentation for an engine using a dummy application + # it can be useful to set this to your engines root.. E.g. Application::Engine.root + config.root = nil + + # This is where rspec_api_documentation outputs the JSON files. This is configurable within RAD, and so is + # configurable here. + config.doc_path = "doc/api/v1" + + # The title of the documentation -- If your project has a name, you'll want to put it here. + config.title = "#{Rails.application.class.parent_name.titleize} V1" + + # The main layout view for all documentation pages. By default this is pretty basic, but you may want to use your + # own application layout. + config.layout = "apitome/application" + + # We're using highlight.js (https://github.com/isagalaev/highlight.js) for code highlighting, and it comes with some + # great themes. You can check http://softwaremaniacs.org/media/soft/highlight/test.html for themes, and enter the + # theme as lowercase/underscore. + config.code_theme = "default" + + # This allows you to override the css manually. You typically want to require `apitome/application` within the + # override, but if you want to override it entirely you can do so. + config.css_override = nil + + # This allows you to override the javascript manually. You typically want to require `apitome/application` within + # the override, but if you want to override it entirely you can do so. + config.js_override = nil + + # You can provide a "README" style markdown file for the documentation, which is a useful place to include general + # information. This path is relative to your doc_path configuration. + config.readme = "../../../README.md" + + # Apitome can render the documentation into a single page that uses scrollspy, or it can render the documentation on + # individual pages on demand. This allows you to specify which one you want, as a single page may impact + # performance. + config.single_page = true + + # Restrict access to documentation + # + if ENV["APITOME_USER"] && ENV["APITOME_PASSWORD"] + Apitome::DocsController.http_basic_authenticate_with( + name: ENV["APITOME_USER"], + password: ENV["APITOME_PASSWORD"], + only: [:index] + ) + end + end +end diff --git a/config/initializers/bullet.rb b/config/initializers/bullet.rb index c2410a5..da6d204 100644 --- a/config/initializers/bullet.rb +++ b/config/initializers/bullet.rb @@ -1,7 +1,9 @@ -Rails.application.config.after_initialize do - Bullet.enable = true - Bullet.bullet_logger = true - Bullet.console = true - Bullet.rails_logger = true - Bullet.add_footer = true -end if defined?(Bullet) +if defined?(Bullet) + Rails.application.config.after_initialize do + Bullet.enable = true + Bullet.bullet_logger = true + Bullet.console = true + Bullet.rails_logger = true + Bullet.add_footer = true + end +end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index f234a07..f7ffdd7 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -5,12 +5,14 @@ # Read more: https://github.com/cyu/rack-cors -Rails.application.config.middleware.insert_before 0, Rack::Cors do - allow do - origins(*ENV.fetch("CORS_ORIGINS").split(",")) +if ENV["CORS_ORIGINS"].present? + Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins(*ENV.fetch("CORS_ORIGINS").split(",")) - resource '*', - headers: :any, - methods: [:get, :post, :put, :patch, :delete, :options, :head] + resource "*", + headers: :any, + methods: %i[get post put patch delete options head] + end end -end if ENV["CORS_ORIGINS"].present? +end diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 69c06b2..3f307d1 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -16,8 +16,8 @@ config.http_status_for_error_object = 500 # You can customize which checks happen on a standard health check - config.standard_checks = %w(database migrations site) + config.standard_checks = %w[database migrations site] # You can set what tests are run with the 'full' or 'all' parameter - config.full_checks = %w(database migrations site cache) + config.full_checks = %w[database migrations site cache] end diff --git a/config/routes.rb b/config/routes.rb index b1acc6a..55263d1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,8 @@ Rails.application.routes.draw do namespace :v1, defaults: { format: "jsonapi" } do resources :tokens, only: :create - resources :users, only: %i(index show create) - resource :profile, only: %i(show update destroy) + resources :users, only: %i[index show] + resources :sign_ups, only: :create + resource :profile, only: %i[show update destroy] end end diff --git a/config/rubocop.yml b/config/rubocop.yml deleted file mode 100644 index cc6bd9e..0000000 --- a/config/rubocop.yml +++ /dev/null @@ -1,58 +0,0 @@ -Rails: - Enabled: true - -AllCops: - DisplayCopNames: true - Exclude: - - bin/**/* - - db/**/* - - lib/templates/**/* - - vendor/**/* - -Style/Documentation: - Description: 'Document classes and non-namespace modules.' - Enabled: false - -Style/MethodCalledOnDoEndBlock: - Enabled: true - -Style/CollectionMethods: - Enabled: true - -Style/SymbolArray: - Description: 'Use %i or %I for arrays of symbols.' - Enabled: true - -Style/StringLiterals: - EnforcedStyle: double_quotes - -Metrics/LineLength: - Description: 'Limit lines to 120 characters.' - Max: 120 - -Layout/EndAlignment: - EnforcedStyleAlignWith: variable - -Layout/AlignParameters: - EnforcedStyle: with_fixed_indentation - SupportedStyles: - - with_first_parameter - - with_fixed_indentation - -Bundler/OrderedGems: - Enabled: false - -Lint/ScriptPermission: - Enabled: false - -Rails/FilePath: - Enabled: false - -Style/FrozenStringLiteralComment: - Enabled: false - -Style/MultilineIfModifier: - Enabled: false - -Style/PercentLiteralDelimiters: - Enabled: false diff --git a/doc/api/index.html b/doc/api/index.html new file mode 100644 index 0000000..a013cfb --- /dev/null +++ b/doc/api/index.html @@ -0,0 +1,113 @@ + + + + API Documentation + + + + +
+

API Documentation

+ +
+ + diff --git a/doc/api/v1/errors/request_to_unexisting_page.json b/doc/api/v1/errors/request_to_unexisting_page.json deleted file mode 100644 index 5a33232..0000000 --- a/doc/api/v1/errors/request_to_unexisting_page.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "resource": "Errors", - "http_method": "GET", - "route": "/not-found", - "description": "Request to unexisting page", - "explanation": null, - "parameters": [ - - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "GET", - "request_path": "/not-found", - "request_body": null, - "request_headers": { - "Accept": "application/json" - }, - "request_query_parameters": { - }, - "request_content_type": null, - "response_status": 404, - "response_status_text": "Not Found", - "response_body": "{\"error\":{\"id\":\"f5576af0-303e-4ca4-b421-ba741116d4a4\",\"status\":404,\"error\":\"Not Found\",\"validations\":null}}", - "response_headers": { - "Content-Type": "application/json; charset=utf-8" - }, - "response_content_type": "application/json; charset=utf-8", - "curl": "curl \"http://localhost:5000/not-found\" -X GET \\\n\t-H \"Accept: application/json\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/index.json b/doc/api/v1/index.json deleted file mode 100644 index 015fd87..0000000 --- a/doc/api/v1/index.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "resources": [ - { - "name": "Errors", - "examples": [ - { - "description": "Request to unexisting page", - "link": "errors/request_to_unexisting_page.json", - "groups": "all" - } - ] - }, - { - "name": "Sessions", - "examples": [ - { - "description": "Sign in with valid password", - "link": "sessions/sign_in_with_valid_password.json", - "groups": "all" - }, - { - "description": "Sign in with invalid password", - "link": "sessions/sign_in_with_invalid_password.json", - "groups": "all" - } - ] - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/index.md b/doc/api/v1/index.md new file mode 100644 index 0000000..0441ac6 --- /dev/null +++ b/doc/api/v1/index.md @@ -0,0 +1,22 @@ +# API Documentation + + +## Profiles + +* [Retrive Profile](profiles/retrive_profile.md) +* [Update Profile](profiles/update_profile.md) +* [Delete Profile](profiles/delete_profile.md) + +## SignUp + +* [Create User](signup/create_user.md) + +## Tokens + +* [Create Token](tokens/create_token.md) + +## Users + +* [List Users](users/list_users.md) +* [Retrive User](users/retrive_user.md) + diff --git a/doc/api/v1/profiles/delete_profile.md b/doc/api/v1/profiles/delete_profile.md new file mode 100644 index 0000000..3c53110 --- /dev/null +++ b/doc/api/v1/profiles/delete_profile.md @@ -0,0 +1,50 @@ +# Profiles API + +## Delete Profile + +### DELETE /v1/profile +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6N30.vDDbZywBIY6HhAyefIxiUtkyryfH4Cy3oMgQVe4UGxc
+ +#### Route + +
DELETE /v1/profile
+ +#### Body + +
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}' -X DELETE \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6N30.vDDbZywBIY6HhAyefIxiUtkyryfH4Cy3oMgQVe4UGxc"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "7",
+    "type": "users",
+    "attributes": {
+      "email": "user6@example.com",
+      "full-name": "Marshall Pagac"
+    }
+  }
+}
diff --git a/doc/api/v1/profiles/retrive_profile.md b/doc/api/v1/profiles/retrive_profile.md new file mode 100644 index 0000000..c4722db --- /dev/null +++ b/doc/api/v1/profiles/retrive_profile.md @@ -0,0 +1,50 @@ +# Profiles API + +## Retrive Profile + +### GET /v1/profile +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MX0.orRPRi_fy9T_gDEDSafkuBztXx-bDCCCT_JJnW7wMdE
+ +#### Route + +
GET /v1/profile
+ +#### Query Parameters + +
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/profile" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MX0.orRPRi_fy9T_gDEDSafkuBztXx-bDCCCT_JJnW7wMdE"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "1",
+    "type": "users",
+    "attributes": {
+      "email": "user1@example.com",
+      "full-name": "Mrs. Felipe Bayer"
+    }
+  }
+}
diff --git a/doc/api/v1/profiles/update_profile.md b/doc/api/v1/profiles/update_profile.md new file mode 100644 index 0000000..f6624e9 --- /dev/null +++ b/doc/api/v1/profiles/update_profile.md @@ -0,0 +1,59 @@ +# Profiles API + +## Update Profile + +### PATCH /v1/profile + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| full_name | full name | false | | +| email | email | false | | +| password | password | false | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6Mn0.9SU-CBPxQt3tmRC0-L67hufyMvwHUBOuXv9lp5do65Y
+ +#### Route + +
PATCH /v1/profile
+ +#### Body + +
{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}' -X PATCH \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6Mn0.9SU-CBPxQt3tmRC0-L67hufyMvwHUBOuXv9lp5do65Y"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "2",
+    "type": "users",
+    "attributes": {
+      "email": "user_updated@example.com",
+      "full-name": "Deon Ryan"
+    }
+  }
+}
diff --git a/doc/api/v1/sessions/sign_in_with_invalid_password.json b/doc/api/v1/sessions/sign_in_with_invalid_password.json deleted file mode 100644 index f079d29..0000000 --- a/doc/api/v1/sessions/sign_in_with_invalid_password.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "resource": "Sessions", - "http_method": "POST", - "route": "/v1/users/sign_in", - "description": "Sign in with invalid password", - "explanation": null, - "parameters": [ - { - "required": true, - "name": "email", - "description": "Email" - }, - { - "required": true, - "name": "password", - "description": "Password" - } - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "POST", - "request_path": "/v1/users/sign_in", - "request_body": "email=user2%40example.com&password=", - "request_headers": { - "Accept": "application/json" - }, - "request_query_parameters": { - }, - "request_content_type": "application/x-www-form-urlencoded", - "response_status": 401, - "response_status_text": "Unauthorized", - "response_body": "{\"error\":{\"id\":\"19acd4f1-2fa1-4c6d-9d95-6cf8d7e8aaa7\",\"status\":401,\"error\":\"Invalid email or password.\",\"validations\":null}}", - "response_headers": { - "Content-Type": "application/json; charset=utf-8" - }, - "response_content_type": "application/json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/users/sign_in\" -d 'email=user2%40example.com&password=' -X POST \\\n\t-H \"Accept: application/json\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/sessions/sign_in_with_valid_password.json b/doc/api/v1/sessions/sign_in_with_valid_password.json deleted file mode 100644 index f1672a8..0000000 --- a/doc/api/v1/sessions/sign_in_with_valid_password.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "resource": "Sessions", - "http_method": "POST", - "route": "/v1/users/sign_in", - "description": "Sign in with valid password", - "explanation": null, - "parameters": [ - { - "required": true, - "name": "email", - "description": "Email" - }, - { - "required": true, - "name": "password", - "description": "Password" - } - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "POST", - "request_path": "/v1/users/sign_in", - "request_body": "email=user1%40example.com&password=123456", - "request_headers": { - "Accept": "application/json" - }, - "request_query_parameters": { - }, - "request_content_type": "application/x-www-form-urlencoded", - "response_status": 201, - "response_status_text": "Created", - "response_body": "{\"user\":{\"id\":1,\"authentication_token\":\"ac2zWyKFhnoaWEggD7tn\",\"email\":\"user1@example.com\"}}", - "response_headers": { - "Content-Type": "application/json; charset=utf-8" - }, - "response_content_type": "application/json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/users/sign_in\" -d 'email=user1%40example.com&password=123456' -X POST \\\n\t-H \"Accept: application/json\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/signup/create_user.md b/doc/api/v1/signup/create_user.md new file mode 100644 index 0000000..d6a179b --- /dev/null +++ b/doc/api/v1/signup/create_user.md @@ -0,0 +1,57 @@ +# SignUp API + +## Create User + +### POST /v1/sign_ups + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| full_name | full name | false | | +| email | email | true | | +| password | password | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+ +#### Route + +
POST /v1/sign_ups
+ +#### Body + +
{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/sign_ups" -d '{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}' -X POST \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
201 Created
+ +#### Body + +
{
+  "data": {
+    "id": "8",
+    "type": "users",
+    "attributes": {
+      "email": "user@example.com",
+      "full-name": null
+    }
+  }
+}
diff --git a/doc/api/v1/tokens/create_token.md b/doc/api/v1/tokens/create_token.md new file mode 100644 index 0000000..6956a78 --- /dev/null +++ b/doc/api/v1/tokens/create_token.md @@ -0,0 +1,55 @@ +# Tokens API + +## Create Token + +### POST /v1/tokens + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| email | email | true | | +| password | password | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+ +#### Route + +
POST /v1/tokens
+ +#### Body + +
{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/tokens" -d '{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}' -X POST \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
201 Created
+ +#### Body + +
{
+  "data": {
+    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTB9.pCGA_K_9r5mb2kaPpvSbhU5tgvox62zIaSa76XvDgB0",
+    "type": "jwt-tokens",
+    "attributes": {
+      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTB9.pCGA_K_9r5mb2kaPpvSbhU5tgvox62zIaSa76XvDgB0"
+    }
+  }
+}
diff --git a/doc/api/v1/users/list_users.md b/doc/api/v1/users/list_users.md new file mode 100644 index 0000000..9bbb60a --- /dev/null +++ b/doc/api/v1/users/list_users.md @@ -0,0 +1,76 @@ +# Users API + +## List Users + +### GET /v1/users +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTV9.l3UZHbKGxQUugSTqjDV3finM1WFP33yRqwiLDfuW4aM
+ +#### Route + +
GET /v1/users
+ +#### Query Parameters + +
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/users" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTV9.l3UZHbKGxQUugSTqjDV3finM1WFP33yRqwiLDfuW4aM"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": [
+    {
+      "id": "12",
+      "type": "users",
+      "attributes": {
+        "email": "user7@example.com",
+        "full-name": "Lesia Quigley"
+      }
+    },
+    {
+      "id": "13",
+      "type": "users",
+      "attributes": {
+        "email": "user8@example.com",
+        "full-name": "Maricruz Carroll"
+      }
+    },
+    {
+      "id": "14",
+      "type": "users",
+      "attributes": {
+        "email": "user9@example.com",
+        "full-name": "Margot Kuhlman"
+      }
+    },
+    {
+      "id": "15",
+      "type": "users",
+      "attributes": {
+        "email": "user10@example.com",
+        "full-name": "Roman Tillman III"
+      }
+    }
+  ]
+}
diff --git a/doc/api/v1/users/retrive_user.md b/doc/api/v1/users/retrive_user.md new file mode 100644 index 0000000..06ad190 --- /dev/null +++ b/doc/api/v1/users/retrive_user.md @@ -0,0 +1,57 @@ +# Users API + +## Retrive User + +### GET /v1/users/:id + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| id | user id | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTd9.3xNGz7ZI3CAQH_DDtXGQNAhjQtjyNWt2lWeFUqBSbZA
+ +#### Route + +
GET /v1/users/16
+ +#### Query Parameters + +
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/users/16" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTd9.3xNGz7ZI3CAQH_DDtXGQNAhjQtjyNWt2lWeFUqBSbZA"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "16",
+    "type": "users",
+    "attributes": {
+      "email": "user11@example.com",
+      "full-name": "Dusty Lang"
+    }
+  }
+}
diff --git a/lib/tasks/docs.rake b/lib/tasks/docs.rake new file mode 100644 index 0000000..2f4695a --- /dev/null +++ b/lib/tasks/docs.rake @@ -0,0 +1,16 @@ +require "rspec/core/rake_task" + +Rake::Task["docs:generate"].clear +Rake::Task["docs:generate:ordered"].clear + +desc "Generate API request documentation from API specs" +RSpec::Core::RakeTask.new("docs:generate") do |t| + t.pattern = "spec/api/**/*_spec.rb" + t.rspec_opts = ["--format RspecApiDocumentation::ApiFormatter"] +end + +desc "Generate API request documentation from API specs (ordered)" +RSpec::Core::RakeTask.new("docs:generate:ordered") do |t| + t.pattern = "spec/api/**/*_spec.rb" + t.rspec_opts = ["--format RspecApiDocumentation::ApiFormatter", "--order defined"] +end diff --git a/spec/api/errors_spec.rb b/spec/api/errors_spec.rb index 8697cfa..b8164d4 100644 --- a/spec/api/errors_spec.rb +++ b/spec/api/errors_spec.rb @@ -1,13 +1,12 @@ require "rails_helper" -require "rspec_api_documentation/dsl" resource "Errors" do include_context "with JSON API Headers" - get "/not-found" do - example_request "Request to unexisting page" do - expect(response_status).to eq(404) - expect(response_body).to match_response_schema("error") - end - end + # get "/not-found" do + # example_request "Request to unexisting page" do + # expect(response_status).to eq(404) + # expect(response_body).to match_response_schema("error") + # end + # end end diff --git a/spec/api/v1/profiles_spec.rb b/spec/api/v1/profiles_spec.rb index 6ce9540..91fcb51 100644 --- a/spec/api/v1/profiles_spec.rb +++ b/spec/api/v1/profiles_spec.rb @@ -29,6 +29,7 @@ class ProfileRequestSerializer < ApplicationSerializer let(:full_name) { "Example User Updated" } let(:email) { "user_updated@example.com" } + let(:password) { "new_password" } let(:request_class) { "profile_request" } example_request "Update Profile" do @@ -46,10 +47,10 @@ class ProfileRequestSerializer < ApplicationSerializer end end - context "with blank password" do - let(:password) { "" } + context "with short password" do + let(:password) { "short" } - example "Update Profile with invalid password", document: false do + example "Update Profile with invalid email", document: false do do_request expect(response_status).to eq(422) expect(response_body).to match_response_schema("error") diff --git a/spec/api/v1/sign_ups_spec.rb b/spec/api/v1/sign_ups_spec.rb new file mode 100644 index 0000000..1bf626b --- /dev/null +++ b/spec/api/v1/sign_ups_spec.rb @@ -0,0 +1,62 @@ +require "rails_helper" + +class SignUpRequest < ActiveModelSerializers::Model + attributes :id, :full_name, :email, :password +end + +class SignUpRequestSerializer < ApplicationSerializer + attributes :full_name, :email, :password +end + +resource "SignUp" do + include_context "with JSON API Headers" + include_context "with JSON API post body from request class" + + post "/v1/sign_ups" do + parameter :full_name, "full name" + parameter :email, "email", required: true + parameter :password, "password", required: true + + let(:full_name) { "Example User" } + let(:email) { "user@example.com" } + let(:password) { "123456" } + let(:request_class) { "user_request" } + + example_request "Create User" do + expect(response_status).to eq(201) + expect(response_body).to match_response_schema("user") + end + + context "with invalid email" do + let(:email) { "invalid" } + + example "Create User with invalid email", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + + context "with blank password" do + let(:password) { "" } + + example "Create User with invalid password", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + + context "when user already exists" do + before do + create :user, email: email + end + + example "Create User with existing email", document: false do + do_request + expect(response_status).to eq(422) + expect(response_body).to match_response_schema("error") + end + end + end +end diff --git a/spec/api/v1/users_spec.rb b/spec/api/v1/users_spec.rb index 9b22360..9519c7c 100644 --- a/spec/api/v1/users_spec.rb +++ b/spec/api/v1/users_spec.rb @@ -10,11 +10,10 @@ class UserRequestSerializer < ApplicationSerializer resource "Users" do include_context "with JSON API Headers" + include_context "with JSON API Authorization header" include_context "with JSON API post body from request class" get "/v1/users" do - include_context "with JSON API Authorization header" - let(:request_class) { "user_request" } before do @@ -28,8 +27,6 @@ class UserRequestSerializer < ApplicationSerializer end get "/v1/users/:id" do - include_context "with JSON API Authorization header" - parameter :id, "user id", required: true let(:user) { create :user } @@ -51,52 +48,4 @@ class UserRequestSerializer < ApplicationSerializer end end end - - post "/v1/users" do - parameter :full_name, "full name" - parameter :email, "email", required: true - parameter :password, "password", required: true - - let(:full_name) { "Example User" } - let(:email) { "user@example.com" } - let(:password) { "123456" } - let(:request_class) { "user_request" } - - example_request "Create User" do - expect(response_status).to eq(201) - expect(response_body).to match_response_schema("user") - end - - context "with invalid email" do - let(:email) { "invalid" } - - example "Create User with invalid email", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") - end - end - - context "with blank password" do - let(:password) { "" } - - example "Create User with invalid password", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") - end - end - - context "when user already exists" do - before do - create :user, email: email - end - - example "Create User with existing email", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") - end - end - end end diff --git a/spec/interactors/create_jwt_spec.rb b/spec/interactors/create_jwt_spec.rb index 6e214c2..7a7e125 100644 --- a/spec/interactors/create_jwt_spec.rb +++ b/spec/interactors/create_jwt_spec.rb @@ -19,7 +19,7 @@ it "sets token in context" do interactor.run - expect(context.jwt_token).to be + expect(context.jwt_token).to be_present end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index faed999..53ba992 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,7 +1,7 @@ ENV["RAILS_ENV"] ||= "test" require "spec_helper" -require File.expand_path("../../config/environment", __FILE__) +require File.expand_path("../config/environment", __dir__) require "rspec/rails" require "shoulda/matchers" From 1c2cfdb9c41f9e876baed93b11dc931a91ef48c2 Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Fri, 7 Sep 2018 23:17:28 +0300 Subject: [PATCH 09/26] Use raddocs gem for api documentation --- Gemfile | 2 +- Gemfile.lock | 23 ++++- config/initializers/apitome.rb | 55 ----------- config/initializers/raddocs.rb | 3 + config/routes.rb | 2 + doc/api.md | 4 + doc/api/index.html | 113 ----------------------- doc/api/v1/index.json | 77 +++++++++++++++ doc/api/v1/index.md | 22 ----- doc/api/v1/profiles/delete_profile.json | 37 ++++++++ doc/api/v1/profiles/delete_profile.md | 50 ---------- doc/api/v1/profiles/retrive_profile.json | 38 ++++++++ doc/api/v1/profiles/retrive_profile.md | 50 ---------- doc/api/v1/profiles/update_profile.json | 48 ++++++++++ doc/api/v1/profiles/update_profile.md | 59 ------------ doc/api/v1/signup/create_user.json | 49 ++++++++++ doc/api/v1/signup/create_user.md | 57 ------------ doc/api/v1/tokens/create_token.json | 45 +++++++++ doc/api/v1/tokens/create_token.md | 55 ----------- doc/api/v1/users/list_users.json | 38 ++++++++ doc/api/v1/users/list_users.md | 76 --------------- doc/api/v1/users/retrive_user.json | 42 +++++++++ doc/api/v1/users/retrive_user.md | 57 ------------ spec/support/database_cleaner.rb | 2 + spec/support/email.rb | 2 + spec/support/rspec_api_documentation.rb | 2 +- 26 files changed, 407 insertions(+), 601 deletions(-) delete mode 100644 config/initializers/apitome.rb create mode 100644 config/initializers/raddocs.rb create mode 100644 doc/api.md delete mode 100644 doc/api/index.html create mode 100644 doc/api/v1/index.json delete mode 100644 doc/api/v1/index.md create mode 100644 doc/api/v1/profiles/delete_profile.json delete mode 100644 doc/api/v1/profiles/delete_profile.md create mode 100644 doc/api/v1/profiles/retrive_profile.json delete mode 100644 doc/api/v1/profiles/retrive_profile.md create mode 100644 doc/api/v1/profiles/update_profile.json delete mode 100644 doc/api/v1/profiles/update_profile.md create mode 100644 doc/api/v1/signup/create_user.json delete mode 100644 doc/api/v1/signup/create_user.md create mode 100644 doc/api/v1/tokens/create_token.json delete mode 100644 doc/api/v1/tokens/create_token.md create mode 100644 doc/api/v1/users/list_users.json delete mode 100644 doc/api/v1/users/list_users.md create mode 100644 doc/api/v1/users/retrive_user.json delete mode 100644 doc/api/v1/users/retrive_user.md diff --git a/Gemfile b/Gemfile index e53454f..ba9cdea 100644 --- a/Gemfile +++ b/Gemfile @@ -53,8 +53,8 @@ group :test do end group :development, :test, :staging do - gem "apitome", require: false gem "factory_bot_rails" gem "faker" + gem "raddocs" gem "rspec_api_documentation" end diff --git a/Gemfile.lock b/Gemfile.lock index 80af8ec..99da1e5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,9 +49,6 @@ GEM tzinfo (~> 1.1) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) - apitome (0.2.0) - kramdown - railties arel (9.0.0) ast (2.4.0) bcrypt (3.1.12) @@ -98,6 +95,9 @@ GEM thor (~> 0.19.1) globalid (0.4.1) activesupport (>= 4.2.0) + haml (5.0.4) + temple (>= 0.8.0) + tilt hashdiff (0.3.7) health_check (3.0.0) railties (>= 5.0) @@ -131,7 +131,6 @@ GEM bcrypt (~> 3.1) jwt (~> 1.5) rails (>= 4.2) - kramdown (1.17.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.6.0) @@ -155,6 +154,7 @@ GEM msgpack (1.2.4) multi_json (1.13.1) mustache (1.0.5) + mustermann (1.0.3) nio4r (2.3.1) nokogiri (1.8.4) mini_portile2 (~> 2.3.0) @@ -166,8 +166,14 @@ GEM public_suffix (3.0.3) puma (3.12.0) rack (2.0.5) + rack-protection (2.0.3) + rack rack-test (1.1.0) rack (>= 1.0, < 3) + raddocs (2.2.0) + haml (>= 4.0) + json + sinatra (~> 2.0) rails (5.2.1) actioncable (= 5.2.1) actionmailer (= 5.2.1) @@ -248,6 +254,11 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) + sinatra (2.0.3) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.3) + tilt (~> 2.0) spring (2.0.2) activesupport (>= 4.2) spring-commands-rspec (1.0.4) @@ -259,8 +270,10 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + temple (0.8.0) thor (0.19.4) thread_safe (0.3.6) + tilt (2.0.8) tzinfo (1.2.5) thread_safe (~> 0.1) unicode-display_width (1.4.0) @@ -278,7 +291,6 @@ PLATFORMS DEPENDENCIES active_model_serializers - apitome bcrypt bootsnap brakeman @@ -302,6 +314,7 @@ DEPENDENCIES listen (>= 3.0.5, < 3.2) pg puma + raddocs rails (= 5.2.1) responders rollbar diff --git a/config/initializers/apitome.rb b/config/initializers/apitome.rb deleted file mode 100644 index 72897c7..0000000 --- a/config/initializers/apitome.rb +++ /dev/null @@ -1,55 +0,0 @@ -if defined? Apitome - Apitome.setup do |config| - # This determines where the Apitome routes will be mounted. Changing this to "/api/documentation" for instance would - # allow you to browse to http://localhost:3000/api/documentation to see your api documentation. Set to nil and mount - # it yourself if you need to. - config.mount_at = "/docs" - - # This defaults to Rails.root if left nil. If you're providing documentation for an engine using a dummy application - # it can be useful to set this to your engines root.. E.g. Application::Engine.root - config.root = nil - - # This is where rspec_api_documentation outputs the JSON files. This is configurable within RAD, and so is - # configurable here. - config.doc_path = "doc/api/v1" - - # The title of the documentation -- If your project has a name, you'll want to put it here. - config.title = "#{Rails.application.class.parent_name.titleize} V1" - - # The main layout view for all documentation pages. By default this is pretty basic, but you may want to use your - # own application layout. - config.layout = "apitome/application" - - # We're using highlight.js (https://github.com/isagalaev/highlight.js) for code highlighting, and it comes with some - # great themes. You can check http://softwaremaniacs.org/media/soft/highlight/test.html for themes, and enter the - # theme as lowercase/underscore. - config.code_theme = "default" - - # This allows you to override the css manually. You typically want to require `apitome/application` within the - # override, but if you want to override it entirely you can do so. - config.css_override = nil - - # This allows you to override the javascript manually. You typically want to require `apitome/application` within - # the override, but if you want to override it entirely you can do so. - config.js_override = nil - - # You can provide a "README" style markdown file for the documentation, which is a useful place to include general - # information. This path is relative to your doc_path configuration. - config.readme = "../../../README.md" - - # Apitome can render the documentation into a single page that uses scrollspy, or it can render the documentation on - # individual pages on demand. This allows you to specify which one you want, as a single page may impact - # performance. - config.single_page = true - - # Restrict access to documentation - # - if ENV["APITOME_USER"] && ENV["APITOME_PASSWORD"] - Apitome::DocsController.http_basic_authenticate_with( - name: ENV["APITOME_USER"], - password: ENV["APITOME_PASSWORD"], - only: [:index] - ) - end - end -end diff --git a/config/initializers/raddocs.rb b/config/initializers/raddocs.rb new file mode 100644 index 0000000..20ddb9d --- /dev/null +++ b/config/initializers/raddocs.rb @@ -0,0 +1,3 @@ +Raddocs.configure do |config| + config.docs_dir = "doc/api/v1" +end diff --git a/config/routes.rb b/config/routes.rb index 55263d1..ed8c2a3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + mount Raddocs::App => "/api/docs" + namespace :v1, defaults: { format: "jsonapi" } do resources :tokens, only: :create resources :users, only: %i[index show] diff --git a/doc/api.md b/doc/api.md new file mode 100644 index 0000000..26fa90d --- /dev/null +++ b/doc/api.md @@ -0,0 +1,4 @@ +Apitome Documentation +===================== + +This file was automatically generated, and can be found at `doc/api.md`. diff --git a/doc/api/index.html b/doc/api/index.html deleted file mode 100644 index a013cfb..0000000 --- a/doc/api/index.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - API Documentation - - - - -
-

API Documentation

- -
- - diff --git a/doc/api/v1/index.json b/doc/api/v1/index.json new file mode 100644 index 0000000..99fb42a --- /dev/null +++ b/doc/api/v1/index.json @@ -0,0 +1,77 @@ +{ + "resources": [ + { + "name": "Profiles", + "explanation": null, + "examples": [ + { + "description": "Retrive Profile", + "link": "profiles/retrive_profile.json", + "groups": "all", + "route": "/v1/profile", + "method": "get" + }, + { + "description": "Update Profile", + "link": "profiles/update_profile.json", + "groups": "all", + "route": "/v1/profile", + "method": "patch" + }, + { + "description": "Delete Profile", + "link": "profiles/delete_profile.json", + "groups": "all", + "route": "/v1/profile", + "method": "delete" + } + ] + }, + { + "name": "SignUp", + "explanation": null, + "examples": [ + { + "description": "Create User", + "link": "signup/create_user.json", + "groups": "all", + "route": "/v1/sign_ups", + "method": "post" + } + ] + }, + { + "name": "Tokens", + "explanation": null, + "examples": [ + { + "description": "Create Token", + "link": "tokens/create_token.json", + "groups": "all", + "route": "/v1/tokens", + "method": "post" + } + ] + }, + { + "name": "Users", + "explanation": null, + "examples": [ + { + "description": "List Users", + "link": "users/list_users.json", + "groups": "all", + "route": "/v1/users", + "method": "get" + }, + { + "description": "Retrive User", + "link": "users/retrive_user.json", + "groups": "all", + "route": "/v1/users/:id", + "method": "get" + } + ] + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/index.md b/doc/api/v1/index.md deleted file mode 100644 index 0441ac6..0000000 --- a/doc/api/v1/index.md +++ /dev/null @@ -1,22 +0,0 @@ -# API Documentation - - -## Profiles - -* [Retrive Profile](profiles/retrive_profile.md) -* [Update Profile](profiles/update_profile.md) -* [Delete Profile](profiles/delete_profile.md) - -## SignUp - -* [Create User](signup/create_user.md) - -## Tokens - -* [Create Token](tokens/create_token.md) - -## Users - -* [List Users](users/list_users.md) -* [Retrive User](users/retrive_user.md) - diff --git a/doc/api/v1/profiles/delete_profile.json b/doc/api/v1/profiles/delete_profile.json new file mode 100644 index 0000000..6ec8625 --- /dev/null +++ b/doc/api/v1/profiles/delete_profile.json @@ -0,0 +1,37 @@ +{ + "resource": "Profiles", + "resource_explanation": null, + "http_method": "DELETE", + "route": "/v1/profile", + "description": "Delete Profile", + "explanation": null, + "parameters": [ + + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "DELETE", + "request_path": "/v1/profile", + "request_body": "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}", + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json", + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjgsInN1YiI6MjZ9.iD68UgkIB44hIfsjj-txcKBxFtdfCRxTkJNeTq_6Pyw" + }, + "request_query_parameters": { + }, + "request_content_type": "application/vnd.api+json", + "response_status": 200, + "response_status_text": "OK", + "response_body": "{\n \"data\": {\n \"id\": \"26\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user6@example.com\",\n \"full-name\": \"Shane Ruecker\"\n }\n }\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}' -X DELETE \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjgsInN1YiI6MjZ9.iD68UgkIB44hIfsjj-txcKBxFtdfCRxTkJNeTq_6Pyw\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/profiles/delete_profile.md b/doc/api/v1/profiles/delete_profile.md deleted file mode 100644 index 3c53110..0000000 --- a/doc/api/v1/profiles/delete_profile.md +++ /dev/null @@ -1,50 +0,0 @@ -# Profiles API - -## Delete Profile - -### DELETE /v1/profile -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6N30.vDDbZywBIY6HhAyefIxiUtkyryfH4Cy3oMgQVe4UGxc
- -#### Route - -
DELETE /v1/profile
- -#### Body - -
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}
- -#### cURL - -
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}' -X DELETE \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6N30.vDDbZywBIY6HhAyefIxiUtkyryfH4Cy3oMgQVe4UGxc"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
200 OK
- -#### Body - -
{
-  "data": {
-    "id": "7",
-    "type": "users",
-    "attributes": {
-      "email": "user6@example.com",
-      "full-name": "Marshall Pagac"
-    }
-  }
-}
diff --git a/doc/api/v1/profiles/retrive_profile.json b/doc/api/v1/profiles/retrive_profile.json new file mode 100644 index 0000000..bbb4d8c --- /dev/null +++ b/doc/api/v1/profiles/retrive_profile.json @@ -0,0 +1,38 @@ +{ + "resource": "Profiles", + "resource_explanation": null, + "http_method": "GET", + "route": "/v1/profile", + "description": "Retrive Profile", + "explanation": null, + "parameters": [ + + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "GET", + "request_path": "/v1/profile", + "request_body": null, + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json", + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjB9.cKmDT-WB6dMnYdDs1QWFYvbExfQ-1dJ9RcmQJqfSkgQ" + }, + "request_query_parameters": { + "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null + }, + "request_content_type": "application/vnd.api+json", + "response_status": 200, + "response_status_text": "OK", + "response_body": "{\n \"data\": {\n \"id\": \"20\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user1@example.com\",\n \"full-name\": \"Drew Murray\"\n }\n }\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl -g \"http://localhost:5000/v1/profile\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjB9.cKmDT-WB6dMnYdDs1QWFYvbExfQ-1dJ9RcmQJqfSkgQ\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/profiles/retrive_profile.md b/doc/api/v1/profiles/retrive_profile.md deleted file mode 100644 index c4722db..0000000 --- a/doc/api/v1/profiles/retrive_profile.md +++ /dev/null @@ -1,50 +0,0 @@ -# Profiles API - -## Retrive Profile - -### GET /v1/profile -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MX0.orRPRi_fy9T_gDEDSafkuBztXx-bDCCCT_JJnW7wMdE
- -#### Route - -
GET /v1/profile
- -#### Query Parameters - -
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
- -#### cURL - -
curl -g "http://localhost:5000/v1/profile" -X GET \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MX0.orRPRi_fy9T_gDEDSafkuBztXx-bDCCCT_JJnW7wMdE"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
200 OK
- -#### Body - -
{
-  "data": {
-    "id": "1",
-    "type": "users",
-    "attributes": {
-      "email": "user1@example.com",
-      "full-name": "Mrs. Felipe Bayer"
-    }
-  }
-}
diff --git a/doc/api/v1/profiles/update_profile.json b/doc/api/v1/profiles/update_profile.json new file mode 100644 index 0000000..b974d6f --- /dev/null +++ b/doc/api/v1/profiles/update_profile.json @@ -0,0 +1,48 @@ +{ + "resource": "Profiles", + "resource_explanation": null, + "http_method": "PATCH", + "route": "/v1/profile", + "description": "Update Profile", + "explanation": null, + "parameters": [ + { + "name": "full_name", + "description": "full name" + }, + { + "name": "email", + "description": "email" + }, + { + "name": "password", + "description": "password" + } + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "PATCH", + "request_path": "/v1/profile", + "request_body": "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":\"Example User Updated\",\"email\":\"user_updated@example.com\",\"password\":\"new_password\"}}}", + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json", + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjF9.VT2DC9Fm-k4FAwRquGtGn7IzsUuB6771z-oVK_q2kKM" + }, + "request_query_parameters": { + }, + "request_content_type": "application/vnd.api+json", + "response_status": 200, + "response_status_text": "OK", + "response_body": "{\n \"data\": {\n \"id\": \"21\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user_updated@example.com\",\n \"full-name\": \"Augustine Donnelly\"\n }\n }\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":\"Example User Updated\",\"email\":\"user_updated@example.com\",\"password\":\"new_password\"}}}' -X PATCH \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjF9.VT2DC9Fm-k4FAwRquGtGn7IzsUuB6771z-oVK_q2kKM\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/profiles/update_profile.md b/doc/api/v1/profiles/update_profile.md deleted file mode 100644 index f6624e9..0000000 --- a/doc/api/v1/profiles/update_profile.md +++ /dev/null @@ -1,59 +0,0 @@ -# Profiles API - -## Update Profile - -### PATCH /v1/profile - -### Parameters - -| Name | Description | Required | Scope | -|------|-------------|----------|-------| -| full_name | full name | false | | -| email | email | false | | -| password | password | false | | - -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6Mn0.9SU-CBPxQt3tmRC0-L67hufyMvwHUBOuXv9lp5do65Y
- -#### Route - -
PATCH /v1/profile
- -#### Body - -
{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}
- -#### cURL - -
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}' -X PATCH \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6Mn0.9SU-CBPxQt3tmRC0-L67hufyMvwHUBOuXv9lp5do65Y"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
200 OK
- -#### Body - -
{
-  "data": {
-    "id": "2",
-    "type": "users",
-    "attributes": {
-      "email": "user_updated@example.com",
-      "full-name": "Deon Ryan"
-    }
-  }
-}
diff --git a/doc/api/v1/signup/create_user.json b/doc/api/v1/signup/create_user.json new file mode 100644 index 0000000..1accf70 --- /dev/null +++ b/doc/api/v1/signup/create_user.json @@ -0,0 +1,49 @@ +{ + "resource": "SignUp", + "resource_explanation": null, + "http_method": "POST", + "route": "/v1/sign_ups", + "description": "Create User", + "explanation": null, + "parameters": [ + { + "name": "full_name", + "description": "full name" + }, + { + "required": true, + "name": "email", + "description": "email" + }, + { + "required": true, + "name": "password", + "description": "password" + } + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "POST", + "request_path": "/v1/sign_ups", + "request_body": "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":\"Example User\",\"email\":\"user@example.com\",\"password\":\"123456\"}}}", + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json" + }, + "request_query_parameters": { + }, + "request_content_type": "application/vnd.api+json", + "response_status": 201, + "response_status_text": "Created", + "response_body": "{\n \"data\": {\n \"id\": \"27\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user@example.com\",\n \"full-name\": null\n }\n }\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl \"http://localhost:5000/v1/sign_ups\" -d '{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":\"Example User\",\"email\":\"user@example.com\",\"password\":\"123456\"}}}' -X POST \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/signup/create_user.md b/doc/api/v1/signup/create_user.md deleted file mode 100644 index d6a179b..0000000 --- a/doc/api/v1/signup/create_user.md +++ /dev/null @@ -1,57 +0,0 @@ -# SignUp API - -## Create User - -### POST /v1/sign_ups - -### Parameters - -| Name | Description | Required | Scope | -|------|-------------|----------|-------| -| full_name | full name | false | | -| email | email | true | | -| password | password | true | | - -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
- -#### Route - -
POST /v1/sign_ups
- -#### Body - -
{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}
- -#### cURL - -
curl "http://localhost:5000/v1/sign_ups" -d '{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}' -X POST \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
201 Created
- -#### Body - -
{
-  "data": {
-    "id": "8",
-    "type": "users",
-    "attributes": {
-      "email": "user@example.com",
-      "full-name": null
-    }
-  }
-}
diff --git a/doc/api/v1/tokens/create_token.json b/doc/api/v1/tokens/create_token.json new file mode 100644 index 0000000..74a020b --- /dev/null +++ b/doc/api/v1/tokens/create_token.json @@ -0,0 +1,45 @@ +{ + "resource": "Tokens", + "resource_explanation": null, + "http_method": "POST", + "route": "/v1/tokens", + "description": "Create Token", + "explanation": null, + "parameters": [ + { + "required": true, + "name": "email", + "description": "email" + }, + { + "required": true, + "name": "password", + "description": "password" + } + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "POST", + "request_path": "/v1/tokens", + "request_body": "{\"data\":{\"type\":\"token-requests\",\"attributes\":{\"email\":\"user@example.com\",\"password\":\"123456\"}}}", + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json" + }, + "request_query_parameters": { + }, + "request_content_type": "application/vnd.api+json", + "response_status": 201, + "response_status_text": "Created", + "response_body": "{\n \"data\": {\n \"id\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6Mjl9.b0Nwzi8JjUN0c6i5iBWFZWHobqiphmHkGgLmUOldhC0\",\n \"type\": \"jwt-tokens\",\n \"attributes\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6Mjl9.b0Nwzi8JjUN0c6i5iBWFZWHobqiphmHkGgLmUOldhC0\"\n }\n }\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl \"http://localhost:5000/v1/tokens\" -d '{\"data\":{\"type\":\"token-requests\",\"attributes\":{\"email\":\"user@example.com\",\"password\":\"123456\"}}}' -X POST \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/tokens/create_token.md b/doc/api/v1/tokens/create_token.md deleted file mode 100644 index 6956a78..0000000 --- a/doc/api/v1/tokens/create_token.md +++ /dev/null @@ -1,55 +0,0 @@ -# Tokens API - -## Create Token - -### POST /v1/tokens - -### Parameters - -| Name | Description | Required | Scope | -|------|-------------|----------|-------| -| email | email | true | | -| password | password | true | | - -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
- -#### Route - -
POST /v1/tokens
- -#### Body - -
{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}
- -#### cURL - -
curl "http://localhost:5000/v1/tokens" -d '{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}' -X POST \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
201 Created
- -#### Body - -
{
-  "data": {
-    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTB9.pCGA_K_9r5mb2kaPpvSbhU5tgvox62zIaSa76XvDgB0",
-    "type": "jwt-tokens",
-    "attributes": {
-      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTB9.pCGA_K_9r5mb2kaPpvSbhU5tgvox62zIaSa76XvDgB0"
-    }
-  }
-}
diff --git a/doc/api/v1/users/list_users.json b/doc/api/v1/users/list_users.json new file mode 100644 index 0000000..c6750bf --- /dev/null +++ b/doc/api/v1/users/list_users.json @@ -0,0 +1,38 @@ +{ + "resource": "Users", + "resource_explanation": null, + "http_method": "GET", + "route": "/v1/users", + "description": "List Users", + "explanation": null, + "parameters": [ + + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "GET", + "request_path": "/v1/users", + "request_body": null, + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json", + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzR9.cA6jlmqtcsXs8AzYMD_tYnS47VlYwwMorHyNe3fqXq8" + }, + "request_query_parameters": { + "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null + }, + "request_content_type": "application/vnd.api+json", + "response_status": 200, + "response_status_text": "OK", + "response_body": "{\n \"data\": [\n {\n \"id\": \"31\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user7@example.com\",\n \"full-name\": \"Buford Purdy\"\n }\n },\n {\n \"id\": \"32\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user8@example.com\",\n \"full-name\": \"Harrison Maggio Sr.\"\n }\n },\n {\n \"id\": \"33\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user9@example.com\",\n \"full-name\": \"Margaret Reilly I\"\n }\n },\n {\n \"id\": \"34\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user10@example.com\",\n \"full-name\": \"Sherril Barrows\"\n }\n }\n ]\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl -g \"http://localhost:5000/v1/users\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzR9.cA6jlmqtcsXs8AzYMD_tYnS47VlYwwMorHyNe3fqXq8\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/users/list_users.md b/doc/api/v1/users/list_users.md deleted file mode 100644 index 9bbb60a..0000000 --- a/doc/api/v1/users/list_users.md +++ /dev/null @@ -1,76 +0,0 @@ -# Users API - -## List Users - -### GET /v1/users -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTV9.l3UZHbKGxQUugSTqjDV3finM1WFP33yRqwiLDfuW4aM
- -#### Route - -
GET /v1/users
- -#### Query Parameters - -
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
- -#### cURL - -
curl -g "http://localhost:5000/v1/users" -X GET \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTV9.l3UZHbKGxQUugSTqjDV3finM1WFP33yRqwiLDfuW4aM"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
200 OK
- -#### Body - -
{
-  "data": [
-    {
-      "id": "12",
-      "type": "users",
-      "attributes": {
-        "email": "user7@example.com",
-        "full-name": "Lesia Quigley"
-      }
-    },
-    {
-      "id": "13",
-      "type": "users",
-      "attributes": {
-        "email": "user8@example.com",
-        "full-name": "Maricruz Carroll"
-      }
-    },
-    {
-      "id": "14",
-      "type": "users",
-      "attributes": {
-        "email": "user9@example.com",
-        "full-name": "Margot Kuhlman"
-      }
-    },
-    {
-      "id": "15",
-      "type": "users",
-      "attributes": {
-        "email": "user10@example.com",
-        "full-name": "Roman Tillman III"
-      }
-    }
-  ]
-}
diff --git a/doc/api/v1/users/retrive_user.json b/doc/api/v1/users/retrive_user.json new file mode 100644 index 0000000..db6ffc6 --- /dev/null +++ b/doc/api/v1/users/retrive_user.json @@ -0,0 +1,42 @@ +{ + "resource": "Users", + "resource_explanation": null, + "http_method": "GET", + "route": "/v1/users/:id", + "description": "Retrive User", + "explanation": null, + "parameters": [ + { + "required": true, + "name": "id", + "description": "user id" + } + ], + "response_fields": [ + + ], + "requests": [ + { + "request_method": "GET", + "request_path": "/v1/users/35", + "request_body": null, + "request_headers": { + "Content-Type": "application/vnd.api+json", + "Accept": "application/vnd.api+json", + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzZ9.f39CMZ0egCtQIPp50FtFZe_OIdYsoqjwDNKQ3d0rGNk" + }, + "request_query_parameters": { + "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null + }, + "request_content_type": "application/vnd.api+json", + "response_status": 200, + "response_status_text": "OK", + "response_body": "{\n \"data\": {\n \"id\": \"35\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user11@example.com\",\n \"full-name\": \"Gene Labadie\"\n }\n }\n}", + "response_headers": { + "Content-Type": "application/vnd.api+json; charset=utf-8" + }, + "response_content_type": "application/vnd.api+json; charset=utf-8", + "curl": "curl -g \"http://localhost:5000/v1/users/35\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzZ9.f39CMZ0egCtQIPp50FtFZe_OIdYsoqjwDNKQ3d0rGNk\"" + } + ] +} \ No newline at end of file diff --git a/doc/api/v1/users/retrive_user.md b/doc/api/v1/users/retrive_user.md deleted file mode 100644 index 06ad190..0000000 --- a/doc/api/v1/users/retrive_user.md +++ /dev/null @@ -1,57 +0,0 @@ -# Users API - -## Retrive User - -### GET /v1/users/:id - -### Parameters - -| Name | Description | Required | Scope | -|------|-------------|----------|-------| -| id | user id | true | | - -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTd9.3xNGz7ZI3CAQH_DDtXGQNAhjQtjyNWt2lWeFUqBSbZA
- -#### Route - -
GET /v1/users/16
- -#### Query Parameters - -
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
- -#### cURL - -
curl -g "http://localhost:5000/v1/users/16" -X GET \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0MzYzNjEsInN1YiI6MTd9.3xNGz7ZI3CAQH_DDtXGQNAhjQtjyNWt2lWeFUqBSbZA"
- -### Response - -#### Headers - -
Content-Type: application/vnd.api+json; charset=utf-8
- -#### Status - -
200 OK
- -#### Body - -
{
-  "data": {
-    "id": "16",
-    "type": "users",
-    "attributes": {
-      "email": "user11@example.com",
-      "full-name": "Dusty Lang"
-    }
-  }
-}
diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 232e01d..1c75a4e 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -1,3 +1,5 @@ +require "database_cleaner" + RSpec.configure do |config| config.before(:suite) do DatabaseCleaner.clean_with(:deletion) diff --git a/spec/support/email.rb b/spec/support/email.rb index f95815b..9dc8762 100644 --- a/spec/support/email.rb +++ b/spec/support/email.rb @@ -1,3 +1,5 @@ +require "email_spec" + RSpec.configure do |config| config.include EmailSpec::Helpers config.include EmailSpec::Matchers diff --git a/spec/support/rspec_api_documentation.rb b/spec/support/rspec_api_documentation.rb index 3707899..2db8a1b 100644 --- a/spec/support/rspec_api_documentation.rb +++ b/spec/support/rspec_api_documentation.rb @@ -3,7 +3,7 @@ # rubocop:disable Style/WordArray RspecApiDocumentation.configure do |config| - config.format = :markdown + config.format = :JSON config.docs_dir = Rails.root.join("doc", "api", "v1") config.request_headers_to_include = ["Accept", "Content-Type", "Authorization"] config.response_headers_to_include = ["Content-Type"] From c7e55d0080027b7ddcd63646d145a16ce99fc86d Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Sun, 9 Sep 2018 13:17:23 +0300 Subject: [PATCH 10/26] Use spring watcher listen gem --- Gemfile | 2 +- Gemfile.lock | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index ba9cdea..f61ea8a 100644 --- a/Gemfile +++ b/Gemfile @@ -25,9 +25,9 @@ group :development do gem "foreman" gem "letter_opener" - gem "listen", ">= 3.0.5", "< 3.2" gem "spring" gem "spring-commands-rspec" + gem "spring-watcher-listen" end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index 99da1e5..235171d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -263,6 +263,9 @@ GEM activesupport (>= 4.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -311,7 +314,6 @@ DEPENDENCIES kaminari knock letter_opener - listen (>= 3.0.5, < 3.2) pg puma raddocs @@ -327,6 +329,7 @@ DEPENDENCIES simplecov spring spring-commands-rspec + spring-watcher-listen webmock RUBY VERSION From 01a5099122264208f77c54d5778e63f998ac6415 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 11:26:52 +0300 Subject: [PATCH 11/26] Move json schemas to v1 folder --- spec/api/v1/profiles_spec.rb | 12 ++++++------ spec/api/v1/sign_ups_spec.rb | 8 ++++---- spec/api/v1/tokens_spec.rb | 4 ++-- spec/api/v1/users_spec.rb | 6 +++--- spec/schemas/{ => v1}/definitions.json | 0 spec/schemas/{ => v1}/error.json | 0 spec/schemas/{ => v1}/jsonapi.json | 0 spec/schemas/{ => v1}/jwt_token.json | 0 spec/schemas/{ => v1}/user.json | 0 spec/schemas/{ => v1}/users.json | 0 10 files changed, 15 insertions(+), 15 deletions(-) rename spec/schemas/{ => v1}/definitions.json (100%) rename spec/schemas/{ => v1}/error.json (100%) rename spec/schemas/{ => v1}/jsonapi.json (100%) rename spec/schemas/{ => v1}/jwt_token.json (100%) rename spec/schemas/{ => v1}/user.json (100%) rename spec/schemas/{ => v1}/users.json (100%) diff --git a/spec/api/v1/profiles_spec.rb b/spec/api/v1/profiles_spec.rb index 91fcb51..8661dcb 100644 --- a/spec/api/v1/profiles_spec.rb +++ b/spec/api/v1/profiles_spec.rb @@ -18,7 +18,7 @@ class ProfileRequestSerializer < ApplicationSerializer example_request "Retrive Profile" do expect(response_status).to eq(200) - expect(response_body).to match_response_schema("users") + expect(response_body).to match_response_schema("v1/users") end end @@ -34,7 +34,7 @@ class ProfileRequestSerializer < ApplicationSerializer example_request "Update Profile" do expect(response_status).to eq(200) - expect(response_body).to match_response_schema("user") + expect(response_body).to match_response_schema("v1/user") end context "with invalid email" do @@ -43,7 +43,7 @@ class ProfileRequestSerializer < ApplicationSerializer example "Update Profile with invalid email", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end @@ -53,7 +53,7 @@ class ProfileRequestSerializer < ApplicationSerializer example "Update Profile with invalid email", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end @@ -65,7 +65,7 @@ class ProfileRequestSerializer < ApplicationSerializer example "Update Profile with existing email", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end end @@ -75,7 +75,7 @@ class ProfileRequestSerializer < ApplicationSerializer example_request "Delete Profile" do expect(response_status).to eq(200) - expect(response_body).to match_response_schema("user") + expect(response_body).to match_response_schema("v1/user") end end end diff --git a/spec/api/v1/sign_ups_spec.rb b/spec/api/v1/sign_ups_spec.rb index 1bf626b..907cec3 100644 --- a/spec/api/v1/sign_ups_spec.rb +++ b/spec/api/v1/sign_ups_spec.rb @@ -24,7 +24,7 @@ class SignUpRequestSerializer < ApplicationSerializer example_request "Create User" do expect(response_status).to eq(201) - expect(response_body).to match_response_schema("user") + expect(response_body).to match_response_schema("v1/user") end context "with invalid email" do @@ -33,7 +33,7 @@ class SignUpRequestSerializer < ApplicationSerializer example "Create User with invalid email", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end @@ -43,7 +43,7 @@ class SignUpRequestSerializer < ApplicationSerializer example "Create User with invalid password", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end @@ -55,7 +55,7 @@ class SignUpRequestSerializer < ApplicationSerializer example "Create User with existing email", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end end diff --git a/spec/api/v1/tokens_spec.rb b/spec/api/v1/tokens_spec.rb index 82885e3..6bafc28 100644 --- a/spec/api/v1/tokens_spec.rb +++ b/spec/api/v1/tokens_spec.rb @@ -27,7 +27,7 @@ class TokenRequestSerializer < ApplicationSerializer example_request "Create Token" do expect(response_status).to eq(201) - expect(response_body).to match_response_schema("jwt_token") + expect(response_body).to match_response_schema("v1/jwt_token") end context "with invalid password" do @@ -36,7 +36,7 @@ class TokenRequestSerializer < ApplicationSerializer example "Create Token with invalid password", document: false do do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end end diff --git a/spec/api/v1/users_spec.rb b/spec/api/v1/users_spec.rb index 9519c7c..7e70c1d 100644 --- a/spec/api/v1/users_spec.rb +++ b/spec/api/v1/users_spec.rb @@ -22,7 +22,7 @@ class UserRequestSerializer < ApplicationSerializer example_request "List Users" do expect(response_status).to eq(200) - expect(response_body).to match_response_schema("users") + expect(response_body).to match_response_schema("v1/users") end end @@ -35,7 +35,7 @@ class UserRequestSerializer < ApplicationSerializer example_request "Retrive User" do expect(response_status).to eq(200) - expect(response_body).to match_response_schema("user") + expect(response_body).to match_response_schema("v1/user") end context "with invalid id" do @@ -44,7 +44,7 @@ class UserRequestSerializer < ApplicationSerializer example "Retrive User with invalid id", document: false do do_request expect(response_status).to eq(404) - expect(response_body).to match_response_schema("error") + expect(response_body).to match_response_schema("v1/error") end end end diff --git a/spec/schemas/definitions.json b/spec/schemas/v1/definitions.json similarity index 100% rename from spec/schemas/definitions.json rename to spec/schemas/v1/definitions.json diff --git a/spec/schemas/error.json b/spec/schemas/v1/error.json similarity index 100% rename from spec/schemas/error.json rename to spec/schemas/v1/error.json diff --git a/spec/schemas/jsonapi.json b/spec/schemas/v1/jsonapi.json similarity index 100% rename from spec/schemas/jsonapi.json rename to spec/schemas/v1/jsonapi.json diff --git a/spec/schemas/jwt_token.json b/spec/schemas/v1/jwt_token.json similarity index 100% rename from spec/schemas/jwt_token.json rename to spec/schemas/v1/jwt_token.json diff --git a/spec/schemas/user.json b/spec/schemas/v1/user.json similarity index 100% rename from spec/schemas/user.json rename to spec/schemas/v1/user.json diff --git a/spec/schemas/users.json b/spec/schemas/v1/users.json similarity index 100% rename from spec/schemas/users.json rename to spec/schemas/v1/users.json From e3238228ab44d94ddbf2866a1347ba8eb7d279f4 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 11:27:20 +0300 Subject: [PATCH 12/26] Remove unused spec --- spec/api/errors_spec.rb | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 spec/api/errors_spec.rb diff --git a/spec/api/errors_spec.rb b/spec/api/errors_spec.rb deleted file mode 100644 index b8164d4..0000000 --- a/spec/api/errors_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require "rails_helper" - -resource "Errors" do - include_context "with JSON API Headers" - - # get "/not-found" do - # example_request "Request to unexisting page" do - # expect(response_status).to eq(404) - # expect(response_body).to match_response_schema("error") - # end - # end -end From 45ed67578dcc477f4b21e37f8475fb56040063db Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 11:34:17 +0300 Subject: [PATCH 13/26] Remove unused JsonApiRequest --- app/models/json_api_request.rb | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 app/models/json_api_request.rb diff --git a/app/models/json_api_request.rb b/app/models/json_api_request.rb deleted file mode 100644 index bff3166..0000000 --- a/app/models/json_api_request.rb +++ /dev/null @@ -1,2 +0,0 @@ -class JsonApiRequest < ActiveModelSerializers::Model -end From 1732e7aa8e686c022670f9444135ea03537e302f Mon Sep 17 00:00:00 2001 From: ArthurZaharov Date: Mon, 10 Sep 2018 11:38:41 +0300 Subject: [PATCH 14/26] Remove byebug from git history --- .byebug_history | 75 ------------------------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 .byebug_history diff --git a/.byebug_history b/.byebug_history deleted file mode 100644 index 3eae956..0000000 --- a/.byebug_history +++ /dev/null @@ -1,75 +0,0 @@ -continue -ActiveModelSerializers::SerializableResource.new(t).to_json -t=request_class.classify.constantize.new(params) -request_class.classify.constantize.new(params) -params -continue -params -request_class.classify.constantize.new(params) -continue -request_class.classify.constantize.new(params) -params.publicrequest_class.classify.constantize.new(params) -params.data -ProfileRequest.new(params.as_json) -ProfileRequest.new(params.to_json) -params.to_json -params -ProfileRequest.new(params) -ProfileRequest.constantize.new(params) -params -user_params -continue -user_params -params -continue -params -continue -params -continue -user_params -params -continue -params -user_params -continue -respond_with_resource_errors(current_user) -current_user.update(user_params) -continue -params -respond_with_resource_errors(current_user) -current_user.update(user_params) -current_user -user_params -continue -users -User.all.count -User.all -continue -User.all -users -continue -params -continue -params -continue -params -continue -JsonSchema.parse!(schema_data).uri -continue -JsonSchema.parse!(schema_data).uri -JsonSchema.parse!(schema_data) -schema_path -continue -schema_path -schema_data -continue -schema.uri -continue -schema.uri -continue -schema.uri -continue -schema.uri.nil? -schema.uri -schema -continue From faa97f1c8a1243dba64a25a1be4d58a439dba561 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 11:44:01 +0300 Subject: [PATCH 15/26] Setup ENV vars for Heroku --- app.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app.json b/app.json index c716306..c28419c 100644 --- a/app.json +++ b/app.json @@ -1,12 +1,17 @@ { "name": "Rails Base API", - "description": "Skeleton for new Rails 4 application for REST API", + "description": "Skeleton for new Rails application with JSON API", "env": { - "ALLOW_REQUESTS_FROM": { "required": true }, + "HOST": { "required": true }, + "LANG": { "required": true }, + "CORS_ORIGINS": { "required": true }, "RACK_ENV": { "required": true }, - "SECRET_KEY_BASE": { "required": true } + "RAILS_ENV": { "required": true }, + "SECRET_KEY_BASE": { "required": true }, + "MAILER_SENDER_ADDRESS": { "required": true } }, "addons": [ - "heroku-postgresql:hobby-dev" + "heroku-postgresql", + "sendgrid" ] } From bc60de3badde9f3ec295b89f63977a71bf61955b Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 12:03:23 +0300 Subject: [PATCH 16/26] Configure puma for Heroku --- Procfile | 2 +- config/puma.rb | 41 +++++++++++------------------------------ 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/Procfile b/Procfile index ca35301..c2c566e 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: bundle exec rails server thin -p $PORT -e $RACK_ENV +web: bundle exec puma -C config/puma.rb diff --git a/config/puma.rb b/config/puma.rb index a5eccf8..7d3431e 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,34 +1,15 @@ -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -# -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +workers Integer(ENV["WEB_CONCURRENCY"] || 2) +threads_count = Integer(ENV["RAILS_MAX_THREADS"] || 5) threads threads_count, threads_count -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. -# -port ENV.fetch("PORT") { 3000 } +preload_app! -# Specifies the `environment` that Puma will run in. -# -environment ENV.fetch("RAILS_ENV") { "development" } +rackup DefaultRackup +port ENV["PORT"] || 3000 +environment ENV["RACK_ENV"] || "development" -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } - -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. -# -# preload_app! - -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart +on_worker_boot do + # Worker specific setup for Rails 4.1+ + # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot + ActiveRecord::Base.establish_connection +end From e4b44161c92496200fe774fb8ad143fc4a27cbb0 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 12:29:13 +0300 Subject: [PATCH 17/26] Update docs --- .erdconfig | 6 ++++++ .gitignore | 1 + Gemfile | 2 +- Gemfile.lock | 8 ++++++++ bin/doc | 6 ++++++ doc/api/v1/profiles/delete_profile.json | 6 +++--- doc/api/v1/profiles/retrive_profile.json | 6 +++--- doc/api/v1/profiles/update_profile.json | 6 +++--- doc/api/v1/signup/create_user.json | 2 +- doc/api/v1/tokens/create_token.json | 2 +- doc/api/v1/users/list_users.json | 6 +++--- doc/api/v1/users/retrive_user.json | 8 ++++---- lib/tasks/docs.rake | 16 ---------------- 13 files changed, 40 insertions(+), 35 deletions(-) create mode 100644 .erdconfig create mode 100755 bin/doc delete mode 100644 lib/tasks/docs.rake diff --git a/.erdconfig b/.erdconfig new file mode 100644 index 0000000..e5fce58 --- /dev/null +++ b/.erdconfig @@ -0,0 +1,6 @@ +# more about rails-erd config file you can find +# here: https://github.com/voormedia/rails-erd#configuration +# and here: http://voormedia.github.io/rails-erd/customise.html + +filename: doc/entity-relationship +filetype: png diff --git a/.gitignore b/.gitignore index 2724481..aa312a9 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ vendor/ruby .sass-cache ._* .env +doc/entity-relationship.png diff --git a/Gemfile b/Gemfile index f61ea8a..d9417a4 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ group :development do gem "bullet" gem "foreman" gem "letter_opener" - + gem "rails-erd" gem "spring" gem "spring-commands-rspec" gem "spring-watcher-listen" diff --git a/Gemfile.lock b/Gemfile.lock index 235171d..94f9601 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,6 +65,7 @@ GEM byebug (10.0.2) case_transform (0.2) activesupport + choice (0.2.0) concurrent-ruby (1.0.5) crack (0.4.3) safe_yaml (~> 1.0.0) @@ -190,6 +191,11 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) + rails-erd (1.5.2) + activerecord (>= 3.2) + activesupport (>= 3.2) + choice (~> 0.2.0) + ruby-graphviz (~> 1.2) rails-html-sanitizer (1.0.4) loofah (~> 2.2, >= 2.2.2) railties (5.2.1) @@ -243,6 +249,7 @@ GEM unicode-display_width (~> 1.0, >= 1.0.1) rubocop-rspec (1.29.1) rubocop (>= 0.58.0) + ruby-graphviz (1.2.3) ruby-progressbar (1.10.0) ruby_dep (1.5.0) safe_yaml (1.0.4) @@ -318,6 +325,7 @@ DEPENDENCIES puma raddocs rails (= 5.2.1) + rails-erd responders rollbar rspec-rails diff --git a/bin/doc b/bin/doc new file mode 100755 index 0000000..7596121 --- /dev/null +++ b/bin/doc @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +set -e + +bin/rspec spec/api/ --format RspecApiDocumentation::ApiFormatter +bin/rails erd diff --git a/doc/api/v1/profiles/delete_profile.json b/doc/api/v1/profiles/delete_profile.json index 6ec8625..ae05447 100644 --- a/doc/api/v1/profiles/delete_profile.json +++ b/doc/api/v1/profiles/delete_profile.json @@ -19,19 +19,19 @@ "request_headers": { "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjgsInN1YiI6MjZ9.iD68UgkIB44hIfsjj-txcKBxFtdfCRxTkJNeTq_6Pyw" + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQyfQ.1KjEfxd9f4cPu4DrKcrKXIYdL6Hl_kzftBKHcT8qTrE" }, "request_query_parameters": { }, "request_content_type": "application/vnd.api+json", "response_status": 200, "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"26\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user6@example.com\",\n \"full-name\": \"Shane Ruecker\"\n }\n }\n}", + "response_body": "{\n \"data\": {\n \"id\": \"142\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user6@example.com\",\n \"full-name\": \"Merna McKenzie\"\n }\n }\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}' -X DELETE \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjgsInN1YiI6MjZ9.iD68UgkIB44hIfsjj-txcKBxFtdfCRxTkJNeTq_6Pyw\"" + "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}' -X DELETE \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQyfQ.1KjEfxd9f4cPu4DrKcrKXIYdL6Hl_kzftBKHcT8qTrE\"" } ] } \ No newline at end of file diff --git a/doc/api/v1/profiles/retrive_profile.json b/doc/api/v1/profiles/retrive_profile.json index bbb4d8c..6368db0 100644 --- a/doc/api/v1/profiles/retrive_profile.json +++ b/doc/api/v1/profiles/retrive_profile.json @@ -19,7 +19,7 @@ "request_headers": { "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjB9.cKmDT-WB6dMnYdDs1QWFYvbExfQ-1dJ9RcmQJqfSkgQ" + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM2fQ.9NwGwpMJ32dxuRAGO5_gIdTdwuX_LSZxCKVGpMUnGso" }, "request_query_parameters": { "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null @@ -27,12 +27,12 @@ "request_content_type": "application/vnd.api+json", "response_status": 200, "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"20\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user1@example.com\",\n \"full-name\": \"Drew Murray\"\n }\n }\n}", + "response_body": "{\n \"data\": {\n \"id\": \"136\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user1@example.com\",\n \"full-name\": \"Branden Flatley V\"\n }\n }\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl -g \"http://localhost:5000/v1/profile\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjB9.cKmDT-WB6dMnYdDs1QWFYvbExfQ-1dJ9RcmQJqfSkgQ\"" + "curl": "curl -g \"http://localhost:5000/v1/profile\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM2fQ.9NwGwpMJ32dxuRAGO5_gIdTdwuX_LSZxCKVGpMUnGso\"" } ] } \ No newline at end of file diff --git a/doc/api/v1/profiles/update_profile.json b/doc/api/v1/profiles/update_profile.json index b974d6f..f0bc35d 100644 --- a/doc/api/v1/profiles/update_profile.json +++ b/doc/api/v1/profiles/update_profile.json @@ -30,19 +30,19 @@ "request_headers": { "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjF9.VT2DC9Fm-k4FAwRquGtGn7IzsUuB6771z-oVK_q2kKM" + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM3fQ.ogYNTYnZR0T9zdRjXeOmyb35qnr5FHjnjY8L5HeyH2Q" }, "request_query_parameters": { }, "request_content_type": "application/vnd.api+json", "response_status": 200, "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"21\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user_updated@example.com\",\n \"full-name\": \"Augustine Donnelly\"\n }\n }\n}", + "response_body": "{\n \"data\": {\n \"id\": \"137\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user_updated@example.com\",\n \"full-name\": \"Wesley Rodriguez\"\n }\n }\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":\"Example User Updated\",\"email\":\"user_updated@example.com\",\"password\":\"new_password\"}}}' -X PATCH \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjcsInN1YiI6MjF9.VT2DC9Fm-k4FAwRquGtGn7IzsUuB6771z-oVK_q2kKM\"" + "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":\"Example User Updated\",\"email\":\"user_updated@example.com\",\"password\":\"new_password\"}}}' -X PATCH \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM3fQ.ogYNTYnZR0T9zdRjXeOmyb35qnr5FHjnjY8L5HeyH2Q\"" } ] } \ No newline at end of file diff --git a/doc/api/v1/signup/create_user.json b/doc/api/v1/signup/create_user.json index 1accf70..e09c743 100644 --- a/doc/api/v1/signup/create_user.json +++ b/doc/api/v1/signup/create_user.json @@ -38,7 +38,7 @@ "request_content_type": "application/vnd.api+json", "response_status": 201, "response_status_text": "Created", - "response_body": "{\n \"data\": {\n \"id\": \"27\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user@example.com\",\n \"full-name\": null\n }\n }\n}", + "response_body": "{\n \"data\": {\n \"id\": \"143\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user@example.com\",\n \"full-name\": null\n }\n }\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, diff --git a/doc/api/v1/tokens/create_token.json b/doc/api/v1/tokens/create_token.json index 74a020b..9da71dd 100644 --- a/doc/api/v1/tokens/create_token.json +++ b/doc/api/v1/tokens/create_token.json @@ -34,7 +34,7 @@ "request_content_type": "application/vnd.api+json", "response_status": 201, "response_status_text": "Created", - "response_body": "{\n \"data\": {\n \"id\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6Mjl9.b0Nwzi8JjUN0c6i5iBWFZWHobqiphmHkGgLmUOldhC0\",\n \"type\": \"jwt-tokens\",\n \"attributes\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6Mjl9.b0Nwzi8JjUN0c6i5iBWFZWHobqiphmHkGgLmUOldhC0\"\n }\n }\n}", + "response_body": "{\n \"data\": {\n \"id\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQ1fQ.J_9o_xb83OUdY64BDC6u7gwYH_TLOK50Xv_FctdHtX8\",\n \"type\": \"jwt-tokens\",\n \"attributes\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQ1fQ.J_9o_xb83OUdY64BDC6u7gwYH_TLOK50Xv_FctdHtX8\"\n }\n }\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, diff --git a/doc/api/v1/users/list_users.json b/doc/api/v1/users/list_users.json index c6750bf..018d08b 100644 --- a/doc/api/v1/users/list_users.json +++ b/doc/api/v1/users/list_users.json @@ -19,7 +19,7 @@ "request_headers": { "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzR9.cA6jlmqtcsXs8AzYMD_tYnS47VlYwwMorHyNe3fqXq8" + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTUwfQ.pLEe8_cz-Mnx8lUyKOQWSDpgm6KIgiCSOqBgpL85V-A" }, "request_query_parameters": { "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null @@ -27,12 +27,12 @@ "request_content_type": "application/vnd.api+json", "response_status": 200, "response_status_text": "OK", - "response_body": "{\n \"data\": [\n {\n \"id\": \"31\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user7@example.com\",\n \"full-name\": \"Buford Purdy\"\n }\n },\n {\n \"id\": \"32\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user8@example.com\",\n \"full-name\": \"Harrison Maggio Sr.\"\n }\n },\n {\n \"id\": \"33\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user9@example.com\",\n \"full-name\": \"Margaret Reilly I\"\n }\n },\n {\n \"id\": \"34\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user10@example.com\",\n \"full-name\": \"Sherril Barrows\"\n }\n }\n ]\n}", + "response_body": "{\n \"data\": [\n {\n \"id\": \"147\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user7@example.com\",\n \"full-name\": \"Clarence Rice\"\n }\n },\n {\n \"id\": \"148\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user8@example.com\",\n \"full-name\": \"Carlton Connelly\"\n }\n },\n {\n \"id\": \"149\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user9@example.com\",\n \"full-name\": \"Mr. Candy Koch\"\n }\n },\n {\n \"id\": \"150\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user10@example.com\",\n \"full-name\": \"Connie Roberts\"\n }\n }\n ]\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl -g \"http://localhost:5000/v1/users\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzR9.cA6jlmqtcsXs8AzYMD_tYnS47VlYwwMorHyNe3fqXq8\"" + "curl": "curl -g \"http://localhost:5000/v1/users\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTUwfQ.pLEe8_cz-Mnx8lUyKOQWSDpgm6KIgiCSOqBgpL85V-A\"" } ] } \ No newline at end of file diff --git a/doc/api/v1/users/retrive_user.json b/doc/api/v1/users/retrive_user.json index db6ffc6..e89a7a0 100644 --- a/doc/api/v1/users/retrive_user.json +++ b/doc/api/v1/users/retrive_user.json @@ -18,12 +18,12 @@ "requests": [ { "request_method": "GET", - "request_path": "/v1/users/35", + "request_path": "/v1/users/151", "request_body": null, "request_headers": { "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzZ9.f39CMZ0egCtQIPp50FtFZe_OIdYsoqjwDNKQ3d0rGNk" + "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzMsInN1YiI6MTUyfQ.bP7_CXiVL1Q3TihIGr0NwNQSnLQAJjB4ChiE88_xp2Q" }, "request_query_parameters": { "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null @@ -31,12 +31,12 @@ "request_content_type": "application/vnd.api+json", "response_status": 200, "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"35\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user11@example.com\",\n \"full-name\": \"Gene Labadie\"\n }\n }\n}", + "response_body": "{\n \"data\": {\n \"id\": \"151\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user11@example.com\",\n \"full-name\": \"Doria Sporer\"\n }\n }\n}", "response_headers": { "Content-Type": "application/vnd.api+json; charset=utf-8" }, "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl -g \"http://localhost:5000/v1/users/35\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY0Mzc2NjksInN1YiI6MzZ9.f39CMZ0egCtQIPp50FtFZe_OIdYsoqjwDNKQ3d0rGNk\"" + "curl": "curl -g \"http://localhost:5000/v1/users/151\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzMsInN1YiI6MTUyfQ.bP7_CXiVL1Q3TihIGr0NwNQSnLQAJjB4ChiE88_xp2Q\"" } ] } \ No newline at end of file diff --git a/lib/tasks/docs.rake b/lib/tasks/docs.rake deleted file mode 100644 index 2f4695a..0000000 --- a/lib/tasks/docs.rake +++ /dev/null @@ -1,16 +0,0 @@ -require "rspec/core/rake_task" - -Rake::Task["docs:generate"].clear -Rake::Task["docs:generate:ordered"].clear - -desc "Generate API request documentation from API specs" -RSpec::Core::RakeTask.new("docs:generate") do |t| - t.pattern = "spec/api/**/*_spec.rb" - t.rspec_opts = ["--format RspecApiDocumentation::ApiFormatter"] -end - -desc "Generate API request documentation from API specs (ordered)" -RSpec::Core::RakeTask.new("docs:generate:ordered") do |t| - t.pattern = "spec/api/**/*_spec.rb" - t.rspec_opts = ["--format RspecApiDocumentation::ApiFormatter", "--order defined"] -end From 864cf6037b94c2a9d4d3bf548a56fc947f3beb0e Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 15:09:35 +0300 Subject: [PATCH 18/26] Add rack-cors --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index d9417a4..2a63540 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ gem "interactor" gem "kaminari" gem "knock" gem "puma" +gem "rack-cors" gem "responders" gem "rollbar" gem "seedbank" diff --git a/Gemfile.lock b/Gemfile.lock index 94f9601..83970ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -167,6 +167,7 @@ GEM public_suffix (3.0.3) puma (3.12.0) rack (2.0.5) + rack-cors (1.0.2) rack-protection (2.0.3) rack rack-test (1.1.0) @@ -323,6 +324,7 @@ DEPENDENCIES letter_opener pg puma + rack-cors raddocs rails (= 5.2.1) rails-erd From f29fd6d456c5fd63299fcf77255c13e34ba32395 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 10 Sep 2018 15:30:45 +0300 Subject: [PATCH 19/26] Generate markdown API docs only --- Gemfile | 8 +-- Gemfile.lock | 18 ------ config/initializers/raddocs.rb | 3 - config/routes.rb | 2 - doc/api.md | 4 -- doc/api/v1/index.json | 77 ------------------------ doc/api/v1/index.md | 22 +++++++ doc/api/v1/profiles/delete_profile.json | 37 ------------ doc/api/v1/profiles/delete_profile.md | 50 +++++++++++++++ doc/api/v1/profiles/retrive_profile.json | 38 ------------ doc/api/v1/profiles/retrive_profile.md | 50 +++++++++++++++ doc/api/v1/profiles/update_profile.json | 48 --------------- doc/api/v1/profiles/update_profile.md | 59 ++++++++++++++++++ doc/api/v1/signup/create_user.json | 49 --------------- doc/api/v1/signup/create_user.md | 57 ++++++++++++++++++ doc/api/v1/tokens/create_token.json | 45 -------------- doc/api/v1/tokens/create_token.md | 55 +++++++++++++++++ doc/api/v1/users/list_users.json | 38 ------------ doc/api/v1/users/list_users.md | 76 +++++++++++++++++++++++ doc/api/v1/users/retrive_user.json | 42 ------------- doc/api/v1/users/retrive_user.md | 57 ++++++++++++++++++ spec/support/rspec_api_documentation.rb | 2 +- 22 files changed, 430 insertions(+), 407 deletions(-) delete mode 100644 config/initializers/raddocs.rb delete mode 100644 doc/api.md delete mode 100644 doc/api/v1/index.json create mode 100644 doc/api/v1/index.md delete mode 100644 doc/api/v1/profiles/delete_profile.json create mode 100644 doc/api/v1/profiles/delete_profile.md delete mode 100644 doc/api/v1/profiles/retrive_profile.json create mode 100644 doc/api/v1/profiles/retrive_profile.md delete mode 100644 doc/api/v1/profiles/update_profile.json create mode 100644 doc/api/v1/profiles/update_profile.md delete mode 100644 doc/api/v1/signup/create_user.json create mode 100644 doc/api/v1/signup/create_user.md delete mode 100644 doc/api/v1/tokens/create_token.json create mode 100644 doc/api/v1/tokens/create_token.md delete mode 100644 doc/api/v1/users/list_users.json create mode 100644 doc/api/v1/users/list_users.md delete mode 100644 doc/api/v1/users/retrive_user.json create mode 100644 doc/api/v1/users/retrive_user.md diff --git a/Gemfile b/Gemfile index 2a63540..860aab5 100644 --- a/Gemfile +++ b/Gemfile @@ -43,19 +43,17 @@ group :development, :test do end group :test do - gem "simplecov", require: false - gem "webmock", require: false - gem "database_cleaner" gem "email_spec" gem "json_matchers" gem "json_spec" + gem "rspec_api_documentation" gem "shoulda-matchers", require: false + gem "simplecov", require: false + gem "webmock", require: false end group :development, :test, :staging do gem "factory_bot_rails" gem "faker" - gem "raddocs" - gem "rspec_api_documentation" end diff --git a/Gemfile.lock b/Gemfile.lock index 83970ad..770eefa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,9 +96,6 @@ GEM thor (~> 0.19.1) globalid (0.4.1) activesupport (>= 4.2.0) - haml (5.0.4) - temple (>= 0.8.0) - tilt hashdiff (0.3.7) health_check (3.0.0) railties (>= 5.0) @@ -155,7 +152,6 @@ GEM msgpack (1.2.4) multi_json (1.13.1) mustache (1.0.5) - mustermann (1.0.3) nio4r (2.3.1) nokogiri (1.8.4) mini_portile2 (~> 2.3.0) @@ -168,14 +164,8 @@ GEM puma (3.12.0) rack (2.0.5) rack-cors (1.0.2) - rack-protection (2.0.3) - rack rack-test (1.1.0) rack (>= 1.0, < 3) - raddocs (2.2.0) - haml (>= 4.0) - json - sinatra (~> 2.0) rails (5.2.1) actioncable (= 5.2.1) actionmailer (= 5.2.1) @@ -262,11 +252,6 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - sinatra (2.0.3) - mustermann (~> 1.0) - rack (~> 2.0) - rack-protection (= 2.0.3) - tilt (~> 2.0) spring (2.0.2) activesupport (>= 4.2) spring-commands-rspec (1.0.4) @@ -281,10 +266,8 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - temple (0.8.0) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.8) tzinfo (1.2.5) thread_safe (~> 0.1) unicode-display_width (1.4.0) @@ -325,7 +308,6 @@ DEPENDENCIES pg puma rack-cors - raddocs rails (= 5.2.1) rails-erd responders diff --git a/config/initializers/raddocs.rb b/config/initializers/raddocs.rb deleted file mode 100644 index 20ddb9d..0000000 --- a/config/initializers/raddocs.rb +++ /dev/null @@ -1,3 +0,0 @@ -Raddocs.configure do |config| - config.docs_dir = "doc/api/v1" -end diff --git a/config/routes.rb b/config/routes.rb index ed8c2a3..55263d1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,4 @@ Rails.application.routes.draw do - mount Raddocs::App => "/api/docs" - namespace :v1, defaults: { format: "jsonapi" } do resources :tokens, only: :create resources :users, only: %i[index show] diff --git a/doc/api.md b/doc/api.md deleted file mode 100644 index 26fa90d..0000000 --- a/doc/api.md +++ /dev/null @@ -1,4 +0,0 @@ -Apitome Documentation -===================== - -This file was automatically generated, and can be found at `doc/api.md`. diff --git a/doc/api/v1/index.json b/doc/api/v1/index.json deleted file mode 100644 index 99fb42a..0000000 --- a/doc/api/v1/index.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "resources": [ - { - "name": "Profiles", - "explanation": null, - "examples": [ - { - "description": "Retrive Profile", - "link": "profiles/retrive_profile.json", - "groups": "all", - "route": "/v1/profile", - "method": "get" - }, - { - "description": "Update Profile", - "link": "profiles/update_profile.json", - "groups": "all", - "route": "/v1/profile", - "method": "patch" - }, - { - "description": "Delete Profile", - "link": "profiles/delete_profile.json", - "groups": "all", - "route": "/v1/profile", - "method": "delete" - } - ] - }, - { - "name": "SignUp", - "explanation": null, - "examples": [ - { - "description": "Create User", - "link": "signup/create_user.json", - "groups": "all", - "route": "/v1/sign_ups", - "method": "post" - } - ] - }, - { - "name": "Tokens", - "explanation": null, - "examples": [ - { - "description": "Create Token", - "link": "tokens/create_token.json", - "groups": "all", - "route": "/v1/tokens", - "method": "post" - } - ] - }, - { - "name": "Users", - "explanation": null, - "examples": [ - { - "description": "List Users", - "link": "users/list_users.json", - "groups": "all", - "route": "/v1/users", - "method": "get" - }, - { - "description": "Retrive User", - "link": "users/retrive_user.json", - "groups": "all", - "route": "/v1/users/:id", - "method": "get" - } - ] - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/index.md b/doc/api/v1/index.md new file mode 100644 index 0000000..0441ac6 --- /dev/null +++ b/doc/api/v1/index.md @@ -0,0 +1,22 @@ +# API Documentation + + +## Profiles + +* [Retrive Profile](profiles/retrive_profile.md) +* [Update Profile](profiles/update_profile.md) +* [Delete Profile](profiles/delete_profile.md) + +## SignUp + +* [Create User](signup/create_user.md) + +## Tokens + +* [Create Token](tokens/create_token.md) + +## Users + +* [List Users](users/list_users.md) +* [Retrive User](users/retrive_user.md) + diff --git a/doc/api/v1/profiles/delete_profile.json b/doc/api/v1/profiles/delete_profile.json deleted file mode 100644 index ae05447..0000000 --- a/doc/api/v1/profiles/delete_profile.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "resource": "Profiles", - "resource_explanation": null, - "http_method": "DELETE", - "route": "/v1/profile", - "description": "Delete Profile", - "explanation": null, - "parameters": [ - - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "DELETE", - "request_path": "/v1/profile", - "request_body": "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}", - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQyfQ.1KjEfxd9f4cPu4DrKcrKXIYdL6Hl_kzftBKHcT8qTrE" - }, - "request_query_parameters": { - }, - "request_content_type": "application/vnd.api+json", - "response_status": 200, - "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"142\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user6@example.com\",\n \"full-name\": \"Merna McKenzie\"\n }\n }\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}' -X DELETE \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQyfQ.1KjEfxd9f4cPu4DrKcrKXIYdL6Hl_kzftBKHcT8qTrE\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/profiles/delete_profile.md b/doc/api/v1/profiles/delete_profile.md new file mode 100644 index 0000000..cbb6667 --- /dev/null +++ b/doc/api/v1/profiles/delete_profile.md @@ -0,0 +1,50 @@ +# Profiles API + +## Delete Profile + +### DELETE /v1/profile +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTk2fQ.-T7y0owQ07dPDNWdFxKGXM1Jq0qJhy11WDkp2FgYhEk
+ +#### Route + +
DELETE /v1/profile
+ +#### Body + +
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}' -X DELETE \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTk2fQ.-T7y0owQ07dPDNWdFxKGXM1Jq0qJhy11WDkp2FgYhEk"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "196",
+    "type": "users",
+    "attributes": {
+      "email": "user6@example.com",
+      "full-name": "Irwin Fadel"
+    }
+  }
+}
diff --git a/doc/api/v1/profiles/retrive_profile.json b/doc/api/v1/profiles/retrive_profile.json deleted file mode 100644 index 6368db0..0000000 --- a/doc/api/v1/profiles/retrive_profile.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "resource": "Profiles", - "resource_explanation": null, - "http_method": "GET", - "route": "/v1/profile", - "description": "Retrive Profile", - "explanation": null, - "parameters": [ - - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "GET", - "request_path": "/v1/profile", - "request_body": null, - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM2fQ.9NwGwpMJ32dxuRAGO5_gIdTdwuX_LSZxCKVGpMUnGso" - }, - "request_query_parameters": { - "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null - }, - "request_content_type": "application/vnd.api+json", - "response_status": 200, - "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"136\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user1@example.com\",\n \"full-name\": \"Branden Flatley V\"\n }\n }\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl -g \"http://localhost:5000/v1/profile\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM2fQ.9NwGwpMJ32dxuRAGO5_gIdTdwuX_LSZxCKVGpMUnGso\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/profiles/retrive_profile.md b/doc/api/v1/profiles/retrive_profile.md new file mode 100644 index 0000000..51dbc1b --- /dev/null +++ b/doc/api/v1/profiles/retrive_profile.md @@ -0,0 +1,50 @@ +# Profiles API + +## Retrive Profile + +### GET /v1/profile +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkwfQ.RS-wWnVsQ4KrPv2Sic2P-FD6yCOi6GWgaJPhUPFn-wE
+ +#### Route + +
GET /v1/profile
+ +#### Query Parameters + +
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/profile" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkwfQ.RS-wWnVsQ4KrPv2Sic2P-FD6yCOi6GWgaJPhUPFn-wE"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "190",
+    "type": "users",
+    "attributes": {
+      "email": "user1@example.com",
+      "full-name": "Mrs. Wilburn Witting"
+    }
+  }
+}
diff --git a/doc/api/v1/profiles/update_profile.json b/doc/api/v1/profiles/update_profile.json deleted file mode 100644 index f0bc35d..0000000 --- a/doc/api/v1/profiles/update_profile.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "resource": "Profiles", - "resource_explanation": null, - "http_method": "PATCH", - "route": "/v1/profile", - "description": "Update Profile", - "explanation": null, - "parameters": [ - { - "name": "full_name", - "description": "full name" - }, - { - "name": "email", - "description": "email" - }, - { - "name": "password", - "description": "password" - } - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "PATCH", - "request_path": "/v1/profile", - "request_body": "{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":\"Example User Updated\",\"email\":\"user_updated@example.com\",\"password\":\"new_password\"}}}", - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM3fQ.ogYNTYnZR0T9zdRjXeOmyb35qnr5FHjnjY8L5HeyH2Q" - }, - "request_query_parameters": { - }, - "request_content_type": "application/vnd.api+json", - "response_status": 200, - "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"137\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user_updated@example.com\",\n \"full-name\": \"Wesley Rodriguez\"\n }\n }\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/profile\" -d '{\"data\":{\"type\":\"profile-requests\",\"attributes\":{\"full-name\":\"Example User Updated\",\"email\":\"user_updated@example.com\",\"password\":\"new_password\"}}}' -X PATCH \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTM3fQ.ogYNTYnZR0T9zdRjXeOmyb35qnr5FHjnjY8L5HeyH2Q\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/profiles/update_profile.md b/doc/api/v1/profiles/update_profile.md new file mode 100644 index 0000000..3908ae2 --- /dev/null +++ b/doc/api/v1/profiles/update_profile.md @@ -0,0 +1,59 @@ +# Profiles API + +## Update Profile + +### PATCH /v1/profile + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| full_name | full name | false | | +| email | email | false | | +| password | password | false | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkxfQ.SuNP-3dS6rgTJLoL8ywQoLZoKyNJ8ot4A4BXZpTF-Ek
+ +#### Route + +
PATCH /v1/profile
+ +#### Body + +
{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}' -X PATCH \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkxfQ.SuNP-3dS6rgTJLoL8ywQoLZoKyNJ8ot4A4BXZpTF-Ek"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "191",
+    "type": "users",
+    "attributes": {
+      "email": "user_updated@example.com",
+      "full-name": "Zachery Gottlieb Jr."
+    }
+  }
+}
diff --git a/doc/api/v1/signup/create_user.json b/doc/api/v1/signup/create_user.json deleted file mode 100644 index e09c743..0000000 --- a/doc/api/v1/signup/create_user.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "resource": "SignUp", - "resource_explanation": null, - "http_method": "POST", - "route": "/v1/sign_ups", - "description": "Create User", - "explanation": null, - "parameters": [ - { - "name": "full_name", - "description": "full name" - }, - { - "required": true, - "name": "email", - "description": "email" - }, - { - "required": true, - "name": "password", - "description": "password" - } - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "POST", - "request_path": "/v1/sign_ups", - "request_body": "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":\"Example User\",\"email\":\"user@example.com\",\"password\":\"123456\"}}}", - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json" - }, - "request_query_parameters": { - }, - "request_content_type": "application/vnd.api+json", - "response_status": 201, - "response_status_text": "Created", - "response_body": "{\n \"data\": {\n \"id\": \"143\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user@example.com\",\n \"full-name\": null\n }\n }\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/sign_ups\" -d '{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":\"Example User\",\"email\":\"user@example.com\",\"password\":\"123456\"}}}' -X POST \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/signup/create_user.md b/doc/api/v1/signup/create_user.md new file mode 100644 index 0000000..742a152 --- /dev/null +++ b/doc/api/v1/signup/create_user.md @@ -0,0 +1,57 @@ +# SignUp API + +## Create User + +### POST /v1/sign_ups + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| full_name | full name | false | | +| email | email | true | | +| password | password | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+ +#### Route + +
POST /v1/sign_ups
+ +#### Body + +
{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/sign_ups" -d '{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}' -X POST \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
201 Created
+ +#### Body + +
{
+  "data": {
+    "id": "197",
+    "type": "users",
+    "attributes": {
+      "email": "user@example.com",
+      "full-name": null
+    }
+  }
+}
diff --git a/doc/api/v1/tokens/create_token.json b/doc/api/v1/tokens/create_token.json deleted file mode 100644 index 9da71dd..0000000 --- a/doc/api/v1/tokens/create_token.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "resource": "Tokens", - "resource_explanation": null, - "http_method": "POST", - "route": "/v1/tokens", - "description": "Create Token", - "explanation": null, - "parameters": [ - { - "required": true, - "name": "email", - "description": "email" - }, - { - "required": true, - "name": "password", - "description": "password" - } - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "POST", - "request_path": "/v1/tokens", - "request_body": "{\"data\":{\"type\":\"token-requests\",\"attributes\":{\"email\":\"user@example.com\",\"password\":\"123456\"}}}", - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json" - }, - "request_query_parameters": { - }, - "request_content_type": "application/vnd.api+json", - "response_status": 201, - "response_status_text": "Created", - "response_body": "{\n \"data\": {\n \"id\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQ1fQ.J_9o_xb83OUdY64BDC6u7gwYH_TLOK50Xv_FctdHtX8\",\n \"type\": \"jwt-tokens\",\n \"attributes\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTQ1fQ.J_9o_xb83OUdY64BDC6u7gwYH_TLOK50Xv_FctdHtX8\"\n }\n }\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl \"http://localhost:5000/v1/tokens\" -d '{\"data\":{\"type\":\"token-requests\",\"attributes\":{\"email\":\"user@example.com\",\"password\":\"123456\"}}}' -X POST \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/tokens/create_token.md b/doc/api/v1/tokens/create_token.md new file mode 100644 index 0000000..1d6ad0a --- /dev/null +++ b/doc/api/v1/tokens/create_token.md @@ -0,0 +1,55 @@ +# Tokens API + +## Create Token + +### POST /v1/tokens + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| email | email | true | | +| password | password | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+ +#### Route + +
POST /v1/tokens
+ +#### Body + +
{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/tokens" -d '{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}' -X POST \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
201 Created
+ +#### Body + +
{
+  "data": {
+    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MTk5fQ.lI4GEJbt0boQZ_nM1qCZk_lebN5576ai-BkN9Bw8F8A",
+    "type": "jwt-tokens",
+    "attributes": {
+      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MTk5fQ.lI4GEJbt0boQZ_nM1qCZk_lebN5576ai-BkN9Bw8F8A"
+    }
+  }
+}
diff --git a/doc/api/v1/users/list_users.json b/doc/api/v1/users/list_users.json deleted file mode 100644 index 018d08b..0000000 --- a/doc/api/v1/users/list_users.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "resource": "Users", - "resource_explanation": null, - "http_method": "GET", - "route": "/v1/users", - "description": "List Users", - "explanation": null, - "parameters": [ - - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "GET", - "request_path": "/v1/users", - "request_body": null, - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTUwfQ.pLEe8_cz-Mnx8lUyKOQWSDpgm6KIgiCSOqBgpL85V-A" - }, - "request_query_parameters": { - "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null - }, - "request_content_type": "application/vnd.api+json", - "response_status": 200, - "response_status_text": "OK", - "response_body": "{\n \"data\": [\n {\n \"id\": \"147\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user7@example.com\",\n \"full-name\": \"Clarence Rice\"\n }\n },\n {\n \"id\": \"148\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user8@example.com\",\n \"full-name\": \"Carlton Connelly\"\n }\n },\n {\n \"id\": \"149\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user9@example.com\",\n \"full-name\": \"Mr. Candy Koch\"\n }\n },\n {\n \"id\": \"150\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user10@example.com\",\n \"full-name\": \"Connie Roberts\"\n }\n }\n ]\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl -g \"http://localhost:5000/v1/users\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzIsInN1YiI6MTUwfQ.pLEe8_cz-Mnx8lUyKOQWSDpgm6KIgiCSOqBgpL85V-A\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/users/list_users.md b/doc/api/v1/users/list_users.md new file mode 100644 index 0000000..e330c7b --- /dev/null +++ b/doc/api/v1/users/list_users.md @@ -0,0 +1,76 @@ +# Users API + +## List Users + +### GET /v1/users +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA0fQ.jNbuAOt54xUm0PFUDbASg1F8rdMc9p9qhnTfQDkhHyg
+ +#### Route + +
GET /v1/users
+ +#### Query Parameters + +
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/users" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA0fQ.jNbuAOt54xUm0PFUDbASg1F8rdMc9p9qhnTfQDkhHyg"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": [
+    {
+      "id": "201",
+      "type": "users",
+      "attributes": {
+        "email": "user7@example.com",
+        "full-name": "Launa Leuschke Jr."
+      }
+    },
+    {
+      "id": "202",
+      "type": "users",
+      "attributes": {
+        "email": "user8@example.com",
+        "full-name": "Miss Renae Nienow"
+      }
+    },
+    {
+      "id": "203",
+      "type": "users",
+      "attributes": {
+        "email": "user9@example.com",
+        "full-name": "Anthony Kassulke"
+      }
+    },
+    {
+      "id": "204",
+      "type": "users",
+      "attributes": {
+        "email": "user10@example.com",
+        "full-name": "Quentin Okuneva"
+      }
+    }
+  ]
+}
diff --git a/doc/api/v1/users/retrive_user.json b/doc/api/v1/users/retrive_user.json deleted file mode 100644 index e89a7a0..0000000 --- a/doc/api/v1/users/retrive_user.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "resource": "Users", - "resource_explanation": null, - "http_method": "GET", - "route": "/v1/users/:id", - "description": "Retrive User", - "explanation": null, - "parameters": [ - { - "required": true, - "name": "id", - "description": "user id" - } - ], - "response_fields": [ - - ], - "requests": [ - { - "request_method": "GET", - "request_path": "/v1/users/151", - "request_body": null, - "request_headers": { - "Content-Type": "application/vnd.api+json", - "Accept": "application/vnd.api+json", - "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzMsInN1YiI6MTUyfQ.bP7_CXiVL1Q3TihIGr0NwNQSnLQAJjB4ChiE88_xp2Q" - }, - "request_query_parameters": { - "{\"data\":{\"type\":\"user-requests\",\"attributes\":{\"full-name\":null,\"email\":null,\"password\":null}}}": null - }, - "request_content_type": "application/vnd.api+json", - "response_status": 200, - "response_status_text": "OK", - "response_body": "{\n \"data\": {\n \"id\": \"151\",\n \"type\": \"users\",\n \"attributes\": {\n \"email\": \"user11@example.com\",\n \"full-name\": \"Doria Sporer\"\n }\n }\n}", - "response_headers": { - "Content-Type": "application/vnd.api+json; charset=utf-8" - }, - "response_content_type": "application/vnd.api+json; charset=utf-8", - "curl": "curl -g \"http://localhost:5000/v1/users/151\" -X GET \\\n\t-H \"Content-Type: application/vnd.api+json\" \\\n\t-H \"Accept: application/vnd.api+json\" \\\n\t-H \"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2NTgxMzMsInN1YiI6MTUyfQ.bP7_CXiVL1Q3TihIGr0NwNQSnLQAJjB4ChiE88_xp2Q\"" - } - ] -} \ No newline at end of file diff --git a/doc/api/v1/users/retrive_user.md b/doc/api/v1/users/retrive_user.md new file mode 100644 index 0000000..25d1943 --- /dev/null +++ b/doc/api/v1/users/retrive_user.md @@ -0,0 +1,57 @@ +# Users API + +## Retrive User + +### GET /v1/users/:id + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| id | user id | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA2fQ.GYofTAg3S6-1fdkDd9w2OnGw3OhGh5zuTSDw5JMtkPc
+ +#### Route + +
GET /v1/users/205
+ +#### Query Parameters + +
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/users/205" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA2fQ.GYofTAg3S6-1fdkDd9w2OnGw3OhGh5zuTSDw5JMtkPc"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
200 OK
+ +#### Body + +
{
+  "data": {
+    "id": "205",
+    "type": "users",
+    "attributes": {
+      "email": "user11@example.com",
+      "full-name": "Marnie Nicolas"
+    }
+  }
+}
diff --git a/spec/support/rspec_api_documentation.rb b/spec/support/rspec_api_documentation.rb index 2db8a1b..3707899 100644 --- a/spec/support/rspec_api_documentation.rb +++ b/spec/support/rspec_api_documentation.rb @@ -3,7 +3,7 @@ # rubocop:disable Style/WordArray RspecApiDocumentation.configure do |config| - config.format = :JSON + config.format = :markdown config.docs_dir = Rails.root.join("doc", "api", "v1") config.request_headers_to_include = ["Accept", "Content-Type", "Authorization"] config.response_headers_to_include = ["Content-Type"] From 2a79e79473f68b57d4da321758ba10aa50f17dc4 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Tue, 11 Sep 2018 16:37:10 +0300 Subject: [PATCH 20/26] Errors refactor --- Gemfile | 6 ++-- Gemfile.lock | 4 +++ app/controllers/v1/base_controller.rb | 31 ++++++---------- app/controllers/v1/sign_ups_controller.rb | 2 +- app/controllers/v1/tokens_controller.rb | 4 +-- app/interactors/create_jwt.rb | 2 +- app/models/error.rb | 35 +++++++++++++++--- app/serializers/error_serializer.rb | 3 ++ config/locales/errors.en.yml | 10 +++--- config/locales/interactors.en.yml | 9 ----- spec/api/v1/auth_spec.rb | 15 ++++++++ spec/api/v1/profiles_spec.rb | 40 +++++++-------------- spec/api/v1/sign_ups_spec.rb | 36 ++----------------- spec/api/v1/tokens_spec.rb | 9 +++-- spec/api/v1/users_spec.rb | 15 +++++--- spec/models/error_spec.rb | 14 ++++++++ spec/schemas/v1/definitions.json | 1 - spec/schemas/v1/{error.json => errors.json} | 0 spec/schemas/v1/jsonapi.json | 1 + 19 files changed, 124 insertions(+), 113 deletions(-) create mode 100644 app/serializers/error_serializer.rb delete mode 100644 config/locales/interactors.en.yml create mode 100644 spec/api/v1/auth_spec.rb create mode 100644 spec/models/error_spec.rb rename spec/schemas/v1/{error.json => errors.json} (100%) diff --git a/Gemfile b/Gemfile index 860aab5..fa031ee 100644 --- a/Gemfile +++ b/Gemfile @@ -32,12 +32,12 @@ group :development do end group :development, :test do + gem "brakeman" + gem "bundler-audit" gem "byebug" gem "dotenv-rails" + gem "rspec-its" gem "rspec-rails" - - gem "brakeman" - gem "bundler-audit" gem "rubocop" gem "rubocop-rspec" end diff --git a/Gemfile.lock b/Gemfile.lock index 770eefa..8ccee72 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -214,6 +214,9 @@ GEM rspec-expectations (3.8.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) + rspec-its (1.2.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) @@ -312,6 +315,7 @@ DEPENDENCIES rails-erd responders rollbar + rspec-its rspec-rails rspec_api_documentation rubocop diff --git a/app/controllers/v1/base_controller.rb b/app/controllers/v1/base_controller.rb index 82011c1..27ffa3b 100644 --- a/app/controllers/v1/base_controller.rb +++ b/app/controllers/v1/base_controller.rb @@ -2,15 +2,16 @@ module V1 class BaseController < ActionController::API include Knock::Authenticable - before_action :authenticate_user + before_action :authenticate_user! - rescue_from ActiveRecord::RecordNotFound, with: :respond_with_record_not_found - rescue_from ActionController::RoutingError, with: :respond_with_route_not_found + rescue_from ActiveRecord::RecordNotFound do |exception| + respond_with_error(:record_not_found) + end private - def authenticate_user - respond_with_unauthorized if current_user.blank? + def authenticate_user! + respond_with_error(:unauthorized) if current_user.blank? end def current_user @@ -25,24 +26,14 @@ def respond_with_resources(resources, include: nil, fields: nil) respond_with_resource(resources, include: include, location: nil, fields: fields) end - def respond_with_resource_errors(resource) + def respond_with_resource_errors(resource, code: :unprocessable_entity) render jsonapi: resource, serializer: ActiveModel::Serializer::ErrorSerializer, status: :unprocessable_entity end - def respond_with_error(message, status: :unprocessable_entity) - render jsonapi: Error.new(base: message), serializer: ActiveModel::Serializer::ErrorSerializer, status: status - end - - def respond_with_unauthorized - respond_with_error(I18n.t("interactors.authenticate.invalid_credentials"), status: :unauthorized) - end - - def respond_with_record_not_found - respond_with_error(I18n.t("generic_errors.record_not_found"), status: :not_found) - end - - def respond_with_route_not_found - respond_with_error(I18n.t("generic_errors.route_not_found"), status: :not_found) + def respond_with_error(code) + Error.new(code: code).tap do |error| + render json: error.to_json, status: error.status + end end def jsonapi_params(options) diff --git a/app/controllers/v1/sign_ups_controller.rb b/app/controllers/v1/sign_ups_controller.rb index 482e188..37d36bf 100644 --- a/app/controllers/v1/sign_ups_controller.rb +++ b/app/controllers/v1/sign_ups_controller.rb @@ -1,6 +1,6 @@ module V1 class SignUpsController < V1::BaseController - skip_before_action :authenticate_user + skip_before_action :authenticate_user! expose :user diff --git a/app/controllers/v1/tokens_controller.rb b/app/controllers/v1/tokens_controller.rb index f6b8d21..e215651 100644 --- a/app/controllers/v1/tokens_controller.rb +++ b/app/controllers/v1/tokens_controller.rb @@ -1,6 +1,6 @@ module V1 class TokensController < V1::BaseController - skip_before_action :authenticate_user + skip_before_action :authenticate_user! def create result = CreateJwt.call(authentication_params) @@ -8,7 +8,7 @@ def create if result.success? respond_with_resource(result.jwt_token, status: :created, location: nil) else - respond_with_error(result.message) + respond_with_error(:invalid_credentials) end end diff --git a/app/interactors/create_jwt.rb b/app/interactors/create_jwt.rb index c137f01..17e8a9e 100644 --- a/app/interactors/create_jwt.rb +++ b/app/interactors/create_jwt.rb @@ -4,7 +4,7 @@ class CreateJwt delegate :email, :password, to: :context def call - context.fail!(message: I18n.t("interactors.create_jwt.invalid_credentials")) unless authenticated? + context.fail! unless authenticated? context.jwt_token = jwt_token end diff --git a/app/models/error.rb b/app/models/error.rb index 8a2600e..10ea940 100644 --- a/app/models/error.rb +++ b/app/models/error.rb @@ -1,8 +1,35 @@ +require "securerandom" + class Error - attr_reader :errors + include ActiveModel::Model + include ActiveModel::Serialization + + CODES_TO_STATUS = { + invalid_credentials: :unprocessable_entity, + unauthorized: :unauthorized, + record_not_found: :not_found, + route_not_found: :not_found, + custom_error: :internal_server_error + } + + attr_accessor :code, :detail + + def attributes + { + code: code, + detail: detail, + } + end + + def detail + I18n.t("errors.#{code}") + end + + def status + CODES_TO_STATUS[code] + end - def initialize(messages = {}) - @errors = ActiveModel::Errors.new(self) - messages.each { |attribute, message| @errors.add(attribute, message) } + def to_json + ActiveModelSerializers::SerializableResource.new([self], adapter: :json).to_json end end diff --git a/app/serializers/error_serializer.rb b/app/serializers/error_serializer.rb new file mode 100644 index 0000000..1cd490b --- /dev/null +++ b/app/serializers/error_serializer.rb @@ -0,0 +1,3 @@ +class ErrorSerializer < ActiveModel::Serializer::ErrorSerializer + attributes :code, :detail +end diff --git a/config/locales/errors.en.yml b/config/locales/errors.en.yml index 6e2faab..5ce9f63 100644 --- a/config/locales/errors.en.yml +++ b/config/locales/errors.en.yml @@ -1,5 +1,7 @@ en: - generic_errors: - record_not_found: "Record not found" - route_not_found: "Route not found" - + errors: + invalid_credentials: Invalid credentials + unauthorized: Authorization required + record_not_found: Record not found + route_not_found: Route not found + custom_error: Custom error message diff --git a/config/locales/interactors.en.yml b/config/locales/interactors.en.yml deleted file mode 100644 index 6e1e1b9..0000000 --- a/config/locales/interactors.en.yml +++ /dev/null @@ -1,9 +0,0 @@ -en: - interactors: - authenticate_user: - success: Successfully authenticated. - create_jwt: - success: Successfully authenticated. - invalid_credentials: Invalid credentials. - authenticate: - invalid_credentials: Invalid credentials. diff --git a/spec/api/v1/auth_spec.rb b/spec/api/v1/auth_spec.rb new file mode 100644 index 0000000..086c836 --- /dev/null +++ b/spec/api/v1/auth_spec.rb @@ -0,0 +1,15 @@ +require "rails_helper" +require "rspec_api_documentation/dsl" + +resource "Authorization" do + include_context "with JSON API Headers" + + get "/v1/profile" do + example "Request without authorization" do + do_request + + expect(response_status).to eq 401 + expect(response_body).to match_response_schema("v1/errors") + end + end +end diff --git a/spec/api/v1/profiles_spec.rb b/spec/api/v1/profiles_spec.rb index 8661dcb..91f6719 100644 --- a/spec/api/v1/profiles_spec.rb +++ b/spec/api/v1/profiles_spec.rb @@ -16,7 +16,9 @@ class ProfileRequestSerializer < ApplicationSerializer get "/v1/profile" do let(:request_class) { "profile_request" } - example_request "Retrive Profile" do + example "Retrive Profile" do + do_request + expect(response_status).to eq(200) expect(response_body).to match_response_schema("v1/users") end @@ -32,40 +34,22 @@ class ProfileRequestSerializer < ApplicationSerializer let(:password) { "new_password" } let(:request_class) { "profile_request" } - example_request "Update Profile" do + example "Update Profile" do + do_request + expect(response_status).to eq(200) expect(response_body).to match_response_schema("v1/user") end - context "with invalid email" do + context "with invalid data" do + let(:password) { "" } let(:email) { "invalid" } - example "Update Profile with invalid email", document: false do + example "Update Profile with empty password and invalid email" do do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") - end - end - - context "with short password" do - let(:password) { "short" } - example "Update Profile with invalid email", document: false do - do_request expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") - end - end - - context "when user already exists" do - before do - create :user, email: email - end - - example "Update Profile with existing email", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") + expect(response_body).to match_response_schema("v1/errors") end end end @@ -73,7 +57,9 @@ class ProfileRequestSerializer < ApplicationSerializer delete "/v1/profile" do let(:request_class) { "profile_request" } - example_request "Delete Profile" do + example "Delete Profile" do + do_request + expect(response_status).to eq(200) expect(response_body).to match_response_schema("v1/user") end diff --git a/spec/api/v1/sign_ups_spec.rb b/spec/api/v1/sign_ups_spec.rb index 907cec3..6dec09e 100644 --- a/spec/api/v1/sign_ups_spec.rb +++ b/spec/api/v1/sign_ups_spec.rb @@ -22,41 +22,11 @@ class SignUpRequestSerializer < ApplicationSerializer let(:password) { "123456" } let(:request_class) { "user_request" } - example_request "Create User" do + example "Create User" do + do_request + expect(response_status).to eq(201) expect(response_body).to match_response_schema("v1/user") end - - context "with invalid email" do - let(:email) { "invalid" } - - example "Create User with invalid email", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") - end - end - - context "with blank password" do - let(:password) { "" } - - example "Create User with invalid password", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") - end - end - - context "when user already exists" do - before do - create :user, email: email - end - - example "Create User with existing email", document: false do - do_request - expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") - end - end end end diff --git a/spec/api/v1/tokens_spec.rb b/spec/api/v1/tokens_spec.rb index 6bafc28..8b36ff1 100644 --- a/spec/api/v1/tokens_spec.rb +++ b/spec/api/v1/tokens_spec.rb @@ -25,7 +25,9 @@ class TokenRequestSerializer < ApplicationSerializer create :user, email: email, password: "123456" end - example_request "Create Token" do + example "Create Token" do + do_request + expect(response_status).to eq(201) expect(response_body).to match_response_schema("v1/jwt_token") end @@ -33,10 +35,11 @@ class TokenRequestSerializer < ApplicationSerializer context "with invalid password" do let(:password) { "invalid" } - example "Create Token with invalid password", document: false do + example "Create Token with invalid password"do do_request + expect(response_status).to eq(422) - expect(response_body).to match_response_schema("v1/error") + expect(response_body).to match_response_schema("v1/errors") end end end diff --git a/spec/api/v1/users_spec.rb b/spec/api/v1/users_spec.rb index 7e70c1d..ea81f33 100644 --- a/spec/api/v1/users_spec.rb +++ b/spec/api/v1/users_spec.rb @@ -20,7 +20,9 @@ class UserRequestSerializer < ApplicationSerializer create_list :user, 3 end - example_request "List Users" do + example "List Users" do + do_request + expect(response_status).to eq(200) expect(response_body).to match_response_schema("v1/users") end @@ -33,18 +35,21 @@ class UserRequestSerializer < ApplicationSerializer let(:id) { user.id } let(:request_class) { "user_request" } - example_request "Retrive User" do + example "Retrive User" do + do_request + expect(response_status).to eq(200) expect(response_body).to match_response_schema("v1/user") end context "with invalid id" do - let(:id) { 332 } + let(:id) { 0 } - example "Retrive User with invalid id", document: false do + example "Retrive User with invalid id"do do_request + expect(response_status).to eq(404) - expect(response_body).to match_response_schema("v1/error") + expect(response_body).to match_response_schema("v1/errors") end end end diff --git a/spec/models/error_spec.rb b/spec/models/error_spec.rb new file mode 100644 index 0000000..3ef9885 --- /dev/null +++ b/spec/models/error_spec.rb @@ -0,0 +1,14 @@ +require "rails_helper" + +describe Error do + subject(:error) { described_class.new(code: :custom_error) } + + it "serializable resource" do + expect { ActiveModelSerializers::SerializableResource.new(error).to_json } + .not_to raise_error + end + + its(:code) { is_expected.to eql(:custom_error) } + its(:status) { is_expected.to eql(:internal_server_error) } + its(:detail) { is_expected.to eql("Custom error message") } +end diff --git a/spec/schemas/v1/definitions.json b/spec/schemas/v1/definitions.json index 2ca26a4..4eeae75 100644 --- a/spec/schemas/v1/definitions.json +++ b/spec/schemas/v1/definitions.json @@ -15,7 +15,6 @@ } }, - "jwt_token": { "required": ["attributes"], "properties": { diff --git a/spec/schemas/v1/error.json b/spec/schemas/v1/errors.json similarity index 100% rename from spec/schemas/v1/error.json rename to spec/schemas/v1/errors.json diff --git a/spec/schemas/v1/jsonapi.json b/spec/schemas/v1/jsonapi.json index 201a691..a79d271 100644 --- a/spec/schemas/v1/jsonapi.json +++ b/spec/schemas/v1/jsonapi.json @@ -333,6 +333,7 @@ "error": { "type": "object", + "required": ["id", "status", "code", "title"], "properties": { "id": { "description": "A unique identifier for this particular occurrence of the problem.", From 435a6885842350dee405afcfaa225d8eb7acd07e Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Tue, 11 Sep 2018 16:40:09 +0300 Subject: [PATCH 21/26] Fix quality issues --- app/controllers/v1/base_controller.rb | 6 +++--- app/models/error.rb | 6 +++--- spec/api/v1/tokens_spec.rb | 2 +- spec/api/v1/users_spec.rb | 2 +- spec/models/error_spec.rb | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/v1/base_controller.rb b/app/controllers/v1/base_controller.rb index 27ffa3b..672e8d8 100644 --- a/app/controllers/v1/base_controller.rb +++ b/app/controllers/v1/base_controller.rb @@ -4,7 +4,7 @@ class BaseController < ActionController::API before_action :authenticate_user! - rescue_from ActiveRecord::RecordNotFound do |exception| + rescue_from ActiveRecord::RecordNotFound do |_exception| respond_with_error(:record_not_found) end @@ -26,8 +26,8 @@ def respond_with_resources(resources, include: nil, fields: nil) respond_with_resource(resources, include: include, location: nil, fields: fields) end - def respond_with_resource_errors(resource, code: :unprocessable_entity) - render jsonapi: resource, serializer: ActiveModel::Serializer::ErrorSerializer, status: :unprocessable_entity + def respond_with_resource_errors(resource, status: :unprocessable_entity) + render jsonapi: resource, serializer: ActiveModel::Serializer::ErrorSerializer, status: status end def respond_with_error(code) diff --git a/app/models/error.rb b/app/models/error.rb index 10ea940..267dd54 100644 --- a/app/models/error.rb +++ b/app/models/error.rb @@ -10,14 +10,14 @@ class Error record_not_found: :not_found, route_not_found: :not_found, custom_error: :internal_server_error - } + }.freeze - attr_accessor :code, :detail + attr_accessor :code def attributes { code: code, - detail: detail, + detail: detail } end diff --git a/spec/api/v1/tokens_spec.rb b/spec/api/v1/tokens_spec.rb index 8b36ff1..4c04439 100644 --- a/spec/api/v1/tokens_spec.rb +++ b/spec/api/v1/tokens_spec.rb @@ -35,7 +35,7 @@ class TokenRequestSerializer < ApplicationSerializer context "with invalid password" do let(:password) { "invalid" } - example "Create Token with invalid password"do + example "Create Token with invalid password" do do_request expect(response_status).to eq(422) diff --git a/spec/api/v1/users_spec.rb b/spec/api/v1/users_spec.rb index ea81f33..ed8c5f7 100644 --- a/spec/api/v1/users_spec.rb +++ b/spec/api/v1/users_spec.rb @@ -45,7 +45,7 @@ class UserRequestSerializer < ApplicationSerializer context "with invalid id" do let(:id) { 0 } - example "Retrive User with invalid id"do + example "Retrive User with invalid id" do do_request expect(response_status).to eq(404) diff --git a/spec/models/error_spec.rb b/spec/models/error_spec.rb index 3ef9885..00b15c0 100644 --- a/spec/models/error_spec.rb +++ b/spec/models/error_spec.rb @@ -8,7 +8,7 @@ .not_to raise_error end - its(:code) { is_expected.to eql(:custom_error) } - its(:status) { is_expected.to eql(:internal_server_error) } + its(:code) { is_expected.to be(:custom_error) } + its(:status) { is_expected.to be(:internal_server_error) } its(:detail) { is_expected.to eql("Custom error message") } end From 12ddb5e487c8d2b89a10d99282742ce25125bb74 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Tue, 11 Sep 2018 17:03:34 +0300 Subject: [PATCH 22/26] Rename SignUps to Registrations --- ...troller.rb => registrations_controller.rb} | 2 +- .../initializers/active_model_serializer.rb | 1 + config/routes.rb | 4 +- .../request_without_authorization.md | 42 ++++++++++++ doc/api/v1/index.md | 11 +++- doc/api/v1/profiles/delete_profile.md | 14 ++-- doc/api/v1/profiles/retrive_profile.md | 10 +-- doc/api/v1/profiles/update_profile.md | 12 ++-- ...e_with_empty_password_and_invalid_email.md | 65 +++++++++++++++++++ .../{signup => registration}/create_user.md | 14 ++-- doc/api/v1/tokens/create_token.md | 10 +-- .../create_token_with_invalid_password.md | 54 +++++++++++++++ doc/api/v1/users/list_users.md | 30 ++++----- doc/api/v1/users/retrive_user.md | 16 ++--- .../v1/users/retrive_user_with_invalid_id.md | 55 ++++++++++++++++ ...sign_ups_spec.rb => registrations_spec.rb} | 10 +-- spec/schemas/v1/definitions.json | 4 +- 17 files changed, 289 insertions(+), 65 deletions(-) rename app/controllers/v1/{sign_ups_controller.rb => registrations_controller.rb} (87%) create mode 100644 doc/api/v1/authorization/request_without_authorization.md create mode 100644 doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md rename doc/api/v1/{signup => registration}/create_user.md (54%) create mode 100644 doc/api/v1/tokens/create_token_with_invalid_password.md create mode 100644 doc/api/v1/users/retrive_user_with_invalid_id.md rename spec/api/v1/{sign_ups_spec.rb => registrations_spec.rb} (74%) diff --git a/app/controllers/v1/sign_ups_controller.rb b/app/controllers/v1/registrations_controller.rb similarity index 87% rename from app/controllers/v1/sign_ups_controller.rb rename to app/controllers/v1/registrations_controller.rb index 37d36bf..ff35569 100644 --- a/app/controllers/v1/sign_ups_controller.rb +++ b/app/controllers/v1/registrations_controller.rb @@ -1,5 +1,5 @@ module V1 - class SignUpsController < V1::BaseController + class RegistrationsController < V1::BaseController skip_before_action :authenticate_user! expose :user diff --git a/config/initializers/active_model_serializer.rb b/config/initializers/active_model_serializer.rb index 74f6cba..ed897a8 100644 --- a/config/initializers/active_model_serializer.rb +++ b/config/initializers/active_model_serializer.rb @@ -1,4 +1,5 @@ ActiveModelSerializers.config.adapter = :json_api +ActiveModelSerializers.config.key_transform = :underscore ActiveSupport.on_load(:action_controller) do require "active_model_serializers/register_jsonapi_renderer" diff --git a/config/routes.rb b/config/routes.rb index 55263d1..2565c29 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,8 @@ Rails.application.routes.draw do namespace :v1, defaults: { format: "jsonapi" } do + resources :registrations, only: :create resources :tokens, only: :create - resources :users, only: %i[index show] - resources :sign_ups, only: :create resource :profile, only: %i[show update destroy] + resources :users, only: %i[index show] end end diff --git a/doc/api/v1/authorization/request_without_authorization.md b/doc/api/v1/authorization/request_without_authorization.md new file mode 100644 index 0000000..1f732d0 --- /dev/null +++ b/doc/api/v1/authorization/request_without_authorization.md @@ -0,0 +1,42 @@ +# Authorization API + +## Request without authorization + +### GET /v1/profile +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+ +#### Route + +
GET /v1/profile
+ +#### cURL + +
curl -g "http://localhost:5000/v1/profile" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json"
+ +### Response + +#### Headers + +
Content-Type: application/json; charset=utf-8
+ +#### Status + +
401 Unauthorized
+ +#### Body + +
{
+  "errors": [
+    {
+      "code": "unauthorized",
+      "detail": "Authorization required"
+    }
+  ]
+}
diff --git a/doc/api/v1/index.md b/doc/api/v1/index.md index 0441ac6..1d29132 100644 --- a/doc/api/v1/index.md +++ b/doc/api/v1/index.md @@ -1,22 +1,29 @@ # API Documentation +## Authorization + +* [Request without authorization](authorization/request_without_authorization.md) + ## Profiles * [Retrive Profile](profiles/retrive_profile.md) * [Update Profile](profiles/update_profile.md) +* [Update Profile with empty password and invalid email](profiles/update_profile_with_empty_password_and_invalid_email.md) * [Delete Profile](profiles/delete_profile.md) -## SignUp +## Registration -* [Create User](signup/create_user.md) +* [Create User](registration/create_user.md) ## Tokens * [Create Token](tokens/create_token.md) +* [Create Token with invalid password](tokens/create_token_with_invalid_password.md) ## Users * [List Users](users/list_users.md) * [Retrive User](users/retrive_user.md) +* [Retrive User with invalid id](users/retrive_user_with_invalid_id.md) diff --git a/doc/api/v1/profiles/delete_profile.md b/doc/api/v1/profiles/delete_profile.md index cbb6667..d8e5e92 100644 --- a/doc/api/v1/profiles/delete_profile.md +++ b/doc/api/v1/profiles/delete_profile.md @@ -9,7 +9,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTk2fQ.-T7y0owQ07dPDNWdFxKGXM1Jq0qJhy11WDkp2FgYhEk
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUyfQ.BTwgCHI3lIP3-vKTRu147lGDXclVYSld93mssUDfzIY #### Route @@ -17,14 +17,14 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M #### Body -
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}
+
{"data":{"type":"profile_requests","attributes":{"full_name":null,"email":null,"password":null}}}
#### cURL -
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}' -X DELETE \
+
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile_requests","attributes":{"full_name":null,"email":null,"password":null}}}' -X DELETE \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTk2fQ.-T7y0owQ07dPDNWdFxKGXM1Jq0qJhy11WDkp2FgYhEk"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUyfQ.BTwgCHI3lIP3-vKTRu147lGDXclVYSld93mssUDfzIY"
### Response @@ -40,11 +40,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M
{
   "data": {
-    "id": "196",
+    "id": "152",
     "type": "users",
     "attributes": {
-      "email": "user6@example.com",
-      "full-name": "Irwin Fadel"
+      "email": "user4@example.com",
+      "full_name": "Armando Gaylord"
     }
   }
 }
diff --git a/doc/api/v1/profiles/retrive_profile.md b/doc/api/v1/profiles/retrive_profile.md index 51dbc1b..0a8103d 100644 --- a/doc/api/v1/profiles/retrive_profile.md +++ b/doc/api/v1/profiles/retrive_profile.md @@ -9,7 +9,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkwfQ.RS-wWnVsQ4KrPv2Sic2P-FD6yCOi6GWgaJPhUPFn-wE
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTQ5fQ.7vtVLtJ-jHlXvxEF2-vRS4U8IZCbiZ4wYbYo5DmZoWE #### Route @@ -17,14 +17,14 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M #### Query Parameters -
{"data":{"type":"profile-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+
{"data":{"type":"profile_requests","attributes":{"full_name":null,"email":null,"password":null}}}: 
#### cURL
curl -g "http://localhost:5000/v1/profile" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkwfQ.RS-wWnVsQ4KrPv2Sic2P-FD6yCOi6GWgaJPhUPFn-wE"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTQ5fQ.7vtVLtJ-jHlXvxEF2-vRS4U8IZCbiZ4wYbYo5DmZoWE" ### Response @@ -40,11 +40,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M
{
   "data": {
-    "id": "190",
+    "id": "149",
     "type": "users",
     "attributes": {
       "email": "user1@example.com",
-      "full-name": "Mrs. Wilburn Witting"
+      "full_name": "Mr. Shoshana Jacobi"
     }
   }
 }
diff --git a/doc/api/v1/profiles/update_profile.md b/doc/api/v1/profiles/update_profile.md index 3908ae2..c62d6a0 100644 --- a/doc/api/v1/profiles/update_profile.md +++ b/doc/api/v1/profiles/update_profile.md @@ -18,7 +18,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkxfQ.SuNP-3dS6rgTJLoL8ywQoLZoKyNJ8ot4A4BXZpTF-Ek
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUwfQ.uxN8Lf0W758tV8-_7qcVQGolo1VFu3E-gePIc8qzL5Q #### Route @@ -26,14 +26,14 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M #### Body -
{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}
+
{"data":{"type":"profile_requests","attributes":{"full_name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}
#### cURL -
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile-requests","attributes":{"full-name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}' -X PATCH \
+
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile_requests","attributes":{"full_name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}' -X PATCH \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjIsInN1YiI6MTkxfQ.SuNP-3dS6rgTJLoL8ywQoLZoKyNJ8ot4A4BXZpTF-Ek"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUwfQ.uxN8Lf0W758tV8-_7qcVQGolo1VFu3E-gePIc8qzL5Q"
### Response @@ -49,11 +49,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M
{
   "data": {
-    "id": "191",
+    "id": "150",
     "type": "users",
     "attributes": {
       "email": "user_updated@example.com",
-      "full-name": "Zachery Gottlieb Jr."
+      "full_name": "Example User Updated"
     }
   }
 }
diff --git a/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md b/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md new file mode 100644 index 0000000..3521b44 --- /dev/null +++ b/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md @@ -0,0 +1,65 @@ +# Profiles API + +## Update Profile with empty password and invalid email + +### PATCH /v1/profile + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| full_name | full name | false | | +| email | email | false | | +| password | password | false | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUxfQ.99LIkGZJpmnaRN5QXS-yhKv8LsIGUePOGiES0aU8J1w
+ +#### Route + +
PATCH /v1/profile
+ +#### Body + +
{"data":{"type":"profile_requests","attributes":{"full_name":"Example User Updated","email":"invalid","password":""}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile_requests","attributes":{"full_name":"Example User Updated","email":"invalid","password":""}}}' -X PATCH \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUxfQ.99LIkGZJpmnaRN5QXS-yhKv8LsIGUePOGiES0aU8J1w"
+ +### Response + +#### Headers + +
Content-Type: application/vnd.api+json; charset=utf-8
+ +#### Status + +
422 Unprocessable Entity
+ +#### Body + +
{
+  "errors": [
+    {
+      "source": {
+        "pointer": "/data/attributes/password"
+      },
+      "detail": "is too short (minimum is 6 characters)"
+    },
+    {
+      "source": {
+        "pointer": "/data/attributes/email"
+      },
+      "detail": "is invalid"
+    }
+  ]
+}
diff --git a/doc/api/v1/signup/create_user.md b/doc/api/v1/registration/create_user.md similarity index 54% rename from doc/api/v1/signup/create_user.md rename to doc/api/v1/registration/create_user.md index 742a152..130736d 100644 --- a/doc/api/v1/signup/create_user.md +++ b/doc/api/v1/registration/create_user.md @@ -1,8 +1,8 @@ -# SignUp API +# Registration API ## Create User -### POST /v1/sign_ups +### POST /v1/registrations ### Parameters @@ -21,15 +21,15 @@ Accept: application/vnd.api+json #### Route -
POST /v1/sign_ups
+
POST /v1/registrations
#### Body -
{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}
+
{"data":{"type":"registration_requests","attributes":{"full_name":"Example User","email":"user@example.com","password":"123456"}}}
#### cURL -
curl "http://localhost:5000/v1/sign_ups" -d '{"data":{"type":"user-requests","attributes":{"full-name":"Example User","email":"user@example.com","password":"123456"}}}' -X POST \
+
curl "http://localhost:5000/v1/registrations" -d '{"data":{"type":"registration_requests","attributes":{"full_name":"Example User","email":"user@example.com","password":"123456"}}}' -X POST \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json"
@@ -47,11 +47,11 @@ Accept: application/vnd.api+json
{
   "data": {
-    "id": "197",
+    "id": "153",
     "type": "users",
     "attributes": {
       "email": "user@example.com",
-      "full-name": null
+      "full_name": "Example User"
     }
   }
 }
diff --git a/doc/api/v1/tokens/create_token.md b/doc/api/v1/tokens/create_token.md index 1d6ad0a..4f9a301 100644 --- a/doc/api/v1/tokens/create_token.md +++ b/doc/api/v1/tokens/create_token.md @@ -24,11 +24,11 @@ Accept: application/vnd.api+json #### Body -
{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}
+
{"data":{"type":"token_requests","attributes":{"email":"user@example.com","password":"123456"}}}
#### cURL -
curl "http://localhost:5000/v1/tokens" -d '{"data":{"type":"token-requests","attributes":{"email":"user@example.com","password":"123456"}}}' -X POST \
+
curl "http://localhost:5000/v1/tokens" -d '{"data":{"type":"token_requests","attributes":{"email":"user@example.com","password":"123456"}}}' -X POST \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json"
@@ -46,10 +46,10 @@ Accept: application/vnd.api+json
{
   "data": {
-    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MTk5fQ.lI4GEJbt0boQZ_nM1qCZk_lebN5576ai-BkN9Bw8F8A",
-    "type": "jwt-tokens",
+    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU0fQ.RhN1NnaQ3sZviA4KGTZ8KA4xz65Dqr5QeqqLOjejiKY",
+    "type": "jwt_tokens",
     "attributes": {
-      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MTk5fQ.lI4GEJbt0boQZ_nM1qCZk_lebN5576ai-BkN9Bw8F8A"
+      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU0fQ.RhN1NnaQ3sZviA4KGTZ8KA4xz65Dqr5QeqqLOjejiKY"
     }
   }
 }
diff --git a/doc/api/v1/tokens/create_token_with_invalid_password.md b/doc/api/v1/tokens/create_token_with_invalid_password.md new file mode 100644 index 0000000..d65086d --- /dev/null +++ b/doc/api/v1/tokens/create_token_with_invalid_password.md @@ -0,0 +1,54 @@ +# Tokens API + +## Create Token with invalid password + +### POST /v1/tokens + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| email | email | true | | +| password | password | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+ +#### Route + +
POST /v1/tokens
+ +#### Body + +
{"data":{"type":"token_requests","attributes":{"email":"user@example.com","password":"invalid"}}}
+ +#### cURL + +
curl "http://localhost:5000/v1/tokens" -d '{"data":{"type":"token_requests","attributes":{"email":"user@example.com","password":"invalid"}}}' -X POST \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json"
+ +### Response + +#### Headers + +
Content-Type: application/json; charset=utf-8
+ +#### Status + +
422 Unprocessable Entity
+ +#### Body + +
{
+  "errors": [
+    {
+      "code": "invalid_credentials",
+      "detail": "Invalid credentials"
+    }
+  ]
+}
diff --git a/doc/api/v1/users/list_users.md b/doc/api/v1/users/list_users.md index e330c7b..216aa7e 100644 --- a/doc/api/v1/users/list_users.md +++ b/doc/api/v1/users/list_users.md @@ -9,7 +9,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA0fQ.jNbuAOt54xUm0PFUDbASg1F8rdMc9p9qhnTfQDkhHyg
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU5fQ.KA9xlyNJj2dw0lt4WQKv1GllLL0UAJu8xWCNjZbPkPs #### Route @@ -17,14 +17,14 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M #### Query Parameters -
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+
{"data":{"type":"user_requests","attributes":{"full_name":null,"email":null,"password":null}}}: 
#### cURL
curl -g "http://localhost:5000/v1/users" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA0fQ.jNbuAOt54xUm0PFUDbASg1F8rdMc9p9qhnTfQDkhHyg"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU5fQ.KA9xlyNJj2dw0lt4WQKv1GllLL0UAJu8xWCNjZbPkPs" ### Response @@ -41,35 +41,35 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M
{
   "data": [
     {
-      "id": "201",
+      "id": "156",
       "type": "users",
       "attributes": {
-        "email": "user7@example.com",
-        "full-name": "Launa Leuschke Jr."
+        "email": "user5@example.com",
+        "full_name": "Wayne Breitenberg"
       }
     },
     {
-      "id": "202",
+      "id": "157",
       "type": "users",
       "attributes": {
-        "email": "user8@example.com",
-        "full-name": "Miss Renae Nienow"
+        "email": "user6@example.com",
+        "full_name": "Valentine Bailey"
       }
     },
     {
-      "id": "203",
+      "id": "158",
       "type": "users",
       "attributes": {
-        "email": "user9@example.com",
-        "full-name": "Anthony Kassulke"
+        "email": "user7@example.com",
+        "full_name": "Miss Leon Schulist"
       }
     },
     {
-      "id": "204",
+      "id": "159",
       "type": "users",
       "attributes": {
-        "email": "user10@example.com",
-        "full-name": "Quentin Okuneva"
+        "email": "user8@example.com",
+        "full_name": "Arlen Rice"
       }
     }
   ]
diff --git a/doc/api/v1/users/retrive_user.md b/doc/api/v1/users/retrive_user.md
index 25d1943..9f37577 100644
--- a/doc/api/v1/users/retrive_user.md
+++ b/doc/api/v1/users/retrive_user.md
@@ -16,22 +16,22 @@
 
 
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA2fQ.GYofTAg3S6-1fdkDd9w2OnGw3OhGh5zuTSDw5JMtkPc
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYxfQ.aCvKdmjFDvUnrLzmm8jpFCx4xaY91fdLtFiQxtsE9u8
#### Route -
GET /v1/users/205
+
GET /v1/users/160
#### Query Parameters -
{"data":{"type":"user-requests","attributes":{"full-name":null,"email":null,"password":null}}}: 
+
{"data":{"type":"user_requests","attributes":{"full_name":null,"email":null,"password":null}}}: 
#### cURL -
curl -g "http://localhost:5000/v1/users/205" -X GET \
+
curl -g "http://localhost:5000/v1/users/160" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4MjMsInN1YiI6MjA2fQ.GYofTAg3S6-1fdkDd9w2OnGw3OhGh5zuTSDw5JMtkPc"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYxfQ.aCvKdmjFDvUnrLzmm8jpFCx4xaY91fdLtFiQxtsE9u8"
### Response @@ -47,11 +47,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY2Njg4M
{
   "data": {
-    "id": "205",
+    "id": "160",
     "type": "users",
     "attributes": {
-      "email": "user11@example.com",
-      "full-name": "Marnie Nicolas"
+      "email": "user9@example.com",
+      "full_name": "Buster Rolfson"
     }
   }
 }
diff --git a/doc/api/v1/users/retrive_user_with_invalid_id.md b/doc/api/v1/users/retrive_user_with_invalid_id.md new file mode 100644 index 0000000..016a2bf --- /dev/null +++ b/doc/api/v1/users/retrive_user_with_invalid_id.md @@ -0,0 +1,55 @@ +# Users API + +## Retrive User with invalid id + +### GET /v1/users/:id + +### Parameters + +| Name | Description | Required | Scope | +|------|-------------|----------|-------| +| id | user id | true | | + +### Request + +#### Headers + +
Content-Type: application/vnd.api+json
+Accept: application/vnd.api+json
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYyfQ.uXcQz764au7gfpmZFOaO8AFn-tEhF7GR9CsjSP8fBos
+ +#### Route + +
GET /v1/users/0
+ +#### Query Parameters + +
{"data":{"type":"user_requests","attributes":{"full_name":null,"email":null,"password":null}}}: 
+ +#### cURL + +
curl -g "http://localhost:5000/v1/users/0" -X GET \
+	-H "Content-Type: application/vnd.api+json" \
+	-H "Accept: application/vnd.api+json" \
+	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYyfQ.uXcQz764au7gfpmZFOaO8AFn-tEhF7GR9CsjSP8fBos"
+ +### Response + +#### Headers + +
Content-Type: application/json; charset=utf-8
+ +#### Status + +
404 Not Found
+ +#### Body + +
{
+  "errors": [
+    {
+      "code": "record_not_found",
+      "detail": "Record not found"
+    }
+  ]
+}
diff --git a/spec/api/v1/sign_ups_spec.rb b/spec/api/v1/registrations_spec.rb similarity index 74% rename from spec/api/v1/sign_ups_spec.rb rename to spec/api/v1/registrations_spec.rb index 6dec09e..d4239cf 100644 --- a/spec/api/v1/sign_ups_spec.rb +++ b/spec/api/v1/registrations_spec.rb @@ -1,18 +1,18 @@ require "rails_helper" -class SignUpRequest < ActiveModelSerializers::Model +class RegistrationRequest < ActiveModelSerializers::Model attributes :id, :full_name, :email, :password end -class SignUpRequestSerializer < ApplicationSerializer +class RegistrationRequestSerializer < ApplicationSerializer attributes :full_name, :email, :password end -resource "SignUp" do +resource "Registration" do include_context "with JSON API Headers" include_context "with JSON API post body from request class" - post "/v1/sign_ups" do + post "/v1/registrations" do parameter :full_name, "full name" parameter :email, "email", required: true parameter :password, "password", required: true @@ -20,7 +20,7 @@ class SignUpRequestSerializer < ApplicationSerializer let(:full_name) { "Example User" } let(:email) { "user@example.com" } let(:password) { "123456" } - let(:request_class) { "user_request" } + let(:request_class) { "registration_request" } example "Create User" do do_request diff --git a/spec/schemas/v1/definitions.json b/spec/schemas/v1/definitions.json index 4eeae75..3c0b85c 100644 --- a/spec/schemas/v1/definitions.json +++ b/spec/schemas/v1/definitions.json @@ -6,10 +6,10 @@ "required": ["attributes"], "properties": { "attributes": { - "required": ["email", "full-name"], + "required": ["email", "full_name"], "properties": { "email": { "type": "string" }, - "full-name": { "type": ["string", "null"] } + "full_name": { "type": ["string", "null"] } } } } From 4d6b30131bf6c7bf522a1f4b6a14ebf3e79d45f1 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Tue, 11 Sep 2018 17:06:34 +0300 Subject: [PATCH 23/26] Remove auth error doc --- .../request_without_authorization.md | 42 ------------------- doc/api/v1/index.md | 4 -- doc/api/v1/profiles/delete_profile.md | 8 ++-- doc/api/v1/profiles/retrive_profile.md | 8 ++-- doc/api/v1/profiles/update_profile.md | 6 +-- ...e_with_empty_password_and_invalid_email.md | 4 +- doc/api/v1/registration/create_user.md | 2 +- doc/api/v1/tokens/create_token.md | 4 +- doc/api/v1/users/list_users.md | 20 ++++----- doc/api/v1/users/retrive_user.md | 12 +++--- .../v1/users/retrive_user_with_invalid_id.md | 4 +- spec/api/v1/auth_spec.rb | 15 ------- 12 files changed, 34 insertions(+), 95 deletions(-) delete mode 100644 doc/api/v1/authorization/request_without_authorization.md delete mode 100644 spec/api/v1/auth_spec.rb diff --git a/doc/api/v1/authorization/request_without_authorization.md b/doc/api/v1/authorization/request_without_authorization.md deleted file mode 100644 index 1f732d0..0000000 --- a/doc/api/v1/authorization/request_without_authorization.md +++ /dev/null @@ -1,42 +0,0 @@ -# Authorization API - -## Request without authorization - -### GET /v1/profile -### Request - -#### Headers - -
Content-Type: application/vnd.api+json
-Accept: application/vnd.api+json
- -#### Route - -
GET /v1/profile
- -#### cURL - -
curl -g "http://localhost:5000/v1/profile" -X GET \
-	-H "Content-Type: application/vnd.api+json" \
-	-H "Accept: application/vnd.api+json"
- -### Response - -#### Headers - -
Content-Type: application/json; charset=utf-8
- -#### Status - -
401 Unauthorized
- -#### Body - -
{
-  "errors": [
-    {
-      "code": "unauthorized",
-      "detail": "Authorization required"
-    }
-  ]
-}
diff --git a/doc/api/v1/index.md b/doc/api/v1/index.md index 1d29132..3cae6dc 100644 --- a/doc/api/v1/index.md +++ b/doc/api/v1/index.md @@ -1,10 +1,6 @@ # API Documentation -## Authorization - -* [Request without authorization](authorization/request_without_authorization.md) - ## Profiles * [Retrive Profile](profiles/retrive_profile.md) diff --git a/doc/api/v1/profiles/delete_profile.md b/doc/api/v1/profiles/delete_profile.md index d8e5e92..eaa67da 100644 --- a/doc/api/v1/profiles/delete_profile.md +++ b/doc/api/v1/profiles/delete_profile.md @@ -9,7 +9,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUyfQ.BTwgCHI3lIP3-vKTRu147lGDXclVYSld93mssUDfzIY
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTY2fQ.qgWchRvG9luv0IVp-sl-xfmFaIPtwTKcwttUozDUdcI #### Route @@ -24,7 +24,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile_requests","attributes":{"full_name":null,"email":null,"password":null}}}' -X DELETE \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUyfQ.BTwgCHI3lIP3-vKTRu147lGDXclVYSld93mssUDfzIY"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTY2fQ.qgWchRvG9luv0IVp-sl-xfmFaIPtwTKcwttUozDUdcI" ### Response @@ -40,11 +40,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
{
   "data": {
-    "id": "152",
+    "id": "166",
     "type": "users",
     "attributes": {
       "email": "user4@example.com",
-      "full_name": "Armando Gaylord"
+      "full_name": "Dr. Joana Heathcote"
     }
   }
 }
diff --git a/doc/api/v1/profiles/retrive_profile.md b/doc/api/v1/profiles/retrive_profile.md index 0a8103d..c31b0c7 100644 --- a/doc/api/v1/profiles/retrive_profile.md +++ b/doc/api/v1/profiles/retrive_profile.md @@ -9,7 +9,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTQ5fQ.7vtVLtJ-jHlXvxEF2-vRS4U8IZCbiZ4wYbYo5DmZoWE
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODcsInN1YiI6MTYzfQ.S1ejGuAOao-cIYvsSnbaETAG-1UCEtDZcDsdV-SeETs #### Route @@ -24,7 +24,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
curl -g "http://localhost:5000/v1/profile" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTQ5fQ.7vtVLtJ-jHlXvxEF2-vRS4U8IZCbiZ4wYbYo5DmZoWE"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODcsInN1YiI6MTYzfQ.S1ejGuAOao-cIYvsSnbaETAG-1UCEtDZcDsdV-SeETs" ### Response @@ -40,11 +40,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
{
   "data": {
-    "id": "149",
+    "id": "163",
     "type": "users",
     "attributes": {
       "email": "user1@example.com",
-      "full_name": "Mr. Shoshana Jacobi"
+      "full_name": "Joe Marks II"
     }
   }
 }
diff --git a/doc/api/v1/profiles/update_profile.md b/doc/api/v1/profiles/update_profile.md index c62d6a0..9bc5291 100644 --- a/doc/api/v1/profiles/update_profile.md +++ b/doc/api/v1/profiles/update_profile.md @@ -18,7 +18,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUwfQ.uxN8Lf0W758tV8-_7qcVQGolo1VFu3E-gePIc8qzL5Q
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODcsInN1YiI6MTY0fQ.khNxDZk0-l7OVUIJEdr7JVmYE8oCmrH8WaorMAWk9Oc #### Route @@ -33,7 +33,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile_requests","attributes":{"full_name":"Example User Updated","email":"user_updated@example.com","password":"new_password"}}}' -X PATCH \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUwfQ.uxN8Lf0W758tV8-_7qcVQGolo1VFu3E-gePIc8qzL5Q"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODcsInN1YiI6MTY0fQ.khNxDZk0-l7OVUIJEdr7JVmYE8oCmrH8WaorMAWk9Oc" ### Response @@ -49,7 +49,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
{
   "data": {
-    "id": "150",
+    "id": "164",
     "type": "users",
     "attributes": {
       "email": "user_updated@example.com",
diff --git a/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md b/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md
index 3521b44..1f68e7c 100644
--- a/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md
+++ b/doc/api/v1/profiles/update_profile_with_empty_password_and_invalid_email.md
@@ -18,7 +18,7 @@
 
 
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUxfQ.99LIkGZJpmnaRN5QXS-yhKv8LsIGUePOGiES0aU8J1w
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTY1fQ.AoMwSorPTETdEFEM1CUTKbEKEQzKhTGRW_yxh4KDPL0
#### Route @@ -33,7 +33,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
curl "http://localhost:5000/v1/profile" -d '{"data":{"type":"profile_requests","attributes":{"full_name":"Example User Updated","email":"invalid","password":""}}}' -X PATCH \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTUxfQ.99LIkGZJpmnaRN5QXS-yhKv8LsIGUePOGiES0aU8J1w"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTY1fQ.AoMwSorPTETdEFEM1CUTKbEKEQzKhTGRW_yxh4KDPL0" ### Response diff --git a/doc/api/v1/registration/create_user.md b/doc/api/v1/registration/create_user.md index 130736d..ebe4569 100644 --- a/doc/api/v1/registration/create_user.md +++ b/doc/api/v1/registration/create_user.md @@ -47,7 +47,7 @@ Accept: application/vnd.api+json
{
   "data": {
-    "id": "153",
+    "id": "167",
     "type": "users",
     "attributes": {
       "email": "user@example.com",
diff --git a/doc/api/v1/tokens/create_token.md b/doc/api/v1/tokens/create_token.md
index 4f9a301..be71e87 100644
--- a/doc/api/v1/tokens/create_token.md
+++ b/doc/api/v1/tokens/create_token.md
@@ -46,10 +46,10 @@ Accept: application/vnd.api+json
{
   "data": {
-    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU0fQ.RhN1NnaQ3sZviA4KGTZ8KA4xz65Dqr5QeqqLOjejiKY",
+    "id": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTY4fQ.42J7f4PUabczP3pmUpQNcmtG4GFmXpHA17VaWmnQqK0",
     "type": "jwt_tokens",
     "attributes": {
-      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU0fQ.RhN1NnaQ3sZviA4KGTZ8KA4xz65Dqr5QeqqLOjejiKY"
+      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTY4fQ.42J7f4PUabczP3pmUpQNcmtG4GFmXpHA17VaWmnQqK0"
     }
   }
 }
diff --git a/doc/api/v1/users/list_users.md b/doc/api/v1/users/list_users.md index 216aa7e..856fb01 100644 --- a/doc/api/v1/users/list_users.md +++ b/doc/api/v1/users/list_users.md @@ -9,7 +9,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU5fQ.KA9xlyNJj2dw0lt4WQKv1GllLL0UAJu8xWCNjZbPkPs
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTczfQ.Abxi19K-az8G3cuhq_ti18PBbtKAc_f3RUJuHVlXc1Q #### Route @@ -24,7 +24,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
curl -g "http://localhost:5000/v1/users" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTU5fQ.KA9xlyNJj2dw0lt4WQKv1GllLL0UAJu8xWCNjZbPkPs"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTczfQ.Abxi19K-az8G3cuhq_ti18PBbtKAc_f3RUJuHVlXc1Q" ### Response @@ -41,35 +41,35 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
{
   "data": [
     {
-      "id": "156",
+      "id": "170",
       "type": "users",
       "attributes": {
         "email": "user5@example.com",
-        "full_name": "Wayne Breitenberg"
+        "full_name": "Danial Gibson"
       }
     },
     {
-      "id": "157",
+      "id": "171",
       "type": "users",
       "attributes": {
         "email": "user6@example.com",
-        "full_name": "Valentine Bailey"
+        "full_name": "Yukiko Ledner"
       }
     },
     {
-      "id": "158",
+      "id": "172",
       "type": "users",
       "attributes": {
         "email": "user7@example.com",
-        "full_name": "Miss Leon Schulist"
+        "full_name": "Elois Kiehn"
       }
     },
     {
-      "id": "159",
+      "id": "173",
       "type": "users",
       "attributes": {
         "email": "user8@example.com",
-        "full_name": "Arlen Rice"
+        "full_name": "Mr. Kelly Connelly"
       }
     }
   ]
diff --git a/doc/api/v1/users/retrive_user.md b/doc/api/v1/users/retrive_user.md
index 9f37577..3148649 100644
--- a/doc/api/v1/users/retrive_user.md
+++ b/doc/api/v1/users/retrive_user.md
@@ -16,11 +16,11 @@
 
 
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYxfQ.aCvKdmjFDvUnrLzmm8jpFCx4xaY91fdLtFiQxtsE9u8
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTc1fQ.DSxpLhyzDiN4nlSW2ikMxIZyIcBUA4au93HT18Ka9WA
#### Route -
GET /v1/users/160
+
GET /v1/users/174
#### Query Parameters @@ -28,10 +28,10 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N #### cURL -
curl -g "http://localhost:5000/v1/users/160" -X GET \
+
curl -g "http://localhost:5000/v1/users/174" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYxfQ.aCvKdmjFDvUnrLzmm8jpFCx4xaY91fdLtFiQxtsE9u8"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTc1fQ.DSxpLhyzDiN4nlSW2ikMxIZyIcBUA4au93HT18Ka9WA"
### Response @@ -47,11 +47,11 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
{
   "data": {
-    "id": "160",
+    "id": "174",
     "type": "users",
     "attributes": {
       "email": "user9@example.com",
-      "full_name": "Buster Rolfson"
+      "full_name": "Bryon Gutkowski"
     }
   }
 }
diff --git a/doc/api/v1/users/retrive_user_with_invalid_id.md b/doc/api/v1/users/retrive_user_with_invalid_id.md index 016a2bf..ab13045 100644 --- a/doc/api/v1/users/retrive_user_with_invalid_id.md +++ b/doc/api/v1/users/retrive_user_with_invalid_id.md @@ -16,7 +16,7 @@
Content-Type: application/vnd.api+json
 Accept: application/vnd.api+json
-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYyfQ.uXcQz764au7gfpmZFOaO8AFn-tEhF7GR9CsjSP8fBos
+Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTc2fQ.SM3fZ97H8I_cvj-xHR18FmfdvXYK_XMtObYmAs4k1lQ #### Route @@ -31,7 +31,7 @@ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5N
curl -g "http://localhost:5000/v1/users/0" -X GET \
 	-H "Content-Type: application/vnd.api+json" \
 	-H "Accept: application/vnd.api+json" \
-	-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjA5NzMsInN1YiI6MTYyfQ.uXcQz764au7gfpmZFOaO8AFn-tEhF7GR9CsjSP8fBos"
+ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzY3NjExODgsInN1YiI6MTc2fQ.SM3fZ97H8I_cvj-xHR18FmfdvXYK_XMtObYmAs4k1lQ" ### Response diff --git a/spec/api/v1/auth_spec.rb b/spec/api/v1/auth_spec.rb deleted file mode 100644 index 086c836..0000000 --- a/spec/api/v1/auth_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require "rails_helper" -require "rspec_api_documentation/dsl" - -resource "Authorization" do - include_context "with JSON API Headers" - - get "/v1/profile" do - example "Request without authorization" do - do_request - - expect(response_status).to eq 401 - expect(response_body).to match_response_schema("v1/errors") - end - end -end From e3da96201db4805fee5c8f2936f3e1e9e5632469 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Wed, 12 Sep 2018 14:12:42 +0300 Subject: [PATCH 24/26] Comment unused S3_ASSET_HOST --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 826aa65..972d7cf 100644 --- a/.env.example +++ b/.env.example @@ -18,7 +18,7 @@ SECRET_KEY_BASE=development_secret # Specify assets server host name, eg.: d2oek0c5zwe48d.cloudfront.net ASSET_HOST=lvh.me:5000 -S3_ASSET_HOST=https://d1ltghz9ehkb4n.cloudfront.net +# S3_ASSET_HOST=https://d1ltghz9ehkb4n.cloudfront.net # Set Rollbar key for the app # ROLLBAR_ACCESS_TOKEN=your_key_here From b991dcb6828a62f5930fa96c04079f64fdbd2d47 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Wed, 12 Sep 2018 15:09:54 +0300 Subject: [PATCH 25/26] Update README and remove unused gems --- Gemfile | 3 -- Gemfile.lock | 15 -------- README.md | 100 ++++++++++++++++----------------------------------- 3 files changed, 31 insertions(+), 87 deletions(-) diff --git a/Gemfile b/Gemfile index fa031ee..74bf1d1 100644 --- a/Gemfile +++ b/Gemfile @@ -23,7 +23,6 @@ gem "seedbank" group :development do gem "bullet" - gem "foreman" gem "letter_opener" gem "rails-erd" gem "spring" @@ -46,10 +45,8 @@ group :test do gem "database_cleaner" gem "email_spec" gem "json_matchers" - gem "json_spec" gem "rspec_api_documentation" gem "shoulda-matchers", require: false - gem "simplecov", require: false gem "webmock", require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 8ccee72..21d5740 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -74,7 +74,6 @@ GEM decent_exposure (3.0.2) activesupport (>= 4.0) diff-lcs (1.3) - docile (1.3.1) dotenv (2.5.0) dotenv-rails (2.5.0) dotenv (= 2.5.0) @@ -92,8 +91,6 @@ GEM faker (1.9.1) i18n (>= 0.7) ffi (1.9.25) - foreman (0.85.0) - thor (~> 0.19.1) globalid (0.4.1) activesupport (>= 4.2.0) hashdiff (0.3.7) @@ -104,13 +101,9 @@ GEM concurrent-ruby (~> 1.0) interactor (3.1.1) jaro_winkler (1.5.1) - json (2.1.0) json_matchers (0.10.0) json_schema json_schema (0.19.1) - json_spec (1.1.5) - multi_json (~> 1.0) - rspec (>= 2.0, < 4.0) jsonapi-renderer (0.2.0) jwt (1.5.6) kaminari (1.1.1) @@ -250,11 +243,6 @@ GEM seedbank (0.4.0) shoulda-matchers (3.1.2) activesupport (>= 4.0.0) - simplecov (0.16.1) - docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) spring (2.0.2) activesupport (>= 4.2) spring-commands-rspec (1.0.4) @@ -300,11 +288,9 @@ DEPENDENCIES email_spec factory_bot_rails faker - foreman health_check interactor json_matchers - json_spec kaminari knock letter_opener @@ -322,7 +308,6 @@ DEPENDENCIES rubocop-rspec seedbank shoulda-matchers - simplecov spring spring-commands-rspec spring-watcher-listen diff --git a/README.md b/README.md index c5001ea..57ca3a2 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,51 @@ -# Skeleton for new Rails 4 application for REST API +# Skeleton for new Rails 5 application for JSON API -[![Code Climate](https://codeclimate.com/github/fs/rails-base-api.png)](https://codeclimate.com/github/fs/rails-base-api) [![Build Status](https://semaphoreci.com/api/v1/fs/rails-base-api/branches/master/shields_badge.svg)](https://semaphoreci.com/fs/rails-base-api) [![Known Vulnerabilities](https://snyk.io/test/github/fs/rails-base-api/badge.svg)](https://snyk.io/test/github/fs/rails-base-api) -This simple application includes ruby/rails technology which we use at FlatStack for new REST API projects. - -Application currently based on Rails 4 stable branch and Ruby 2.3.6 - -## API - -Status of the API could be checked at [http://localhost:5000/docs](http://localhost:5000/docs) +This simple application includes Ruby/Rails technology which we use at Flatstack for new [JSON API](http://jsonapi.org) projects. Application currently based on Rails 5 stable branch and Ruby 2.5.1 ## What's included ### Application gems: -* [Decent Exposure](https://github.com/voxdolo/decent_exposure) for DRY controllers -* [Rollbar](https://github.com/rollbar/rollbar-gem) for exception notification -* [Thin](https://github.com/macournoyer/thin) as rails web server -* [Kaminari](https://github.com/amatsuda/kaminari) for pagination -* [Rack CORS](https://github.com/cyu/rack-cors) for [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) +* [active_model_serializers](https://github.com/rails-api/active_model_serializers) - resource serializers for JSON API +* [decent_exposure](https://github.com/voxdolo/decent_exposure) for DRY controllers +* [health_check](https://github.com/ianheggie/health_check) - health check endpoint +* [interactor](https://github.com/collectiveidea/interactor) – encapsulates business logic +* [kaminari](https://github.com/amatsuda/kaminari) for pagination +* [knock](https://github.com/nsarno/knock) – seamless JWT authentication +* [puma](https://github.com/puma/puma) as Rails web server +* [rack-cors](https://github.com/cyu/rack-cors) for [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) +* [responders](https://github.com/plataformatec/responders) - DRY controllers +* [rollbar](https://github.com/rollbar/rollbar-gem) for exception notification ### Development gems -* [Foreman](https://github.com/ddollar/foreman) for managing development stack with Procfile -* [Letter Opener](https://github.com/ryanb/letter_opener) for preview mail in the browser instead of sending -* [Mail Safe](https://github.com/myronmarston/mail_safe) keep ActionMailer emails from escaping into the wild during development -* [Bullet](https://github.com/flyerhzm/bullet) gem to kill N+1 queries and unused eager loading -* [Rails Best Practices](https://github.com/railsbp/rails_best_practices) code metric tool -* [Brakeman](https://github.com/presidentbeef/brakeman) static analysis security vulnerability scanner -* [Bundler Audit](https://github.com/rubysec/bundler-audit) Patch-level verification for Gems -* [Spring](https://github.com/rails/spring) for fast Rails actions via pre-loading +* [brakeman](https://github.com/presidentbeef/brakeman) - static analysis security vulnerability scanner +* [bullet](https://github.com/flyerhzm/bullet) - kill n+1 queries and unused eager loading +* [bundler-audit](https://github.com/rubysec/bundler-audit) - patch-level verification for gems +* [dotenv](https://github.com/bkeepers/dotenv) - load environment variables from `.env` +* [letter_opener](https://github.com/ryanb/letter_opener) - preview E-Mails in the browser instead of sending +* [rails-erd](https://github.com/voormedia/rails-erd) - generate a diagram based on application's AR models +* [seedbank](https://github.com/james2m/seedbank) - seeds on steroids ### Testing gems -* [Factory Bot](https://github.com/thoughtbot/factory_bot) for easier creation of test data -* [RSpec](https://github.com/rspec/rspec) for awesome, readable isolation testing -* [Shoulda Matchers](http://github.com/thoughtbot/shoulda-matchers) for frequently needed Rails and RSpec matchers -* [Email Spec](https://github.com/bmabey/email-spec) Collection of rspec matchers and cucumber steps for testing emails -* [Rspec Api Documentation](https://github.com/zipmark/rspec_api_documentation) Generate pretty API docs for your Rails APIs +* [factory bot](https://github.com/thoughtbot/factory_bot) - create test data +* [faker](https://github.com/stympy/faker) - generate fake data +* [json_matchers](https://github.com/thoughtbot/json_matchers) - validate JSON with JSON Schema +* [rspec api documentation](https://github.com/zipmark/rspec_api_documentation) - generate pretty API docs +* [rspec](https://github.com/rspec/rspec) - awesome, readable isolation testing +* [shoulda matchers](http://github.com/thoughtbot/shoulda-matchers) - frequently needed Rails and RSpec matchers -### Initializes +### Non standard initializes -* `01_config.rb` - shortcut for getting application config with `app_config` -* `mailer.rb` - setup default hosts for mailer from configuration -* `requires.rb` - automatically requires everything in lib/ & lib/extensions -* `rack_cors.rb` - setup whitelist of domains to allow cross-origin resource sharing +* `active_model_serializer.rb` - setup serializers for JSON API +* `bullet.rb` - setup Bullet to catch up N+1 +* `cors.rb` - setup whitelist of domains to allow cross-origin resource sharing +* `health_check.rb` - setup Health Check endpoint +* `rollbar.rb` - setup Rollbar ### Scripts @@ -55,39 +54,6 @@ Status of the API could be checked at [http://localhost:5000/docs](http://localh * `bin/ci` - should be used in the CI or locally * `bin/server` - to run server locally -### Serializers - -### PaginatedArraySerializer - -Use that serializer if you want to add meta with pagination info on response - -```ruby -def index - respond_with( - posts, - serializer: PaginatedArraySerializer - ) -end -``` - -The above usage of `PaginatedArraySerializer` will produce the following: - -```json -{ - "meta": { - "pagination": { - "total":46, - "per_page":2, - "page":1 - } - }, - "posts": [ - { "title": "Post 1", "body": "Hello!" }, - { "title": "Post 2", "body": "Goodbye!" } - ] -} -``` - ## Quick start Clone application as new project with original repository named "rails-base-api" @@ -130,10 +96,6 @@ mv doc/README_TEMPLATE.md README.md git commit -am "Update README.md" ``` -### Restrict access to documentation - -You can enable restrict access to documentation by adding `APITOME_USER` and `APITOME_PASSWORD` environment variables. Can be useful on staging environment. - ## Examples Please check how to build API endpoints and test them properly in the From 0a489a3fd7996d50da90492cc8796ac2520188fa Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Wed, 12 Sep 2018 15:13:19 +0300 Subject: [PATCH 26/26] Move error code to interactor --- app/controllers/v1/tokens_controller.rb | 2 +- app/interactors/create_jwt.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/v1/tokens_controller.rb b/app/controllers/v1/tokens_controller.rb index e215651..dc869b0 100644 --- a/app/controllers/v1/tokens_controller.rb +++ b/app/controllers/v1/tokens_controller.rb @@ -8,7 +8,7 @@ def create if result.success? respond_with_resource(result.jwt_token, status: :created, location: nil) else - respond_with_error(:invalid_credentials) + respond_with_error(result.code) end end diff --git a/app/interactors/create_jwt.rb b/app/interactors/create_jwt.rb index 17e8a9e..055c0b0 100644 --- a/app/interactors/create_jwt.rb +++ b/app/interactors/create_jwt.rb @@ -4,7 +4,7 @@ class CreateJwt delegate :email, :password, to: :context def call - context.fail! unless authenticated? + context.fail!(code: :invalid_credentials) unless authenticated? context.jwt_token = jwt_token end