From c5367db276034ccf72c23279467ca2fc0c911761 Mon Sep 17 00:00:00 2001 From: sibsmc Date: Tue, 18 Jun 2019 20:00:32 +0200 Subject: [PATCH 1/8] Add User resource and browser Create, Edit, Read and Delete function. Also contains user database migration and database seed. --- server/app/assets/javascripts/users.coffee | 3 ++ server/app/assets/stylesheets/users.scss | 3 ++ .../app/controllers/application_controller.rb | 1 + server/app/controllers/users_controller.rb | 48 +++++++++++++++++++ server/app/helpers/users_helper.rb | 2 + server/app/models/user.rb | 3 ++ server/app/views/users/edit.html.erb | 21 ++++++++ server/app/views/users/index.html.erb | 21 ++++++++ server/app/views/users/new.html.erb | 36 ++++++++++++++ server/app/views/users/show.html.erb | 29 +++++++++++ server/config/routes.rb | 2 +- .../db/migrate/20190618164807_create_users.rb | 8 ++++ .../20190618171808_add_first_name_to_user.rb | 5 ++ .../20190618171848_add_last_name_to_user.rb | 5 ++ .../20190618171912_add_email_to_user.rb | 5 ++ .../20190618171932_add_city_to_user.rb | 5 ++ .../20190618172134_add_mentor_to_user.rb | 5 ++ .../20190618172151_add_mentee_to_user.rb | 5 ++ server/db/schema.rb | 26 ++++++++++ server/db/seeds.rb | 2 + .../test/controllers/users_controller_test.rb | 7 +++ server/test/fixtures/users.yml | 11 +++++ server/test/models/user_test.rb | 7 +++ 23 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 server/app/assets/javascripts/users.coffee create mode 100644 server/app/assets/stylesheets/users.scss create mode 100644 server/app/controllers/users_controller.rb create mode 100644 server/app/helpers/users_helper.rb create mode 100644 server/app/models/user.rb create mode 100644 server/app/views/users/edit.html.erb create mode 100644 server/app/views/users/index.html.erb create mode 100644 server/app/views/users/new.html.erb create mode 100644 server/app/views/users/show.html.erb create mode 100644 server/db/migrate/20190618164807_create_users.rb create mode 100644 server/db/migrate/20190618171808_add_first_name_to_user.rb create mode 100644 server/db/migrate/20190618171848_add_last_name_to_user.rb create mode 100644 server/db/migrate/20190618171912_add_email_to_user.rb create mode 100644 server/db/migrate/20190618171932_add_city_to_user.rb create mode 100644 server/db/migrate/20190618172134_add_mentor_to_user.rb create mode 100644 server/db/migrate/20190618172151_add_mentee_to_user.rb create mode 100644 server/db/schema.rb create mode 100644 server/test/controllers/users_controller_test.rb create mode 100644 server/test/fixtures/users.yml create mode 100644 server/test/models/user_test.rb diff --git a/server/app/assets/javascripts/users.coffee b/server/app/assets/javascripts/users.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/server/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/server/app/assets/stylesheets/users.scss b/server/app/assets/stylesheets/users.scss new file mode 100644 index 00000000..31a2eacb --- /dev/null +++ b/server/app/assets/stylesheets/users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/server/app/controllers/application_controller.rb b/server/app/controllers/application_controller.rb index 1c07694e..d1046e97 100644 --- a/server/app/controllers/application_controller.rb +++ b/server/app/controllers/application_controller.rb @@ -1,3 +1,4 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception end + diff --git a/server/app/controllers/users_controller.rb b/server/app/controllers/users_controller.rb new file mode 100644 index 00000000..ae97728a --- /dev/null +++ b/server/app/controllers/users_controller.rb @@ -0,0 +1,48 @@ +class UsersController < ApplicationController + def index + @users = User.all + end + + def show + @user = User.find(params[:id]) + end + + def new + end + + def edit + @user = User.find(params[:id]) + end + + def create + @user = User.new(user_params) + if @user.save + redirect_to @user + else + render 'new' + end + end + + def update + @user = User.find(params[:id]) + + if @user.update(user_params) + redirect_to @user + else + render 'edit' + end + end + + def destroy + @user = User.find(params[:id]) + @user.destroy + + redirect_to users_path + end + + private + def user_params + params.require(:user).permit(:first_name, :last_name, :email, :city, :mentor, :mentee) + end + +end diff --git a/server/app/helpers/users_helper.rb b/server/app/helpers/users_helper.rb new file mode 100644 index 00000000..2310a240 --- /dev/null +++ b/server/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/server/app/models/user.rb b/server/app/models/user.rb new file mode 100644 index 00000000..245026de --- /dev/null +++ b/server/app/models/user.rb @@ -0,0 +1,3 @@ +class User < ApplicationRecord + +end diff --git a/server/app/views/users/edit.html.erb b/server/app/views/users/edit.html.erb new file mode 100644 index 00000000..dab71d2b --- /dev/null +++ b/server/app/views/users/edit.html.erb @@ -0,0 +1,21 @@ +

Edit user

+ +<%= form_with(model: @user, local: true) do |form| %> + +

+ <%= form.label :first_name %>
+ <%= form.text_field :first_name%> +

+ +

+ <%= form.label :last_name %>
+ <%= form.text_area :last_name %> +

+ +

+ <%= form.submit %> +

+ +<% end %> + +<%= link_to 'Back', users_path %> diff --git a/server/app/views/users/index.html.erb b/server/app/views/users/index.html.erb new file mode 100644 index 00000000..ac932ea5 --- /dev/null +++ b/server/app/views/users/index.html.erb @@ -0,0 +1,21 @@ +

Listing Users

+ + + + + + + + + <% @users.each do |user| %> + + + + + + + + <% end %> +
FirstLast
<%= user.first_name %><%= user.last_name %><%= link_to 'Show', user_path(user) %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user_path(user), + method: :delete, + data: { confirm: 'Are you sure?' } %>
diff --git a/server/app/views/users/new.html.erb b/server/app/views/users/new.html.erb new file mode 100644 index 00000000..3d2b0bc8 --- /dev/null +++ b/server/app/views/users/new.html.erb @@ -0,0 +1,36 @@ +

New User

+ + +<%= form_with scope: :user, url: users_path, local: true do |form| %> + + +

+ <%= form.label :first_name %>
+ <%= form.text_area :first_name %> +

+

+ <%= form.label :last_name %>
+ <%= form.text_area :last_name %> +

+

+ <%= form.label :email %>
+ <%= form.text_area :email %> +

+

+ <%= form.label :city %>
+ <%= form.text_area :city %> +

+

+ <%= form.label :mentor %>
+ <%= form.check_box :mentor %> +

+

+ <%= form.label :mentee %>
+ <%= form.check_box :mentee %> +

+ +

+ <%= form.submit %> +

+<% end %> + diff --git a/server/app/views/users/show.html.erb b/server/app/views/users/show.html.erb new file mode 100644 index 00000000..94eaa637 --- /dev/null +++ b/server/app/views/users/show.html.erb @@ -0,0 +1,29 @@ +

+ First Name: + <%= @user.first_name %> +

+ +

+ Last Name: + <%= @user.last_name %> +

+

+ Email: + <%= @user.email %> +

+

+ City: + <%= @user.city %> +

+

+ Mentor: + <%= @user.mentor %> +

+

+ Mentee: + <%= @user.mentee %> +

+ +<%= link_to 'New', new_user_path(@user) %> | +<%= link_to 'Edit', edit_user_path(@user) %> | +<%= link_to 'Back', users_path %> diff --git a/server/config/routes.rb b/server/config/routes.rb index d91ad920..a0eae3ee 100644 --- a/server/config/routes.rb +++ b/server/config/routes.rb @@ -1,6 +1,6 @@ Rails.application.routes.draw do get 'welcome/index' - + resources :users root 'welcome#index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/server/db/migrate/20190618164807_create_users.rb b/server/db/migrate/20190618164807_create_users.rb new file mode 100644 index 00000000..8006900e --- /dev/null +++ b/server/db/migrate/20190618164807_create_users.rb @@ -0,0 +1,8 @@ +class CreateUsers < ActiveRecord::Migration[5.2] + def change + create_table :users do |t| + + t.timestamps + end + end +end diff --git a/server/db/migrate/20190618171808_add_first_name_to_user.rb b/server/db/migrate/20190618171808_add_first_name_to_user.rb new file mode 100644 index 00000000..86fe66f2 --- /dev/null +++ b/server/db/migrate/20190618171808_add_first_name_to_user.rb @@ -0,0 +1,5 @@ +class AddFirstNameToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :first_name, :string + end +end diff --git a/server/db/migrate/20190618171848_add_last_name_to_user.rb b/server/db/migrate/20190618171848_add_last_name_to_user.rb new file mode 100644 index 00000000..d2c16f40 --- /dev/null +++ b/server/db/migrate/20190618171848_add_last_name_to_user.rb @@ -0,0 +1,5 @@ +class AddLastNameToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :last_name, :string + end +end diff --git a/server/db/migrate/20190618171912_add_email_to_user.rb b/server/db/migrate/20190618171912_add_email_to_user.rb new file mode 100644 index 00000000..0f5627ee --- /dev/null +++ b/server/db/migrate/20190618171912_add_email_to_user.rb @@ -0,0 +1,5 @@ +class AddEmailToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :email, :string + end +end diff --git a/server/db/migrate/20190618171932_add_city_to_user.rb b/server/db/migrate/20190618171932_add_city_to_user.rb new file mode 100644 index 00000000..b6d289bc --- /dev/null +++ b/server/db/migrate/20190618171932_add_city_to_user.rb @@ -0,0 +1,5 @@ +class AddCityToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :city, :string + end +end diff --git a/server/db/migrate/20190618172134_add_mentor_to_user.rb b/server/db/migrate/20190618172134_add_mentor_to_user.rb new file mode 100644 index 00000000..ec926675 --- /dev/null +++ b/server/db/migrate/20190618172134_add_mentor_to_user.rb @@ -0,0 +1,5 @@ +class AddMentorToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :mentor, :boolean + end +end diff --git a/server/db/migrate/20190618172151_add_mentee_to_user.rb b/server/db/migrate/20190618172151_add_mentee_to_user.rb new file mode 100644 index 00000000..ea3e3bd5 --- /dev/null +++ b/server/db/migrate/20190618172151_add_mentee_to_user.rb @@ -0,0 +1,5 @@ +class AddMenteeToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :mentee, :boolean + end +end diff --git a/server/db/schema.rb b/server/db/schema.rb new file mode 100644 index 00000000..e5bc2e73 --- /dev/null +++ b/server/db/schema.rb @@ -0,0 +1,26 @@ +# 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. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2019_06_18_172151) do + + create_table "users", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "first_name" + t.string "last_name" + t.string "email" + t.string "city" + t.boolean "mentor" + t.boolean "mentee" + end + +end diff --git a/server/db/seeds.rb b/server/db/seeds.rb index 1beea2ac..ef859b26 100644 --- a/server/db/seeds.rb +++ b/server/db/seeds.rb @@ -5,3 +5,5 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) +User.destroy_all +User.create!([{fist_name: 'Mary', last_name: 'Brown',email: 'mary@email.com', mentor: 'True', mentee: 'False'},{fist_name: 'Nuala', last_name: 'May',email: 'nuala@email.com', mentor: 'False', mentee: 'True'},{fist_name: 'Bob', last_name: 'Brown',email: 'bob@email.com', mentor: 'True', mentee: 'False'}]) diff --git a/server/test/controllers/users_controller_test.rb b/server/test/controllers/users_controller_test.rb new file mode 100644 index 00000000..6c3da770 --- /dev/null +++ b/server/test/controllers/users_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UsersControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/server/test/fixtures/users.yml b/server/test/fixtures/users.yml new file mode 100644 index 00000000..80aed36e --- /dev/null +++ b/server/test/fixtures/users.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/server/test/models/user_test.rb b/server/test/models/user_test.rb new file mode 100644 index 00000000..82f61e01 --- /dev/null +++ b/server/test/models/user_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From c77bd974591a532dcc001fd2d5c9be575a5c41ec Mon Sep 17 00:00:00 2001 From: sibsmc Date: Wed, 19 Jun 2019 22:43:25 +0200 Subject: [PATCH 2/8] CRUD API and rspec tests added for User Rescources, tests can be run with command 'rspec spec/requests/api/v1/users_spec.rb' in the server directory. --- server/.rspec | 1 + server/Gemfile | 1 + server/Gemfile.lock | 19 ++++ .../controllers/api/v1/users_controller.rb | 53 ++++++++++ server/config/routes.rb | 10 +- server/db/seeds.rb | 3 +- server/spec/rails_helper.rb | 61 ++++++++++++ server/spec/requests/api/v1/users_spec.rb | 42 ++++++++ server/spec/spec_helper.rb | 96 +++++++++++++++++++ 9 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 server/.rspec create mode 100644 server/app/controllers/api/v1/users_controller.rb create mode 100644 server/spec/rails_helper.rb create mode 100644 server/spec/requests/api/v1/users_spec.rb create mode 100644 server/spec/spec_helper.rb diff --git a/server/.rspec b/server/.rspec new file mode 100644 index 00000000..c99d2e73 --- /dev/null +++ b/server/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/server/Gemfile b/server/Gemfile index 4d0b6211..c3892f3a 100644 --- a/server/Gemfile +++ b/server/Gemfile @@ -44,6 +44,7 @@ group :development, :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '~> 3.24' gem 'selenium-webdriver' + gem 'rspec-rails', '~> 3.8' end group :development do diff --git a/server/Gemfile.lock b/server/Gemfile.lock index 3c8e778f..feebe794 100644 --- a/server/Gemfile.lock +++ b/server/Gemfile.lock @@ -69,6 +69,7 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.1.5) crass (1.0.4) + diff-lcs (1.3) erubi (1.8.0) execjs (2.7.0) ffi (1.10.0) @@ -133,6 +134,23 @@ GEM rb-inotify (0.10.0) ffi (~> 1.0) regexp_parser (1.5.1) + rspec-core (3.8.1) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.4) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-rails (3.8.2) + 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.2) ruby_dep (1.5.0) rubyzip (1.2.2) sass (3.7.3) @@ -195,6 +213,7 @@ DEPENDENCIES pg puma (~> 3.12) rails (~> 5.2) + rspec-rails (~> 3.8) sass-rails (~> 5.0) selenium-webdriver spring diff --git a/server/app/controllers/api/v1/users_controller.rb b/server/app/controllers/api/v1/users_controller.rb new file mode 100644 index 00000000..09a7ad79 --- /dev/null +++ b/server/app/controllers/api/v1/users_controller.rb @@ -0,0 +1,53 @@ +class Api::V1::UsersController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :set_user, only: [:show, :update, :destroy] + + def index + @users = User.all + render json: {users: @users} + end + + def show + @user = User.find(params[:id]) + render json: {user: @user} + end + + def new + end + + def edit + @user = User.find(params[:id]) + end + + def create + @user = User.new(user_params[:user]) + if @user.save + render json: @user, status: :created, location: api_v1_user_url(@user) + else + render json: @user.errors, status: :unprocessable_entity + end + end + def update + if @user.update(user_params[:user]) + render json: @user, status: :ok, location: api_v1_user_url(@user) + else + render json: @user.errors, status: :unprocessable_entity + end + end + + def set_user + @user = User.find(params[:id]) + end + + def destroy + @user = User.find(params[:id]) + @user.destroy + end + private + + def user_params + params.require(:user) + params.permit(user: [:id, :first_name, :last_name, :city, :email, :mentor, :mentee ]) + + end +end diff --git a/server/config/routes.rb b/server/config/routes.rb index a0eae3ee..1bbb84a4 100644 --- a/server/config/routes.rb +++ b/server/config/routes.rb @@ -1,6 +1,14 @@ Rails.application.routes.draw do get 'welcome/index' - resources :users + + resources :users do + end + namespace :api do + namespace :v1 do + resources :users + end + end root 'welcome#index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end + diff --git a/server/db/seeds.rb b/server/db/seeds.rb index ef859b26..a0507882 100644 --- a/server/db/seeds.rb +++ b/server/db/seeds.rb @@ -6,4 +6,5 @@ # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) User.destroy_all -User.create!([{fist_name: 'Mary', last_name: 'Brown',email: 'mary@email.com', mentor: 'True', mentee: 'False'},{fist_name: 'Nuala', last_name: 'May',email: 'nuala@email.com', mentor: 'False', mentee: 'True'},{fist_name: 'Bob', last_name: 'Brown',email: 'bob@email.com', mentor: 'True', mentee: 'False'}]) +User.create!([{first_name: 'Mary', last_name: 'Brown',email: 'mary@email.com',city: 'Sydney', mentor: 'True', mentee: 'False'},{first_name: 'Nuala', last_name: 'May',email: 'nuala@email.com', city: 'Adelaide', mentor: 'False', mentee: 'True'},{first_name: 'Bob', last_name: 'Brown',email: 'bob@email.com', city: 'Perth', mentor: 'True', mentee: 'False'}]) + diff --git a/server/spec/rails_helper.rb b/server/spec/rails_helper.rb new file mode 100644 index 00000000..d73d80bd --- /dev/null +++ b/server/spec/rails_helper.rb @@ -0,0 +1,61 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # 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.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/server/spec/requests/api/v1/users_spec.rb b/server/spec/requests/api/v1/users_spec.rb new file mode 100644 index 00000000..8a91376a --- /dev/null +++ b/server/spec/requests/api/v1/users_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' +RSpec.describe Api::V1::UsersController do + before(:all) do + Rails.application.load_seed + end + #describe "Test Users", :type => :controller do + it 'Test we get a success response to GET' do + get "/api/v1/users" + + json = JSON.parse(response.body) + print(json) + # test for the 200 status-code + expect(response).to be_success + end + + it "Test GET payload size 3 which matches test database" do + get "/api/v1/users" + expect(JSON.parse(response.body)["users"].size).to eq(3) + end + + it 'Test addition new user, which increases count by 1' do + expect{post "http://localhost:3000/api/v1/users/", params: {user: {first_name: 'Sue', last_name: 'Titter',email: 'sue@email.com', city: 'Melbourne', mentor: 'True', mentee: 'False'}}}.to change(User, :count).by(+1) + end + + it 'Test update an existing user and check it was updated' do + get "/api/v1/users" + idval = JSON.parse(response.body)["users"][0]["id"].to_s + patch "/api/v1/users/#{idval}", params: {user: {first_name: 'Robert'}} + + get "/api/v1/users/#{idval}" + + expect(JSON.parse(response.body)["user"]['first_name']).to eql('Robert') + end + + it 'test deletion of existing user and check it was deleted' do + get "/api/v1/users" + idval = JSON.parse(response.body)["users"].select{ |e| e["first_name"]=="Mary"}.first['id'] + delete "/api/v1/users/#{idval}" + get "/api/v1/users" + expect(JSON.parse(response.body)["users"].select{ |e| e["first_name"]=="Mary"}).to eq([]) + end +end diff --git a/server/spec/spec_helper.rb b/server/spec/spec_helper.rb new file mode 100644 index 00000000..ce33d66d --- /dev/null +++ b/server/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# 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, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From 1efb978b8b529ddfe0cb0eee357e238add902d7a Mon Sep 17 00:00:00 2001 From: sibsmc Date: Sun, 23 Jun 2019 21:26:46 +0200 Subject: [PATCH 3/8] followed API tutorial, started JWT but this is incomplete --- server/Gemfile | 10 ++- server/Gemfile.lock | 42 +++++++++++ .../app/assets/javascripts/api_users.coffee | 3 + server/app/assets/stylesheets/api_users.scss | 3 + server/app/auth/authenticate_user.rb | 24 +++++++ server/app/auth/authorize_api_request.rb | 42 +++++++++++ .../controllers/api/v1/users_controller.rb | 2 +- .../app/controllers/api_users_controller.rb | 39 ++++++++++ .../app/controllers/application_controller.rb | 6 +- .../controllers/concerns/exception_handler.rb | 32 +++++++++ server/app/controllers/concerns/response.rb | 5 ++ server/app/helpers/api_users_helper.rb | 2 + server/app/lib/json_web_token.rb | 17 +++++ server/app/lib/message.rb | 33 +++++++++ server/app/models/api_user.rb | 11 +++ server/config/routes.rb | 3 + .../20190623093635_create_api_users.rb | 12 ++++ server/db/schema.rb | 11 ++- .../spec/auth/authorize_api_request_spec.rb | 72 +++++++++++++++++++ .../controllers/api_users_controller_spec.rb | 5 ++ server/spec/factories/api_users.rb | 6 ++ server/spec/helpers/api_users_helper_spec.rb | 15 ++++ server/spec/models/api_user_spec.rb | 15 ++++ server/spec/rails_helper.rb | 25 +++++++ server/spec/requests/.#api_users_spec.rb | 1 + server/spec/requests/api_users_spec.rb | 41 +++++++++++ server/spec/support/controller_spec_helper.rb | 27 +++++++ server/spec/support/request_spec_helper.rb | 6 ++ 28 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 server/app/assets/javascripts/api_users.coffee create mode 100644 server/app/assets/stylesheets/api_users.scss create mode 100644 server/app/auth/authenticate_user.rb create mode 100644 server/app/auth/authorize_api_request.rb create mode 100644 server/app/controllers/api_users_controller.rb create mode 100644 server/app/controllers/concerns/exception_handler.rb create mode 100644 server/app/controllers/concerns/response.rb create mode 100644 server/app/helpers/api_users_helper.rb create mode 100644 server/app/lib/json_web_token.rb create mode 100644 server/app/lib/message.rb create mode 100644 server/app/models/api_user.rb create mode 100644 server/db/migrate/20190623093635_create_api_users.rb create mode 100644 server/spec/auth/authorize_api_request_spec.rb create mode 100644 server/spec/controllers/api_users_controller_spec.rb create mode 100644 server/spec/factories/api_users.rb create mode 100644 server/spec/helpers/api_users_helper_spec.rb create mode 100644 server/spec/models/api_user_spec.rb create mode 120000 server/spec/requests/.#api_users_spec.rb create mode 100644 server/spec/requests/api_users_spec.rb create mode 100644 server/spec/support/controller_spec_helper.rb create mode 100644 server/spec/support/request_spec_helper.rb diff --git a/server/Gemfile b/server/Gemfile index c3892f3a..3ba4895f 100644 --- a/server/Gemfile +++ b/server/Gemfile @@ -30,8 +30,8 @@ gem 'jbuilder', '~> 2.9' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' - +gem 'bcrypt', '~> 3.1.7' +gem 'jwt' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development @@ -56,5 +56,11 @@ group :development do gem 'spring-watcher-listen', '~> 2.0.0' end +group :test do + gem 'factory_bot_rails', '~> 4.0' + gem 'shoulda-matchers', '~> 3.1' + gem 'faker' + gem 'database_cleaner' +end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/server/Gemfile.lock b/server/Gemfile.lock index feebe794..8f7f58b9 100644 --- a/server/Gemfile.lock +++ b/server/Gemfile.lock @@ -45,6 +45,7 @@ GEM addressable (2.6.0) public_suffix (>= 2.0.2, < 4.0) arel (9.0.0) + bcrypt (3.1.13) bindex (0.5.0) bootsnap (1.4.4) msgpack (~> 1.0) @@ -69,9 +70,23 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.1.5) crass (1.0.4) + database_cleaner (1.7.0) diff-lcs (1.3) + equatable (0.6.0) erubi (1.8.0) execjs (2.7.0) + factory_bot (4.11.1) + activesupport (>= 3.0.0) + factory_bot_rails (4.11.1) + factory_bot (~> 4.11.1) + railties (>= 3.0.0) + faker (1.9.4) + i18n (>= 0.7) + pastel (~> 0.7.2) + thor (~> 0.20.0) + tty-pager (~> 0.12.0) + tty-screen (~> 0.6.5) + tty-tree (~> 0.3.0) ffi (1.10.0) globalid (0.4.2) activesupport (>= 4.2.0) @@ -79,6 +94,7 @@ GEM concurrent-ruby (~> 1.0) jbuilder (2.9.1) activesupport (>= 4.2.0) + jwt (2.2.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -99,6 +115,9 @@ GEM nio4r (2.3.1) nokogiri (1.10.3) mini_portile2 (~> 2.4.0) + pastel (0.7.3) + equatable (~> 0.6) + tty-color (~> 0.5) pg (1.1.4) public_suffix (3.1.0) puma (3.12.1) @@ -167,6 +186,8 @@ GEM selenium-webdriver (3.142.3) childprocess (>= 0.5, < 2.0) rubyzip (~> 1.2, >= 1.2.2) + shoulda-matchers (3.1.3) + activesupport (>= 4.0.0) spring (2.1.0) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) @@ -179,9 +200,22 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.4.1) + strings (0.1.5) + strings-ansi (~> 0.1) + unicode-display_width (~> 1.5) + unicode_utils (~> 1.4) + strings-ansi (0.1.0) thor (0.20.3) thread_safe (0.3.6) tilt (2.0.9) + tty-color (0.5.0) + tty-pager (0.12.1) + strings (~> 0.1.4) + tty-screen (~> 0.6) + tty-which (~> 0.4) + tty-screen (0.6.5) + tty-tree (0.3.0) + tty-which (0.4.1) turbolinks (5.2.0) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) @@ -189,6 +223,8 @@ GEM thread_safe (~> 0.1) uglifier (4.1.20) execjs (>= 0.3.0, < 3) + unicode-display_width (1.6.0) + unicode_utils (1.4.0) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) @@ -204,11 +240,16 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1.7) bootsnap (>= 1.1.0) byebug capybara (~> 3.24) coffee-rails (~> 5.0) + database_cleaner + factory_bot_rails (~> 4.0) + faker jbuilder (~> 2.9) + jwt listen (>= 3.0.5, < 3.2) pg puma (~> 3.12) @@ -216,6 +257,7 @@ DEPENDENCIES rspec-rails (~> 3.8) sass-rails (~> 5.0) selenium-webdriver + shoulda-matchers (~> 3.1) spring spring-watcher-listen (~> 2.0.0) sqlite3 (~> 1.4.1) diff --git a/server/app/assets/javascripts/api_users.coffee b/server/app/assets/javascripts/api_users.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/server/app/assets/javascripts/api_users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/server/app/assets/stylesheets/api_users.scss b/server/app/assets/stylesheets/api_users.scss new file mode 100644 index 00000000..f9234056 --- /dev/null +++ b/server/app/assets/stylesheets/api_users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the ApiUsers controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/server/app/auth/authenticate_user.rb b/server/app/auth/authenticate_user.rb new file mode 100644 index 00000000..32cc77b4 --- /dev/null +++ b/server/app/auth/authenticate_user.rb @@ -0,0 +1,24 @@ +# app/auth/authenticate_user.rb +class AuthenticateUser + def initialize(email, password) + @email = email + @password = password + end + + # Service entry point + def call + JsonWebToken.encode(user_id: api_user.id) if api_user + end + + private + + attr_reader :email, :password + + # verify user credentials + def api_user + api_user = ApiUser.find_by(email: email) + return api_user if api_user && api_user.authenticate(password) + # raise Authentication error if credentials are invalid + raise(ExceptionHandler::AuthenticationError, Message.invalid_credentials) + end +end diff --git a/server/app/auth/authorize_api_request.rb b/server/app/auth/authorize_api_request.rb new file mode 100644 index 00000000..a47d87ea --- /dev/null +++ b/server/app/auth/authorize_api_request.rb @@ -0,0 +1,42 @@ +class AuthorizeApiRequest + def initialize(headers = {}) + @headers = headers + end + + # Service entry point - return valid user object + def call + { + api_user: api_user + } + end + + private + + attr_reader :headers + + def api_user + # check if user is in the database + # memoize user object + @api_user ||= ApiUser.find(decoded_auth_token[:api_user_id]) if decoded_auth_token + # handle user not found + rescue ActiveRecord::RecordNotFound => e + # raise custom error + raise( + ExceptionHandler::InvalidToken, + ("#{Message.invalid_token} #{e.message}") + ) + end + + # decode authentication token + def decoded_auth_token + @decoded_auth_token ||= JsonWebToken.decode(http_auth_header) + end + + # check for token in `Authorization` header + def http_auth_header + if headers['Authorization'].present? + return headers['Authorization'].split(' ').last + end + raise(ExceptionHandler::MissingToken, Message.missing_token) + end + end diff --git a/server/app/controllers/api/v1/users_controller.rb b/server/app/controllers/api/v1/users_controller.rb index 09a7ad79..2225b318 100644 --- a/server/app/controllers/api/v1/users_controller.rb +++ b/server/app/controllers/api/v1/users_controller.rb @@ -1,5 +1,5 @@ class Api::V1::UsersController < ApplicationController - skip_before_action :verify_authenticity_token + #skip_before_action :verify_authenticity_token before_action :set_user, only: [:show, :update, :destroy] def index diff --git a/server/app/controllers/api_users_controller.rb b/server/app/controllers/api_users_controller.rb new file mode 100644 index 00000000..9d9bd6ff --- /dev/null +++ b/server/app/controllers/api_users_controller.rb @@ -0,0 +1,39 @@ +class ApiUsersController < ApplicationController + before_action :set_api_user, only: [:show, :update, :destroy] + + #GET /api_users + def index + @api_users = ApiUser.all + json_response(@api_users) + end + + # POST /api_users + def create + @api_user = ApiUser.create!(api_user_params) + json_response(@api_user, :created) + end + + # GET /api_users/:id + def show + json_response(@api_user) + end + + # PUT /api_users/:id + def update + @api_user.update(api_user_params) + head :no_content + end + + # DELETE /api_users/:id + def destroy + @api_user.destroy + head :no_content + end + + private + + def api_user_params + #whitelist params + params.permit(:email, :password_digest) + end +end diff --git a/server/app/controllers/application_controller.rb b/server/app/controllers/application_controller.rb index d1046e97..20fa5a03 100644 --- a/server/app/controllers/application_controller.rb +++ b/server/app/controllers/application_controller.rb @@ -1,4 +1,6 @@ -class ApplicationController < ActionController::Base - protect_from_forgery with: :exception +class ApplicationController < ActionController::API # used to say 'Base' + #protect_from_forgery with: :exception + include Response + include ExceptionHandler end diff --git a/server/app/controllers/concerns/exception_handler.rb b/server/app/controllers/concerns/exception_handler.rb new file mode 100644 index 00000000..fb753857 --- /dev/null +++ b/server/app/controllers/concerns/exception_handler.rb @@ -0,0 +1,32 @@ +module ExceptionHandler + extend ActiveSupport::Concern + + # Define custom error subclasses - rescue catches `StandardErrors` + class AuthenticationError < StandardError; end + class MissingToken < StandardError; end + class InvalidToken < StandardError; end + + included do + rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two + rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request + rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two + rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two + + rescue_from ActiveRecord::RecordInvalid do |e| + json_response({ message: e.message}, :unprocessable_entity) + end + end + + private + + # JSON response with message; Status code 422 - unprocessable entity + def four_twenty_two(e) + json_response({ message: e.message }, :unprocessable_entity) + end + + # JSON response with message; Status code 401 - Unauthorized + def unauthorized_request(e) + json_response({ message: e.message }, :unauthorized) + end + +end diff --git a/server/app/controllers/concerns/response.rb b/server/app/controllers/concerns/response.rb new file mode 100644 index 00000000..cfb0caa4 --- /dev/null +++ b/server/app/controllers/concerns/response.rb @@ -0,0 +1,5 @@ +module Response + def json_response(object, status = :ok) + render json: object, status: status + end +end diff --git a/server/app/helpers/api_users_helper.rb b/server/app/helpers/api_users_helper.rb new file mode 100644 index 00000000..2b56598f --- /dev/null +++ b/server/app/helpers/api_users_helper.rb @@ -0,0 +1,2 @@ +module ApiUsersHelper +end diff --git a/server/app/lib/json_web_token.rb b/server/app/lib/json_web_token.rb new file mode 100644 index 00000000..0d58c7d0 --- /dev/null +++ b/server/app/lib/json_web_token.rb @@ -0,0 +1,17 @@ +class JsonWebToken + #secret to encode and decode token + HMAC_SECRET = Rails.application.secrets.secret_key_base + + def self.encode(payload,exp = 24.hours.from_now) + payload[:exp] = exp.to_i + JWT.encode(payload,HMAC_SECRET) + end + + def self.decode(token) + body = JWT.decode(token,HMAC_SECRET)[0] + HashWithIndifferentAccess.new body + rescue JWT::DecodeError => e + raise ExceptionHandler::InvalidToken, e.message + end +end + diff --git a/server/app/lib/message.rb b/server/app/lib/message.rb new file mode 100644 index 00000000..e47526c2 --- /dev/null +++ b/server/app/lib/message.rb @@ -0,0 +1,33 @@ +class Message + def self.not_found(record = 'record') + "Sorry, #{record} not found." + end + + def self.invalid_credentials + 'Invalid credentials' + end + + def self.invalid_token + 'Invalid token' + end + + def self.missing_token + 'Missing token' + end + + def self.unauthorized + 'Unauthorized request' + end + + def self.account_created + 'Account created successfully' + end + + def self.account_not_created + 'Account could not be created' + end + + def self.expired_token + 'Sorry, your token has expired. Please login to continue.' + end +end diff --git a/server/app/models/api_user.rb b/server/app/models/api_user.rb new file mode 100644 index 00000000..7036e6d3 --- /dev/null +++ b/server/app/models/api_user.rb @@ -0,0 +1,11 @@ +class ApiUser < ApplicationRecord + # encrypt password + has_secure_password + + + # Can write model association here for example + #h has_many :***, dependent: :destroy + + # validation + validates_presence_of :email, :password_digest +end diff --git a/server/config/routes.rb b/server/config/routes.rb index 1bbb84a4..b62a9f78 100644 --- a/server/config/routes.rb +++ b/server/config/routes.rb @@ -3,6 +3,9 @@ resources :users do end + resources :api_users do + #you can add related resources. + end namespace :api do namespace :v1 do resources :users diff --git a/server/db/migrate/20190623093635_create_api_users.rb b/server/db/migrate/20190623093635_create_api_users.rb new file mode 100644 index 00000000..7045754c --- /dev/null +++ b/server/db/migrate/20190623093635_create_api_users.rb @@ -0,0 +1,12 @@ +class CreateApiUsers < ActiveRecord::Migration[5.2] + def change + create_table :api_users do |t| + t.string :name + t.string :email + t.string :password_digest + t.boolean :admin + + t.timestamps + end + end +end diff --git a/server/db/schema.rb b/server/db/schema.rb index e5bc2e73..fd9537b6 100644 --- a/server/db/schema.rb +++ b/server/db/schema.rb @@ -10,7 +10,16 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_06_18_172151) do +ActiveRecord::Schema.define(version: 2019_06_23_093635) do + + create_table "api_users", force: :cascade do |t| + t.string "name" + t.string "email" + t.string "password_digest" + t.boolean "admin" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end create_table "users", force: :cascade do |t| t.datetime "created_at", null: false diff --git a/server/spec/auth/authorize_api_request_spec.rb b/server/spec/auth/authorize_api_request_spec.rb new file mode 100644 index 00000000..476516e5 --- /dev/null +++ b/server/spec/auth/authorize_api_request_spec.rb @@ -0,0 +1,72 @@ +require 'rails_helper' + +RSpec.describe AuthorizeApiRequest do + # Create test user + let(:api_user) { create(:api_user) } + # Mock `Authorization` header + let(:header) { { 'Authorization' => token_generator(api_user.id) } } + # Invalid request subject + subject(:invalid_request_obj) { described_class.new({}) } + # Valid request subject + subject(:request_obj) { described_class.new(header) } + + # Test Suite for AuthorizeApiRequest#call + # This is our entry point into the service class + describe '#call' do + # returns user object when request is valid + context 'when valid request' do + it 'returns user object' do + result = request_obj.call + expect(result[:api_user]).to eq(api_user) + end + end + + # returns error message when invalid request + context 'when invalid request' do + context 'when missing token' do + it 'raises a MissingToken error' do + expect { invalid_request_obj.call } + .to raise_error(ExceptionHandler::MissingToken, 'Missing token') + end + end + + context 'when invalid token' do + subject(:invalid_request_obj) do + # custom helper method `token_generator` + described_class.new('Authorization' => token_generator(5)) + end + + it 'raises an InvalidToken error' do + expect { invalid_request_obj.call } + .to raise_error(ExceptionHandler::InvalidToken, /Invalid token/) + end + end + + context 'when token is expired' do + let(:header) { { 'Authorization' => expired_token_generator(api_user.id) } } + subject(:request_obj) { described_class.new(header) } + + it 'raises ExceptionHandler::ExpiredSignature error' do + expect { request_obj.call } + .to raise_error( + ExceptionHandler::InvalidToken, + /Signature has expired/ + ) + end + end + + context 'fake token' do + let(:header) { { 'Authorization' => 'foobar' } } + subject(:invalid_request_obj) { described_class.new(header) } + + it 'handles JWT::DecodeError' do + expect { invalid_request_obj.call } + .to raise_error( + ExceptionHandler::InvalidToken, + /Not enough or too many segments/ + ) + end + end + end + end +end diff --git a/server/spec/controllers/api_users_controller_spec.rb b/server/spec/controllers/api_users_controller_spec.rb new file mode 100644 index 00000000..9c5361d8 --- /dev/null +++ b/server/spec/controllers/api_users_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ApiUsersController, type: :controller do + +end diff --git a/server/spec/factories/api_users.rb b/server/spec/factories/api_users.rb new file mode 100644 index 00000000..060dd02d --- /dev/null +++ b/server/spec/factories/api_users.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :api_user do + email {Faker::Internet.safe_email} + password_digest {Faker::Lorem.word} #Misc doesn't seem to work {Faker::Misc.password} + end +end diff --git a/server/spec/helpers/api_users_helper_spec.rb b/server/spec/helpers/api_users_helper_spec.rb new file mode 100644 index 00000000..c5174feb --- /dev/null +++ b/server/spec/helpers/api_users_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ApiUsersHelper. For example: +# +# describe ApiUsersHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ApiUsersHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/server/spec/models/api_user_spec.rb b/server/spec/models/api_user_spec.rb new file mode 100644 index 00000000..43cb9cea --- /dev/null +++ b/server/spec/models/api_user_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +RSpec.describe ApiUser, type: :model do + # Association test + # ensure an item record has 1:m relationship with the *** model + + # it { should have_many(:***).dependent(destroy)} + + # Validation test + # ensure columns 'email' and 'password' are present before + + it {should validate_presence_of(:email)} + it {should validate_presence_of(:password_digest)} + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/server/spec/rails_helper.rb b/server/spec/rails_helper.rb index d73d80bd..230d682c 100644 --- a/server/spec/rails_helper.rb +++ b/server/spec/rails_helper.rb @@ -5,8 +5,17 @@ # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' + # Add additional requires below this line. Rails is not loaded until this point! +require 'database_cleaner' +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end @@ -58,4 +67,20 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + + config.include RequestSpecHelper + config.include ControllerSpecHelper + # Add FactoryBot methods + config.include FactoryBot::Syntax::Methods + + config.before(:suite) do + DatabaseCleaner.clean_with(:truncation) + DatabaseCleaner.strategy = :transaction + end + + config.around(:each) do |example| + DatabaseCleaner.cleaning do + example.run + end + end end diff --git a/server/spec/requests/.#api_users_spec.rb b/server/spec/requests/.#api_users_spec.rb new file mode 120000 index 00000000..808d4827 --- /dev/null +++ b/server/spec/requests/.#api_users_spec.rb @@ -0,0 +1 @@ +siobhanmcloughlin@Siobhans-MBP.h.drongo.info.949 \ No newline at end of file diff --git a/server/spec/requests/api_users_spec.rb b/server/spec/requests/api_users_spec.rb new file mode 100644 index 00000000..35040a29 --- /dev/null +++ b/server/spec/requests/api_users_spec.rb @@ -0,0 +1,41 @@ +require 'rails_helper' + +RSpec.describe 'api_users API', type: :request do + + # initialize test data + let!(:api_users){create_list(:api_user, 10)} + let(:api_user_id) {api_users.first.id} + + #Test suite for GET /api_users + describe 'GET /api_users' do + # make HTTP GET request before each example + before {get '/api_users'} + + it 'returns api_users' do + expect(json).not_to be_empty + expect(json.size).to eq(10) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + # Test suite for GET /api_user/:id + # describe 'GET /api_users/:id' do + # before {get "/api_users/#{api_user_id}"} + + # context 'when the record exists' do + # it 'returns the api_user' do + # expect(json).not_to_be_empty + # expect(json['id']).to eq(api_user_id) + # end + + # it 'returns status code 200' do + # expect(response).to have_http_status(200) + # end + + # end + # end + +end diff --git a/server/spec/support/controller_spec_helper.rb b/server/spec/support/controller_spec_helper.rb new file mode 100644 index 00000000..9af5f7ce --- /dev/null +++ b/server/spec/support/controller_spec_helper.rb @@ -0,0 +1,27 @@ +module ControllerSpecHelper + # generate tokens from user id + def token_generator(api_user_id) + JsonWebToken.encode(api_user_id: api_user_id) + end + + # generate expired tokens from user id + def expired_token_generator(api_user_id) + JsonWebToken.encode({ api_user_id: api_user_id }, (Time.now.to_i - 10)) + end + + # return valid headers + def valid_headers + { + "Authorization" => token_generator(api_user.id), + "Content-Type" => "application/json" + } + end + + # return invalid headers + def invalid_headers + { + "Authorization" => nil, + "Content-Type" => "application/json" + } + end +end diff --git a/server/spec/support/request_spec_helper.rb b/server/spec/support/request_spec_helper.rb new file mode 100644 index 00000000..06a2d3ea --- /dev/null +++ b/server/spec/support/request_spec_helper.rb @@ -0,0 +1,6 @@ +module RequestSpecHelper + # Parse JSON reponse to ruby hash + def json + JSON.parse(response.body) + end +end From d6e4fb3fb747e7acb4212ece7882d62f547d8d8a Mon Sep 17 00:00:00 2001 From: sibsmc Date: Sun, 23 Jun 2019 22:27:45 +0200 Subject: [PATCH 4/8] Removed some browser views which are not needed, also fixed api_user so that I can call individual accounts via API --- .../app/controllers/api_users_controller.rb | 6 ++- server/app/controllers/users_controller.rb | 48 ------------------- server/app/controllers/welcome_controller.rb | 4 -- 3 files changed, 5 insertions(+), 53 deletions(-) delete mode 100644 server/app/controllers/users_controller.rb delete mode 100644 server/app/controllers/welcome_controller.rb diff --git a/server/app/controllers/api_users_controller.rb b/server/app/controllers/api_users_controller.rb index 9d9bd6ff..13498379 100644 --- a/server/app/controllers/api_users_controller.rb +++ b/server/app/controllers/api_users_controller.rb @@ -7,7 +7,7 @@ def index json_response(@api_users) end - # POST /api_users + # Post /api_users def create @api_user = ApiUser.create!(api_user_params) json_response(@api_user, :created) @@ -36,4 +36,8 @@ def api_user_params #whitelist params params.permit(:email, :password_digest) end + + def set_api_user + @api_user = ApiUser.find(params[:id]) + end end diff --git a/server/app/controllers/users_controller.rb b/server/app/controllers/users_controller.rb deleted file mode 100644 index ae97728a..00000000 --- a/server/app/controllers/users_controller.rb +++ /dev/null @@ -1,48 +0,0 @@ -class UsersController < ApplicationController - def index - @users = User.all - end - - def show - @user = User.find(params[:id]) - end - - def new - end - - def edit - @user = User.find(params[:id]) - end - - def create - @user = User.new(user_params) - if @user.save - redirect_to @user - else - render 'new' - end - end - - def update - @user = User.find(params[:id]) - - if @user.update(user_params) - redirect_to @user - else - render 'edit' - end - end - - def destroy - @user = User.find(params[:id]) - @user.destroy - - redirect_to users_path - end - - private - def user_params - params.require(:user).permit(:first_name, :last_name, :email, :city, :mentor, :mentee) - end - -end diff --git a/server/app/controllers/welcome_controller.rb b/server/app/controllers/welcome_controller.rb deleted file mode 100644 index f9b859b9..00000000 --- a/server/app/controllers/welcome_controller.rb +++ /dev/null @@ -1,4 +0,0 @@ -class WelcomeController < ApplicationController - def index - end -end From 4e3cff05b6af116879b2031e46c6be7122c9cb74 Mon Sep 17 00:00:00 2001 From: sibsmc Date: Sun, 30 Jun 2019 13:07:47 +0200 Subject: [PATCH 5/8] Created tests for ApiUser and versioned api --- .../api/v1/api_users_controller.rb | 44 +++++ .../app/controllers/api_users_controller.rb | 3 +- .../controllers/concerns/exception_handler.rb | 16 +- server/app/models/.#api_user.rb | 1 + server/app/models/api_user.rb | 3 +- server/app/views/users/edit.html.erb | 21 --- server/app/views/users/index.html.erb | 21 --- server/app/views/users/new.html.erb | 36 ---- server/app/views/users/show.html.erb | 29 ---- server/app/views/welcome/index.html.erb | 10 -- server/config/routes.rb | 5 + server/spec/helpers/api_users_helper_spec.rb | 2 +- server/spec/models/api_user_spec.rb | 3 +- server/spec/requests/.#api_users_spec.rb | 1 - server/spec/requests/api/v1/api_users_spec.rb | 158 ++++++++++++++++++ server/spec/requests/api_users_spec.rb | 149 +++++++++++++++-- 16 files changed, 360 insertions(+), 142 deletions(-) create mode 100644 server/app/controllers/api/v1/api_users_controller.rb create mode 120000 server/app/models/.#api_user.rb delete mode 100644 server/app/views/users/edit.html.erb delete mode 100644 server/app/views/users/index.html.erb delete mode 100644 server/app/views/users/new.html.erb delete mode 100644 server/app/views/users/show.html.erb delete mode 100644 server/app/views/welcome/index.html.erb delete mode 120000 server/spec/requests/.#api_users_spec.rb create mode 100644 server/spec/requests/api/v1/api_users_spec.rb diff --git a/server/app/controllers/api/v1/api_users_controller.rb b/server/app/controllers/api/v1/api_users_controller.rb new file mode 100644 index 00000000..18c084b3 --- /dev/null +++ b/server/app/controllers/api/v1/api_users_controller.rb @@ -0,0 +1,44 @@ +class Api::V1::ApiUsersController < ApplicationController + before_action :set_api_user, only: [:show, :update, :destroy] + + #GET /api_users + def index + @api_users = ApiUser.all + json_response(@api_users) + end + + # Post /api_users + def create + @api_user = ApiUser.create!(api_user_params) + json_response(@api_user, :created) + end + + # GET /api_users/:id + def show + json_response(@api_user) + end + + # PUT /api_users/:id + def update + @api_user.update(api_user_params) + head :no_content + end + + # DELETE /api_users/:id + def destroy + @api_user.destroy + head :no_content + end + + private + + def api_user_params + #whitelist params + #params.permit(:email, :password_digest) + params.require(:api_user).permit(:email, :password_digest) + end + + def set_api_user + @api_user = ApiUser.find(params[:id]) + end +end diff --git a/server/app/controllers/api_users_controller.rb b/server/app/controllers/api_users_controller.rb index 13498379..c6b38b99 100644 --- a/server/app/controllers/api_users_controller.rb +++ b/server/app/controllers/api_users_controller.rb @@ -34,7 +34,8 @@ def destroy def api_user_params #whitelist params - params.permit(:email, :password_digest) + #params.permit(:email, :password_digest) + params.require(:api_user).permit(:email, :password_digest) end def set_api_user diff --git a/server/app/controllers/concerns/exception_handler.rb b/server/app/controllers/concerns/exception_handler.rb index fb753857..a6a10084 100644 --- a/server/app/controllers/concerns/exception_handler.rb +++ b/server/app/controllers/concerns/exception_handler.rb @@ -11,19 +11,27 @@ class InvalidToken < StandardError; end rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two - + rescue_from ActiveRecord::RecordNotFound, with: :four_o_four + rescue_from ActionController::ParameterMissing, with: :four_two_two rescue_from ActiveRecord::RecordInvalid do |e| - json_response({ message: e.message}, :unprocessable_entity) + print(json_response({ message: e.message}, :unprocessable_entity)) end end private - + def four_o_four(e) + json_response({message: e.message}, :not_found) + end + def four_two_two(e) + json_response({message: e.message}, :unprocessable_entity) + end # JSON response with message; Status code 422 - unprocessable entity def four_twenty_two(e) - json_response({ message: e.message }, :unprocessable_entity) + json_response({ message: e.message}, :unprocessable_entity) end + + # JSON response with message; Status code 401 - Unauthorized def unauthorized_request(e) json_response({ message: e.message }, :unauthorized) diff --git a/server/app/models/.#api_user.rb b/server/app/models/.#api_user.rb new file mode 120000 index 00000000..0b38349e --- /dev/null +++ b/server/app/models/.#api_user.rb @@ -0,0 +1 @@ +siobhanmcloughlin@Siobhans-MBP.h.drongo.info.730 \ No newline at end of file diff --git a/server/app/models/api_user.rb b/server/app/models/api_user.rb index 7036e6d3..9cecadbc 100644 --- a/server/app/models/api_user.rb +++ b/server/app/models/api_user.rb @@ -1,6 +1,6 @@ class ApiUser < ApplicationRecord # encrypt password - has_secure_password + #has_secure_password # Can write model association here for example @@ -9,3 +9,4 @@ class ApiUser < ApplicationRecord # validation validates_presence_of :email, :password_digest end + diff --git a/server/app/views/users/edit.html.erb b/server/app/views/users/edit.html.erb deleted file mode 100644 index dab71d2b..00000000 --- a/server/app/views/users/edit.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -

Edit user

- -<%= form_with(model: @user, local: true) do |form| %> - -

- <%= form.label :first_name %>
- <%= form.text_field :first_name%> -

- -

- <%= form.label :last_name %>
- <%= form.text_area :last_name %> -

- -

- <%= form.submit %> -

- -<% end %> - -<%= link_to 'Back', users_path %> diff --git a/server/app/views/users/index.html.erb b/server/app/views/users/index.html.erb deleted file mode 100644 index ac932ea5..00000000 --- a/server/app/views/users/index.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -

Listing Users

- - - - - - - - - <% @users.each do |user| %> - - - - - - - - <% end %> -
FirstLast
<%= user.first_name %><%= user.last_name %><%= link_to 'Show', user_path(user) %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user_path(user), - method: :delete, - data: { confirm: 'Are you sure?' } %>
diff --git a/server/app/views/users/new.html.erb b/server/app/views/users/new.html.erb deleted file mode 100644 index 3d2b0bc8..00000000 --- a/server/app/views/users/new.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -

New User

- - -<%= form_with scope: :user, url: users_path, local: true do |form| %> - - -

- <%= form.label :first_name %>
- <%= form.text_area :first_name %> -

-

- <%= form.label :last_name %>
- <%= form.text_area :last_name %> -

-

- <%= form.label :email %>
- <%= form.text_area :email %> -

-

- <%= form.label :city %>
- <%= form.text_area :city %> -

-

- <%= form.label :mentor %>
- <%= form.check_box :mentor %> -

-

- <%= form.label :mentee %>
- <%= form.check_box :mentee %> -

- -

- <%= form.submit %> -

-<% end %> - diff --git a/server/app/views/users/show.html.erb b/server/app/views/users/show.html.erb deleted file mode 100644 index 94eaa637..00000000 --- a/server/app/views/users/show.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -

- First Name: - <%= @user.first_name %> -

- -

- Last Name: - <%= @user.last_name %> -

-

- Email: - <%= @user.email %> -

-

- City: - <%= @user.city %> -

-

- Mentor: - <%= @user.mentor %> -

-

- Mentee: - <%= @user.mentee %> -

- -<%= link_to 'New', new_user_path(@user) %> | -<%= link_to 'Edit', edit_user_path(@user) %> | -<%= link_to 'Back', users_path %> diff --git a/server/app/views/welcome/index.html.erb b/server/app/views/welcome/index.html.erb deleted file mode 100644 index 2f622239..00000000 --- a/server/app/views/welcome/index.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
-

Welcome to Mentor Mentee Platform

- - <%= image_tag("group.png", :alt => "group avatar", :class => "avatar") %> -
- - - - - diff --git a/server/config/routes.rb b/server/config/routes.rb index b62a9f78..be8c81da 100644 --- a/server/config/routes.rb +++ b/server/config/routes.rb @@ -11,6 +11,11 @@ resources :users end end + namespace :api do + namespace :v1 do + resources :api_users + end + end root 'welcome#index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/server/spec/helpers/api_users_helper_spec.rb b/server/spec/helpers/api_users_helper_spec.rb index c5174feb..2eb820c9 100644 --- a/server/spec/helpers/api_users_helper_spec.rb +++ b/server/spec/helpers/api_users_helper_spec.rb @@ -11,5 +11,5 @@ # end # end RSpec.describe ApiUsersHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + pending "Have not used this helper file function yet #{__FILE__}" end diff --git a/server/spec/models/api_user_spec.rb b/server/spec/models/api_user_spec.rb index 43cb9cea..09a307d9 100644 --- a/server/spec/models/api_user_spec.rb +++ b/server/spec/models/api_user_spec.rb @@ -11,5 +11,6 @@ it {should validate_presence_of(:email)} it {should validate_presence_of(:password_digest)} - pending "add some examples to (or delete) #{__FILE__}" + + pending "At some stage add more contraints and invoke more resource relationships in this file #{__FILE__}" end diff --git a/server/spec/requests/.#api_users_spec.rb b/server/spec/requests/.#api_users_spec.rb deleted file mode 120000 index 808d4827..00000000 --- a/server/spec/requests/.#api_users_spec.rb +++ /dev/null @@ -1 +0,0 @@ -siobhanmcloughlin@Siobhans-MBP.h.drongo.info.949 \ No newline at end of file diff --git a/server/spec/requests/api/v1/api_users_spec.rb b/server/spec/requests/api/v1/api_users_spec.rb new file mode 100644 index 00000000..7c394c24 --- /dev/null +++ b/server/spec/requests/api/v1/api_users_spec.rb @@ -0,0 +1,158 @@ +require 'rails_helper' + +RSpec.describe 'api_users API', type: :request do + + # initialize test data + let!(:api_users){create_list(:api_user, 10)} + let(:api_user_id) {api_users.first.id} + + #Test suite for GET /api_users + describe 'GET /api/v1/api_users' do + # make HTTP GET request before each example + before {get '/api/v1/api_users'} + + it 'returns api_users' do + expect(json).not_to be_empty + expect(json.size).to eq(10) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + # Test suite for GET /api_userss/:id + describe 'GET /api/v1/api_users/:id' do + before { get "/api/v1/api_users/#{api_user_id}/" } + + context 'when api_user exists' do + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + # it 'returns the item' do + # expect(json['id']).to eq(api_user_id) + # end + end + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { get "/api/v1/api_users/#{api_user_id}/" } + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + + + # Test suite POST /api/v1/api_user + describe 'POST /api/v1/api_users' do + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + + context 'when request is valid' do + before { post '/api/v1/api_users', params: valid_attributes} + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + it 'returns same params as entered' do + expect(json['email'], json['password_digest']).to eq("Test_email@email.com","password1") + end + end + end + + context 'when the request is invalid as no params' do + let(:invalid_attributes) { { api_user: { email: nil } }.to_json } + before { post '/api/v1/api_users', params: invalid_attributes } + + it 'returns status code 422' do + expect(response).to have_http_status(422) + end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + context 'when the request is invalid as only some requird params' do + let(:invalid_attributes) { { "api_user": { "email": "email@email.com" } }.to_json } + before { post '/api/v1/api_users', params: invalid_attributes } + + it 'returns status code 422' do + expect(response).to have_http_status(422) + end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + + end + + # Test suite for Patch /api/v1/api_userss/:id + describe 'PATCH /api/v1/api_users/:id' do + let(:valid_attributes) do + # send json payload + { "name": "Bob", "email": "Test_email@email.com", "password_digest": "password1"}.to_json + let(:api_user_id) {api_users.first.id} + context 'when request is valid' do + before { patch "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} + + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end + context 'check that parameters have updated correctly' do + before { get "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} + end + it 'returns same params as entered' do + expect(json['name'], json['email'], json['password_digest']).to eq("Bob", "Test_email@email.com","password1") + end + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + before { patch "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + end + + # Test suite for Delete /api/v1/api_userss/:id + describe 'DELETE /api/v1/api_users/:id' do + + context 'when request made to delete a user' do + let(:api_user_id) {api_users.first.id} + before { delete "/api/v1/api_users/#{api_user_id}/" } + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { delete "/api/v1/api_users/#{api_user_id}/" } + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + +end diff --git a/server/spec/requests/api_users_spec.rb b/server/spec/requests/api_users_spec.rb index 35040a29..f24cc9b7 100644 --- a/server/spec/requests/api_users_spec.rb +++ b/server/spec/requests/api_users_spec.rb @@ -21,21 +21,138 @@ end end - # Test suite for GET /api_user/:id - # describe 'GET /api_users/:id' do - # before {get "/api_users/#{api_user_id}"} - - # context 'when the record exists' do - # it 'returns the api_user' do - # expect(json).not_to_be_empty - # expect(json['id']).to eq(api_user_id) - # end - - # it 'returns status code 200' do - # expect(response).to have_http_status(200) - # end - - # end - # end + # Test suite for GET /api_userss/:id + describe 'GET /api_users/:id' do + before { get "/api_users/#{api_user_id}/" } + + context 'when api_user exists' do + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + # it 'returns the item' do + # expect(json['id']).to eq(api_user_id) + # end + end + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { get "/api_users/#{api_user_id}/" } + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + + + # Test suite POST/api_user + describe 'POST /api_users' do + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + + context 'when request is valid' do + before { post '/api_users', params: valid_attributes} + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + it 'returns same params as entered' do + expect(json['email'], json['password_digest']).to eq("Test_email@email.com","password1") + end + end + end + + context 'when the request is invalid as no params' do + let(:invalid_attributes) { { api_user: { email: nil } }.to_json } + before { post '/api_users', params: invalid_attributes } + + it 'returns status code 422' do + expect(response).to have_http_status(422) + end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + context 'when the request is invalid as only some requird params' do + let(:invalid_attributes) { { "api_user": { "email": "email@email.com" } }.to_json } + before { post '/api_users', params: invalid_attributes } + + it 'returns status code 422' do + expect(response).to have_http_status(422) + end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + + end + + # Test suite for Patch /api_userss/:id + describe 'PATCH /api_users/:id' do + let(:valid_attributes) do + # send json payload + { "name": "Bob", "email": "Test_email@email.com", "password_digest": "password1"}.to_json + let(:api_user_id) {api_users.first.id} + context 'when request is valid' do + before { patch "/api_users/#{api_user_id}/", params: valid_attributes} + + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end + context 'check that parameters have updated correctly' do + before { get "/api_users/#{api_user_id}/", params: valid_attributes} + end + it 'returns same params as entered' do + expect(json['name'], json['email'], json['password_digest']).to eq("Bob", "Test_email@email.com","password1") + end + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + before { patch "/api_users/#{api_user_id}/", params: valid_attributes} + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + end + + # Test suite for Delete /api_userss/:id + describe 'DELETE /api_users/:id' do + + context 'when request made to delete a user' do + let(:api_user_id) {api_users.first.id} + before { delete "/api_users/#{api_user_id}/" } + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { delete "/api_users/#{api_user_id}/" } + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end end From 40057abb6b346460abebfe6a19bfde260af55538 Mon Sep 17 00:00:00 2001 From: sibsmc Date: Sun, 30 Jun 2019 16:33:02 +0200 Subject: [PATCH 6/8] Created database of users called ApiUsers, created api for calling information form this ApiUser database, also created a series of tests for these Apis. --- .../api/v1/api_users_controller.rb | 2 +- .../controllers/api/v1/users_controller.rb | 53 ------ .../app/controllers/api_users_controller.rb | 44 ----- .../controllers/concerns/exception_handler.rb | 20 +-- server/app/helpers/users_helper.rb | 2 - server/app/models/user.rb | 3 - server/config/routes.rb | 10 -- .../.#20190623093635_create_api_users.rb | 1 + .../db/migrate/20190618164807_create_users.rb | 8 - .../20190618171808_add_first_name_to_user.rb | 5 - .../20190618171848_add_last_name_to_user.rb | 5 - .../20190618171912_add_email_to_user.rb | 5 - .../20190618171932_add_city_to_user.rb | 5 - .../20190618172134_add_mentor_to_user.rb | 5 - .../20190618172151_add_mentee_to_user.rb | 5 - .../20190623093635_create_api_users.rb | 8 +- server/db/schema.rb | 16 +- .../controllers/api_users_controller_spec.rb | 2 +- server/spec/factories/api_users.rb | 5 + server/spec/requests/api/v1/api_users_spec.rb | 154 +++++++++-------- server/spec/requests/api/v1/users_spec.rb | 42 ----- server/spec/requests/api_users_spec.rb | 158 ------------------ 22 files changed, 108 insertions(+), 450 deletions(-) delete mode 100644 server/app/controllers/api/v1/users_controller.rb delete mode 100644 server/app/controllers/api_users_controller.rb delete mode 100644 server/app/helpers/users_helper.rb delete mode 100644 server/app/models/user.rb create mode 120000 server/db/migrate/.#20190623093635_create_api_users.rb delete mode 100644 server/db/migrate/20190618164807_create_users.rb delete mode 100644 server/db/migrate/20190618171808_add_first_name_to_user.rb delete mode 100644 server/db/migrate/20190618171848_add_last_name_to_user.rb delete mode 100644 server/db/migrate/20190618171912_add_email_to_user.rb delete mode 100644 server/db/migrate/20190618171932_add_city_to_user.rb delete mode 100644 server/db/migrate/20190618172134_add_mentor_to_user.rb delete mode 100644 server/db/migrate/20190618172151_add_mentee_to_user.rb delete mode 100644 server/spec/requests/api/v1/users_spec.rb delete mode 100644 server/spec/requests/api_users_spec.rb diff --git a/server/app/controllers/api/v1/api_users_controller.rb b/server/app/controllers/api/v1/api_users_controller.rb index 18c084b3..dee4a184 100644 --- a/server/app/controllers/api/v1/api_users_controller.rb +++ b/server/app/controllers/api/v1/api_users_controller.rb @@ -35,7 +35,7 @@ def destroy def api_user_params #whitelist params #params.permit(:email, :password_digest) - params.require(:api_user).permit(:email, :password_digest) + params.require(:api_user).permit(:first_name, :last_name, :city, :email, :password_digest, :mentor, :mentee) end def set_api_user diff --git a/server/app/controllers/api/v1/users_controller.rb b/server/app/controllers/api/v1/users_controller.rb deleted file mode 100644 index 2225b318..00000000 --- a/server/app/controllers/api/v1/users_controller.rb +++ /dev/null @@ -1,53 +0,0 @@ -class Api::V1::UsersController < ApplicationController - #skip_before_action :verify_authenticity_token - before_action :set_user, only: [:show, :update, :destroy] - - def index - @users = User.all - render json: {users: @users} - end - - def show - @user = User.find(params[:id]) - render json: {user: @user} - end - - def new - end - - def edit - @user = User.find(params[:id]) - end - - def create - @user = User.new(user_params[:user]) - if @user.save - render json: @user, status: :created, location: api_v1_user_url(@user) - else - render json: @user.errors, status: :unprocessable_entity - end - end - def update - if @user.update(user_params[:user]) - render json: @user, status: :ok, location: api_v1_user_url(@user) - else - render json: @user.errors, status: :unprocessable_entity - end - end - - def set_user - @user = User.find(params[:id]) - end - - def destroy - @user = User.find(params[:id]) - @user.destroy - end - private - - def user_params - params.require(:user) - params.permit(user: [:id, :first_name, :last_name, :city, :email, :mentor, :mentee ]) - - end -end diff --git a/server/app/controllers/api_users_controller.rb b/server/app/controllers/api_users_controller.rb deleted file mode 100644 index c6b38b99..00000000 --- a/server/app/controllers/api_users_controller.rb +++ /dev/null @@ -1,44 +0,0 @@ -class ApiUsersController < ApplicationController - before_action :set_api_user, only: [:show, :update, :destroy] - - #GET /api_users - def index - @api_users = ApiUser.all - json_response(@api_users) - end - - # Post /api_users - def create - @api_user = ApiUser.create!(api_user_params) - json_response(@api_user, :created) - end - - # GET /api_users/:id - def show - json_response(@api_user) - end - - # PUT /api_users/:id - def update - @api_user.update(api_user_params) - head :no_content - end - - # DELETE /api_users/:id - def destroy - @api_user.destroy - head :no_content - end - - private - - def api_user_params - #whitelist params - #params.permit(:email, :password_digest) - params.require(:api_user).permit(:email, :password_digest) - end - - def set_api_user - @api_user = ApiUser.find(params[:id]) - end -end diff --git a/server/app/controllers/concerns/exception_handler.rb b/server/app/controllers/concerns/exception_handler.rb index a6a10084..639b83d1 100644 --- a/server/app/controllers/concerns/exception_handler.rb +++ b/server/app/controllers/concerns/exception_handler.rb @@ -7,30 +7,24 @@ class MissingToken < StandardError; end class InvalidToken < StandardError; end included do - rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two - rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request - rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two - rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two + rescue_from ActiveRecord::RecordInvalid, with: :four_two_two rescue_from ActiveRecord::RecordNotFound, with: :four_o_four rescue_from ActionController::ParameterMissing, with: :four_two_two - rescue_from ActiveRecord::RecordInvalid do |e| - print(json_response({ message: e.message}, :unprocessable_entity)) - end + #Following handlers not required until authentication set up + #rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request + #rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two + #rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two end private + def four_o_four(e) json_response({message: e.message}, :not_found) end + def four_two_two(e) json_response({message: e.message}, :unprocessable_entity) end - # JSON response with message; Status code 422 - unprocessable entity - def four_twenty_two(e) - json_response({ message: e.message}, :unprocessable_entity) - end - - # JSON response with message; Status code 401 - Unauthorized def unauthorized_request(e) diff --git a/server/app/helpers/users_helper.rb b/server/app/helpers/users_helper.rb deleted file mode 100644 index 2310a240..00000000 --- a/server/app/helpers/users_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module UsersHelper -end diff --git a/server/app/models/user.rb b/server/app/models/user.rb deleted file mode 100644 index 245026de..00000000 --- a/server/app/models/user.rb +++ /dev/null @@ -1,3 +0,0 @@ -class User < ApplicationRecord - -end diff --git a/server/config/routes.rb b/server/config/routes.rb index be8c81da..3bb9069b 100644 --- a/server/config/routes.rb +++ b/server/config/routes.rb @@ -1,16 +1,6 @@ Rails.application.routes.draw do get 'welcome/index' - resources :users do - end - resources :api_users do - #you can add related resources. - end - namespace :api do - namespace :v1 do - resources :users - end - end namespace :api do namespace :v1 do resources :api_users diff --git a/server/db/migrate/.#20190623093635_create_api_users.rb b/server/db/migrate/.#20190623093635_create_api_users.rb new file mode 120000 index 00000000..8eb152c0 --- /dev/null +++ b/server/db/migrate/.#20190623093635_create_api_users.rb @@ -0,0 +1 @@ +siobhanmcloughlin@Siobhans-MBP.h.drongo.info.22986 \ No newline at end of file diff --git a/server/db/migrate/20190618164807_create_users.rb b/server/db/migrate/20190618164807_create_users.rb deleted file mode 100644 index 8006900e..00000000 --- a/server/db/migrate/20190618164807_create_users.rb +++ /dev/null @@ -1,8 +0,0 @@ -class CreateUsers < ActiveRecord::Migration[5.2] - def change - create_table :users do |t| - - t.timestamps - end - end -end diff --git a/server/db/migrate/20190618171808_add_first_name_to_user.rb b/server/db/migrate/20190618171808_add_first_name_to_user.rb deleted file mode 100644 index 86fe66f2..00000000 --- a/server/db/migrate/20190618171808_add_first_name_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddFirstNameToUser < ActiveRecord::Migration[5.2] - def change - add_column :users, :first_name, :string - end -end diff --git a/server/db/migrate/20190618171848_add_last_name_to_user.rb b/server/db/migrate/20190618171848_add_last_name_to_user.rb deleted file mode 100644 index d2c16f40..00000000 --- a/server/db/migrate/20190618171848_add_last_name_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddLastNameToUser < ActiveRecord::Migration[5.2] - def change - add_column :users, :last_name, :string - end -end diff --git a/server/db/migrate/20190618171912_add_email_to_user.rb b/server/db/migrate/20190618171912_add_email_to_user.rb deleted file mode 100644 index 0f5627ee..00000000 --- a/server/db/migrate/20190618171912_add_email_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddEmailToUser < ActiveRecord::Migration[5.2] - def change - add_column :users, :email, :string - end -end diff --git a/server/db/migrate/20190618171932_add_city_to_user.rb b/server/db/migrate/20190618171932_add_city_to_user.rb deleted file mode 100644 index b6d289bc..00000000 --- a/server/db/migrate/20190618171932_add_city_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddCityToUser < ActiveRecord::Migration[5.2] - def change - add_column :users, :city, :string - end -end diff --git a/server/db/migrate/20190618172134_add_mentor_to_user.rb b/server/db/migrate/20190618172134_add_mentor_to_user.rb deleted file mode 100644 index ec926675..00000000 --- a/server/db/migrate/20190618172134_add_mentor_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddMentorToUser < ActiveRecord::Migration[5.2] - def change - add_column :users, :mentor, :boolean - end -end diff --git a/server/db/migrate/20190618172151_add_mentee_to_user.rb b/server/db/migrate/20190618172151_add_mentee_to_user.rb deleted file mode 100644 index ea3e3bd5..00000000 --- a/server/db/migrate/20190618172151_add_mentee_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddMenteeToUser < ActiveRecord::Migration[5.2] - def change - add_column :users, :mentee, :boolean - end -end diff --git a/server/db/migrate/20190623093635_create_api_users.rb b/server/db/migrate/20190623093635_create_api_users.rb index 7045754c..ae065c6d 100644 --- a/server/db/migrate/20190623093635_create_api_users.rb +++ b/server/db/migrate/20190623093635_create_api_users.rb @@ -1,11 +1,15 @@ class CreateApiUsers < ActiveRecord::Migration[5.2] def change create_table :api_users do |t| - t.string :name + + t.string :first_name + t.string :last_name + t.string :city t.string :email t.string :password_digest - t.boolean :admin + t.boolean :mentor + t.boolean :mentee t.timestamps end end diff --git a/server/db/schema.rb b/server/db/schema.rb index fd9537b6..13876ee2 100644 --- a/server/db/schema.rb +++ b/server/db/schema.rb @@ -13,23 +13,15 @@ ActiveRecord::Schema.define(version: 2019_06_23_093635) do create_table "api_users", force: :cascade do |t| - t.string "name" - t.string "email" - t.string "password_digest" - t.boolean "admin" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - create_table "users", force: :cascade do |t| - t.datetime "created_at", null: false - t.datetime "updated_at", null: false t.string "first_name" t.string "last_name" - t.string "email" t.string "city" + t.string "email" + t.string "password_digest" t.boolean "mentor" t.boolean "mentee" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end end diff --git a/server/spec/controllers/api_users_controller_spec.rb b/server/spec/controllers/api_users_controller_spec.rb index 9c5361d8..de265e86 100644 --- a/server/spec/controllers/api_users_controller_spec.rb +++ b/server/spec/controllers/api_users_controller_spec.rb @@ -1,5 +1,5 @@ require 'rails_helper' -RSpec.describe ApiUsersController, type: :controller do +RSpec.describe Api::V1::ApiUsersController, type: :controller do end diff --git a/server/spec/factories/api_users.rb b/server/spec/factories/api_users.rb index 060dd02d..fe613662 100644 --- a/server/spec/factories/api_users.rb +++ b/server/spec/factories/api_users.rb @@ -2,5 +2,10 @@ factory :api_user do email {Faker::Internet.safe_email} password_digest {Faker::Lorem.word} #Misc doesn't seem to work {Faker::Misc.password} + first_name {Faker::Name.first_name} + last_name {Faker::Name.last_name} + city {Faker::Address.city} + mentor {Faker::Boolean.boolean(0.5)} + mentee {Faker::Boolean.boolean(0.5)} end end diff --git a/server/spec/requests/api/v1/api_users_spec.rb b/server/spec/requests/api/v1/api_users_spec.rb index 7c394c24..28687465 100644 --- a/server/spec/requests/api/v1/api_users_spec.rb +++ b/server/spec/requests/api/v1/api_users_spec.rb @@ -1,14 +1,14 @@ require 'rails_helper' -RSpec.describe 'api_users API', type: :request do +RSpec.describe Api::V1::ApiUsersController, type: :request do +#RSpec.describe 'api_users API', type: :request do # initialize test data let!(:api_users){create_list(:api_user, 10)} let(:api_user_id) {api_users.first.id} - #Test suite for GET /api_users + # Test suite for GET /api/v1/api_users describe 'GET /api/v1/api_users' do - # make HTTP GET request before each example before {get '/api/v1/api_users'} it 'returns api_users' do @@ -21,7 +21,7 @@ end end - # Test suite for GET /api_userss/:id + # Test suite for GET /api/v1/api_userss/:id describe 'GET /api/v1/api_users/:id' do before { get "/api/v1/api_users/#{api_user_id}/" } @@ -30,73 +30,81 @@ expect(response).to have_http_status(200) end - # it 'returns the item' do - # expect(json['id']).to eq(api_user_id) - # end + it 'returns the item' do + expect(json['id']).to eq(api_user_id) + end end + context 'when api_user does not exist' do let(:api_user_id) {0} before { get "/api/v1/api_users/#{api_user_id}/" } + it 'returns status code 404' do expect(response).to have_http_status(404) end + it 'returns message informing no user with that id' do expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) end end - end + end # Test suite POST /api/v1/api_user describe 'POST /api/v1/api_users' do - let(:valid_attributes) do - # send json payload - { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json - context 'when request is valid' do - before { post '/api/v1/api_users', params: valid_attributes} - it 'returns status code 201' do - expect(response).to have_http_status(201) - end - it 'returns same params as entered' do - expect(json['email'], json['password_digest']).to eq("Test_email@email.com","password1") - end + context 'when request is valid' do + before { post '/api/v1/api_users', params: valid_attributes} + + it 'returns status code 201' do + expect(response).to have_http_status(201) end + + it 'returns same params as entered' do + expect(json['email'], json['password_digest']).to eq("Test_email@email.com","password1") end + end + end - context 'when the request is invalid as no params' do - let(:invalid_attributes) { { api_user: { email: nil } }.to_json } - before { post '/api/v1/api_users', params: invalid_attributes } + context 'when the request is invalid as no params' do + let(:invalid_attributes) { { api_user: { email: nil } }.to_json } + before { post '/api/v1/api_users', params: invalid_attributes } - it 'returns status code 422' do - expect(response).to have_http_status(422) - end - it 'returns a validation failure message' do - expect(json['message']) - .to match(/param is missing or the value is empty: api_user/) - end - end - context 'when the request is invalid as only some requird params' do - let(:invalid_attributes) { { "api_user": { "email": "email@email.com" } }.to_json } - before { post '/api/v1/api_users', params: invalid_attributes } + it 'returns status code 422' do + expect(response).to have_http_status(422) + end - it 'returns status code 422' do - expect(response).to have_http_status(422) - end - it 'returns a validation failure message' do - expect(json['message']) - .to match(/param is missing or the value is empty: api_user/) - end - end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + + context 'when the request is invalid as only some requird params' do + let(:invalid_attributes) { { "api_user": { "email": "email@email.com" } }.to_json } + before { post '/api/v1/api_users', params: invalid_attributes } + it 'returns status code 422' do + expect(response).to have_http_status(422) end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + + end # Test suite for Patch /api/v1/api_userss/:id describe 'PATCH /api/v1/api_users/:id' do let(:valid_attributes) do # send json payload - { "name": "Bob", "email": "Test_email@email.com", "password_digest": "password1"}.to_json + { "first_name": "Bobby","last_name": "Dylan", "city": "Mexico", "email": "Test_email@email.com", "password_digest": "password1", "mentor": True, "mentee": False}.to_json let(:api_user_id) {api_users.first.id} + context 'when request is valid' do before { patch "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} @@ -104,14 +112,17 @@ expect(response).to have_http_status(204) end end + context 'check that parameters have updated correctly' do before { get "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} end - it 'returns same params as entered' do - expect(json['name'], json['email'], json['password_digest']).to eq("Bob", "Test_email@email.com","password1") - end + + it 'returns same params as entered' do + expect(json['first_name'],json['last_name'],json['city'], json['email'], json['password_digest'],json['mentor'],json['mentee']).to eq("Bobby", "Dylan","Mexico", "Test_email@email.com","password1", True, False) end + end + context 'when api_user does not exist' do let(:api_user_id) {0} let(:valid_attributes) do @@ -119,40 +130,41 @@ { "email": "Test_email@email.com", "password_digest": "password1"}.to_json before { patch "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} - it 'returns status code 404' do - expect(response).to have_http_status(404) - end + it 'returns status code 404' do + expect(response).to have_http_status(404) + end - it 'returns message informing no user with that id' do - expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) - end + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end end end - end + end + # Test suite for Delete /api/v1/api_userss/:id describe 'DELETE /api/v1/api_users/:id' do - context 'when request made to delete a user' do - let(:api_user_id) {api_users.first.id} - before { delete "/api/v1/api_users/#{api_user_id}/" } - it 'returns status code 204' do - expect(response).to have_http_status(204) - end - end - - context 'when api_user does not exist' do - let(:api_user_id) {0} - before { delete "/api/v1/api_users/#{api_user_id}/" } + context 'when request made to delete a user' do + let(:api_user_id) {api_users.first.id} + before { delete "/api/v1/api_users/#{api_user_id}/" } + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end - it 'returns status code 404' do - expect(response).to have_http_status(404) - end + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { delete "/api/v1/api_users/#{api_user_id}/" } + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end - it 'returns message informing no user with that id' do - expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) - end - end - end + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end end diff --git a/server/spec/requests/api/v1/users_spec.rb b/server/spec/requests/api/v1/users_spec.rb deleted file mode 100644 index 8a91376a..00000000 --- a/server/spec/requests/api/v1/users_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'rails_helper' -RSpec.describe Api::V1::UsersController do - before(:all) do - Rails.application.load_seed - end - #describe "Test Users", :type => :controller do - it 'Test we get a success response to GET' do - get "/api/v1/users" - - json = JSON.parse(response.body) - print(json) - # test for the 200 status-code - expect(response).to be_success - end - - it "Test GET payload size 3 which matches test database" do - get "/api/v1/users" - expect(JSON.parse(response.body)["users"].size).to eq(3) - end - - it 'Test addition new user, which increases count by 1' do - expect{post "http://localhost:3000/api/v1/users/", params: {user: {first_name: 'Sue', last_name: 'Titter',email: 'sue@email.com', city: 'Melbourne', mentor: 'True', mentee: 'False'}}}.to change(User, :count).by(+1) - end - - it 'Test update an existing user and check it was updated' do - get "/api/v1/users" - idval = JSON.parse(response.body)["users"][0]["id"].to_s - patch "/api/v1/users/#{idval}", params: {user: {first_name: 'Robert'}} - - get "/api/v1/users/#{idval}" - - expect(JSON.parse(response.body)["user"]['first_name']).to eql('Robert') - end - - it 'test deletion of existing user and check it was deleted' do - get "/api/v1/users" - idval = JSON.parse(response.body)["users"].select{ |e| e["first_name"]=="Mary"}.first['id'] - delete "/api/v1/users/#{idval}" - get "/api/v1/users" - expect(JSON.parse(response.body)["users"].select{ |e| e["first_name"]=="Mary"}).to eq([]) - end -end diff --git a/server/spec/requests/api_users_spec.rb b/server/spec/requests/api_users_spec.rb deleted file mode 100644 index f24cc9b7..00000000 --- a/server/spec/requests/api_users_spec.rb +++ /dev/null @@ -1,158 +0,0 @@ -require 'rails_helper' - -RSpec.describe 'api_users API', type: :request do - - # initialize test data - let!(:api_users){create_list(:api_user, 10)} - let(:api_user_id) {api_users.first.id} - - #Test suite for GET /api_users - describe 'GET /api_users' do - # make HTTP GET request before each example - before {get '/api_users'} - - it 'returns api_users' do - expect(json).not_to be_empty - expect(json.size).to eq(10) - end - - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - end - - # Test suite for GET /api_userss/:id - describe 'GET /api_users/:id' do - before { get "/api_users/#{api_user_id}/" } - - context 'when api_user exists' do - it 'returns status code 200' do - expect(response).to have_http_status(200) - end - - # it 'returns the item' do - # expect(json['id']).to eq(api_user_id) - # end - end - context 'when api_user does not exist' do - let(:api_user_id) {0} - before { get "/api_users/#{api_user_id}/" } - it 'returns status code 404' do - expect(response).to have_http_status(404) - end - it 'returns message informing no user with that id' do - expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) - end - end - end - - - # Test suite POST/api_user - describe 'POST /api_users' do - let(:valid_attributes) do - # send json payload - { "email": "Test_email@email.com", "password_digest": "password1"}.to_json - - context 'when request is valid' do - before { post '/api_users', params: valid_attributes} - it 'returns status code 201' do - expect(response).to have_http_status(201) - end - it 'returns same params as entered' do - expect(json['email'], json['password_digest']).to eq("Test_email@email.com","password1") - end - end - end - - context 'when the request is invalid as no params' do - let(:invalid_attributes) { { api_user: { email: nil } }.to_json } - before { post '/api_users', params: invalid_attributes } - - it 'returns status code 422' do - expect(response).to have_http_status(422) - end - it 'returns a validation failure message' do - expect(json['message']) - .to match(/param is missing or the value is empty: api_user/) - end - end - context 'when the request is invalid as only some requird params' do - let(:invalid_attributes) { { "api_user": { "email": "email@email.com" } }.to_json } - before { post '/api_users', params: invalid_attributes } - - it 'returns status code 422' do - expect(response).to have_http_status(422) - end - it 'returns a validation failure message' do - expect(json['message']) - .to match(/param is missing or the value is empty: api_user/) - end - end - - end - - # Test suite for Patch /api_userss/:id - describe 'PATCH /api_users/:id' do - let(:valid_attributes) do - # send json payload - { "name": "Bob", "email": "Test_email@email.com", "password_digest": "password1"}.to_json - let(:api_user_id) {api_users.first.id} - context 'when request is valid' do - before { patch "/api_users/#{api_user_id}/", params: valid_attributes} - - it 'returns status code 204' do - expect(response).to have_http_status(204) - end - end - context 'check that parameters have updated correctly' do - before { get "/api_users/#{api_user_id}/", params: valid_attributes} - end - it 'returns same params as entered' do - expect(json['name'], json['email'], json['password_digest']).to eq("Bob", "Test_email@email.com","password1") - end - end - - context 'when api_user does not exist' do - let(:api_user_id) {0} - let(:valid_attributes) do - # send json payload - { "email": "Test_email@email.com", "password_digest": "password1"}.to_json - before { patch "/api_users/#{api_user_id}/", params: valid_attributes} - - it 'returns status code 404' do - expect(response).to have_http_status(404) - end - - it 'returns message informing no user with that id' do - expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) - end - end - end - end - - # Test suite for Delete /api_userss/:id - describe 'DELETE /api_users/:id' do - - context 'when request made to delete a user' do - let(:api_user_id) {api_users.first.id} - before { delete "/api_users/#{api_user_id}/" } - it 'returns status code 204' do - expect(response).to have_http_status(204) - end - end - - context 'when api_user does not exist' do - let(:api_user_id) {0} - before { delete "/api_users/#{api_user_id}/" } - - it 'returns status code 404' do - expect(response).to have_http_status(404) - end - - it 'returns message informing no user with that id' do - expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) - end - end - end - -end From 2ed80ee0df6a236c0cdfd8487667152061f2aef4 Mon Sep 17 00:00:00 2001 From: sibsmc Date: Sun, 30 Jun 2019 16:54:22 +0200 Subject: [PATCH 7/8] Creates a user database called 'ApiUser' CRUD APIs have been created A series of tests in Spec have been created for the CRUD APIs --- server/app/controllers/application_controller.rb | 2 +- server/app/models/.#api_user.rb | 1 - .../db/migrate/.#20190623093635_create_api_users.rb | 1 - server/test/controllers/users_controller_test.rb | 7 ------- server/test/fixtures/users.yml | 11 ----------- server/test/models/user_test.rb | 7 ------- 6 files changed, 1 insertion(+), 28 deletions(-) delete mode 120000 server/app/models/.#api_user.rb delete mode 120000 server/db/migrate/.#20190623093635_create_api_users.rb delete mode 100644 server/test/controllers/users_controller_test.rb delete mode 100644 server/test/fixtures/users.yml delete mode 100644 server/test/models/user_test.rb diff --git a/server/app/controllers/application_controller.rb b/server/app/controllers/application_controller.rb index 20fa5a03..02642bbd 100644 --- a/server/app/controllers/application_controller.rb +++ b/server/app/controllers/application_controller.rb @@ -1,4 +1,4 @@ -class ApplicationController < ActionController::API # used to say 'Base' +class ApplicationController < ActionController::API #protect_from_forgery with: :exception include Response include ExceptionHandler diff --git a/server/app/models/.#api_user.rb b/server/app/models/.#api_user.rb deleted file mode 120000 index 0b38349e..00000000 --- a/server/app/models/.#api_user.rb +++ /dev/null @@ -1 +0,0 @@ -siobhanmcloughlin@Siobhans-MBP.h.drongo.info.730 \ No newline at end of file diff --git a/server/db/migrate/.#20190623093635_create_api_users.rb b/server/db/migrate/.#20190623093635_create_api_users.rb deleted file mode 120000 index 8eb152c0..00000000 --- a/server/db/migrate/.#20190623093635_create_api_users.rb +++ /dev/null @@ -1 +0,0 @@ -siobhanmcloughlin@Siobhans-MBP.h.drongo.info.22986 \ No newline at end of file diff --git a/server/test/controllers/users_controller_test.rb b/server/test/controllers/users_controller_test.rb deleted file mode 100644 index 6c3da770..00000000 --- a/server/test/controllers/users_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class UsersControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/server/test/fixtures/users.yml b/server/test/fixtures/users.yml deleted file mode 100644 index 80aed36e..00000000 --- a/server/test/fixtures/users.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/server/test/models/user_test.rb b/server/test/models/user_test.rb deleted file mode 100644 index 82f61e01..00000000 --- a/server/test/models/user_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class UserTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end From 2fffcabdc3ee0af44431e1a01992e5628a44a4ed Mon Sep 17 00:00:00 2001 From: sibsmc Date: Mon, 8 Jul 2019 21:34:34 +0200 Subject: [PATCH 8/8] removed auth and unused .scss and .coffee files --- .../app/assets/javascripts/api_users.coffee | 3 - server/app/assets/javascripts/users.coffee | 3 - server/app/assets/javascripts/welcome.coffee | 3 - server/app/assets/stylesheets/api_users.scss | 3 - server/app/assets/stylesheets/users.scss | 3 - server/app/assets/stylesheets/welcome.scss | 84 ------------------- server/app/auth/authenticate_user.rb | 24 ------ server/app/auth/authorize_api_request.rb | 42 ---------- server/app/lib/json_web_token.rb | 17 ---- server/db/seeds.rb | 10 --- .../spec/auth/authorize_api_request_spec.rb | 72 ---------------- 11 files changed, 264 deletions(-) delete mode 100644 server/app/assets/javascripts/api_users.coffee delete mode 100644 server/app/assets/javascripts/users.coffee delete mode 100644 server/app/assets/javascripts/welcome.coffee delete mode 100644 server/app/assets/stylesheets/api_users.scss delete mode 100644 server/app/assets/stylesheets/users.scss delete mode 100644 server/app/assets/stylesheets/welcome.scss delete mode 100644 server/app/auth/authenticate_user.rb delete mode 100644 server/app/auth/authorize_api_request.rb delete mode 100644 server/app/lib/json_web_token.rb delete mode 100644 server/db/seeds.rb delete mode 100644 server/spec/auth/authorize_api_request_spec.rb diff --git a/server/app/assets/javascripts/api_users.coffee b/server/app/assets/javascripts/api_users.coffee deleted file mode 100644 index 24f83d18..00000000 --- a/server/app/assets/javascripts/api_users.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/server/app/assets/javascripts/users.coffee b/server/app/assets/javascripts/users.coffee deleted file mode 100644 index 24f83d18..00000000 --- a/server/app/assets/javascripts/users.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/server/app/assets/javascripts/welcome.coffee b/server/app/assets/javascripts/welcome.coffee deleted file mode 100644 index 24f83d18..00000000 --- a/server/app/assets/javascripts/welcome.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/server/app/assets/stylesheets/api_users.scss b/server/app/assets/stylesheets/api_users.scss deleted file mode 100644 index f9234056..00000000 --- a/server/app/assets/stylesheets/api_users.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the ApiUsers controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/server/app/assets/stylesheets/users.scss b/server/app/assets/stylesheets/users.scss deleted file mode 100644 index 31a2eacb..00000000 --- a/server/app/assets/stylesheets/users.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Users controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/server/app/assets/stylesheets/welcome.scss b/server/app/assets/stylesheets/welcome.scss deleted file mode 100644 index f931ca65..00000000 --- a/server/app/assets/stylesheets/welcome.scss +++ /dev/null @@ -1,84 +0,0 @@ -//base css -* { - margin: 0; - padding: 0; - box-sizing: inherit; -} -body -{ - box-sizing: border-box; -} -html{ - font-size: 62.5%; - font-family: Arial, Helvetica, sans-serif; -} -// base css ends here - -//color variables -$color-primary: #DC143C; -$color-ligt-grey: #eeeeee; -$color-black: #0000; -$color-white: #ffffff; -$color-btn-gradient-dark: #ec008c; -$color-btn-gradient-light: #fc6767; -//color variables end here - -//welcome page css starts here -h1{ - letter-spacing: 0.1em; - font-size: 3.5rem; - margin: 5rem 0; - color: $color-primary; - text-shadow: 0 0.2rem 0.8rem $color-ligt-grey; -} - -.homeContainer{ - text-align: center; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -} -.btn { - &, - &:link, - &:visited{ - text-transform: uppercase; - text-decoration: none; - padding: 15px 40px; - font-size: 2rem; - cursor: pointer; - text-shadow: 0 0.1rem 0.2rem $color-ligt-grey; - letter-spacing: 0.2rem; - transition: all 0.2s; - } - &:hover { - transform: translateY(-3px); - box-shadow: 0 10px 20px rgba($color-black, 0.2); - &:after { - transform: scaleX(1.4) scaleY(1.6); - opacity: 0; - } - } - &:active, - &:focus - { - outline: none; - transform: translateY(-2px); - box-shadow: 0 5px 10px rgba($color-black, 0.2); - } - &__signup{ - font-size: 2rem; - background: #ec008c; /* fallback for old browsers */ - background: -webkit-linear-gradient(to right, $color-btn-gradient-dark, $color-btn-gradient-light); - background: linear-gradient(to right, $color-btn-gradient-dark, $color-btn-gradient-light); - border-radius: 100px; - color: $color-white; - } -} - -.avatar{ - margin: 4rem 0; - max-width: 100%; - max-height: 100%; -} \ No newline at end of file diff --git a/server/app/auth/authenticate_user.rb b/server/app/auth/authenticate_user.rb deleted file mode 100644 index 32cc77b4..00000000 --- a/server/app/auth/authenticate_user.rb +++ /dev/null @@ -1,24 +0,0 @@ -# app/auth/authenticate_user.rb -class AuthenticateUser - def initialize(email, password) - @email = email - @password = password - end - - # Service entry point - def call - JsonWebToken.encode(user_id: api_user.id) if api_user - end - - private - - attr_reader :email, :password - - # verify user credentials - def api_user - api_user = ApiUser.find_by(email: email) - return api_user if api_user && api_user.authenticate(password) - # raise Authentication error if credentials are invalid - raise(ExceptionHandler::AuthenticationError, Message.invalid_credentials) - end -end diff --git a/server/app/auth/authorize_api_request.rb b/server/app/auth/authorize_api_request.rb deleted file mode 100644 index a47d87ea..00000000 --- a/server/app/auth/authorize_api_request.rb +++ /dev/null @@ -1,42 +0,0 @@ -class AuthorizeApiRequest - def initialize(headers = {}) - @headers = headers - end - - # Service entry point - return valid user object - def call - { - api_user: api_user - } - end - - private - - attr_reader :headers - - def api_user - # check if user is in the database - # memoize user object - @api_user ||= ApiUser.find(decoded_auth_token[:api_user_id]) if decoded_auth_token - # handle user not found - rescue ActiveRecord::RecordNotFound => e - # raise custom error - raise( - ExceptionHandler::InvalidToken, - ("#{Message.invalid_token} #{e.message}") - ) - end - - # decode authentication token - def decoded_auth_token - @decoded_auth_token ||= JsonWebToken.decode(http_auth_header) - end - - # check for token in `Authorization` header - def http_auth_header - if headers['Authorization'].present? - return headers['Authorization'].split(' ').last - end - raise(ExceptionHandler::MissingToken, Message.missing_token) - end - end diff --git a/server/app/lib/json_web_token.rb b/server/app/lib/json_web_token.rb deleted file mode 100644 index 0d58c7d0..00000000 --- a/server/app/lib/json_web_token.rb +++ /dev/null @@ -1,17 +0,0 @@ -class JsonWebToken - #secret to encode and decode token - HMAC_SECRET = Rails.application.secrets.secret_key_base - - def self.encode(payload,exp = 24.hours.from_now) - payload[:exp] = exp.to_i - JWT.encode(payload,HMAC_SECRET) - end - - def self.decode(token) - body = JWT.decode(token,HMAC_SECRET)[0] - HashWithIndifferentAccess.new body - rescue JWT::DecodeError => e - raise ExceptionHandler::InvalidToken, e.message - end -end - diff --git a/server/db/seeds.rb b/server/db/seeds.rb deleted file mode 100644 index a0507882..00000000 --- a/server/db/seeds.rb +++ /dev/null @@ -1,10 +0,0 @@ -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). -# -# Examples: -# -# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) -# Character.create(name: 'Luke', movie: movies.first) -User.destroy_all -User.create!([{first_name: 'Mary', last_name: 'Brown',email: 'mary@email.com',city: 'Sydney', mentor: 'True', mentee: 'False'},{first_name: 'Nuala', last_name: 'May',email: 'nuala@email.com', city: 'Adelaide', mentor: 'False', mentee: 'True'},{first_name: 'Bob', last_name: 'Brown',email: 'bob@email.com', city: 'Perth', mentor: 'True', mentee: 'False'}]) - diff --git a/server/spec/auth/authorize_api_request_spec.rb b/server/spec/auth/authorize_api_request_spec.rb deleted file mode 100644 index 476516e5..00000000 --- a/server/spec/auth/authorize_api_request_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'rails_helper' - -RSpec.describe AuthorizeApiRequest do - # Create test user - let(:api_user) { create(:api_user) } - # Mock `Authorization` header - let(:header) { { 'Authorization' => token_generator(api_user.id) } } - # Invalid request subject - subject(:invalid_request_obj) { described_class.new({}) } - # Valid request subject - subject(:request_obj) { described_class.new(header) } - - # Test Suite for AuthorizeApiRequest#call - # This is our entry point into the service class - describe '#call' do - # returns user object when request is valid - context 'when valid request' do - it 'returns user object' do - result = request_obj.call - expect(result[:api_user]).to eq(api_user) - end - end - - # returns error message when invalid request - context 'when invalid request' do - context 'when missing token' do - it 'raises a MissingToken error' do - expect { invalid_request_obj.call } - .to raise_error(ExceptionHandler::MissingToken, 'Missing token') - end - end - - context 'when invalid token' do - subject(:invalid_request_obj) do - # custom helper method `token_generator` - described_class.new('Authorization' => token_generator(5)) - end - - it 'raises an InvalidToken error' do - expect { invalid_request_obj.call } - .to raise_error(ExceptionHandler::InvalidToken, /Invalid token/) - end - end - - context 'when token is expired' do - let(:header) { { 'Authorization' => expired_token_generator(api_user.id) } } - subject(:request_obj) { described_class.new(header) } - - it 'raises ExceptionHandler::ExpiredSignature error' do - expect { request_obj.call } - .to raise_error( - ExceptionHandler::InvalidToken, - /Signature has expired/ - ) - end - end - - context 'fake token' do - let(:header) { { 'Authorization' => 'foobar' } } - subject(:invalid_request_obj) { described_class.new(header) } - - it 'handles JWT::DecodeError' do - expect { invalid_request_obj.call } - .to raise_error( - ExceptionHandler::InvalidToken, - /Not enough or too many segments/ - ) - end - end - end - end -end