From 22e12d8fc35108248eb2740982da25ffac359dba Mon Sep 17 00:00:00 2001 From: Oleksii Kriuchykhin Date: Tue, 31 Jul 2018 13:40:27 +0200 Subject: [PATCH] Add GET teams and team API endpoints [SCI-2611][SCI-2612] --- Gemfile | 4 +- Gemfile.lock | 9 ++++ app/controllers/api/v1/base_controller.rb | 15 ++++++ app/controllers/api/v1/teams_controller.rb | 24 +++++++++ app/serializers/api/v1/team_serializer.rb | 7 +++ .../initializers/active_model_serializer.rb | 2 + config/initializers/kaminari_config.rb | 12 +++++ config/routes.rb | 4 ++ spec/rails_helper.rb | 1 + spec/requests/api/v1/teams_controller_spec.rb | 54 +++++++++++++++++++ spec/support/api_helper.rb | 2 +- 11 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 app/controllers/api/v1/base_controller.rb create mode 100644 app/controllers/api/v1/teams_controller.rb create mode 100644 app/serializers/api/v1/team_serializer.rb create mode 100644 config/initializers/active_model_serializer.rb create mode 100644 config/initializers/kaminari_config.rb create mode 100644 spec/requests/api/v1/teams_controller_spec.rb diff --git a/Gemfile b/Gemfile index 3bbb09b1c..7a03bc99b 100644 --- a/Gemfile +++ b/Gemfile @@ -19,8 +19,10 @@ gem 'omniauth' gem 'omniauth-linkedin-oauth2' # Gems for API implementation -gem 'jwt', '~> 1.5' +gem 'active_model_serializers', '~> 0.10.7' gem 'json-jwt' +gem 'jwt', '~> 1.5' +gem 'kaminari' # JS datetime library, requirement of datetime picker gem 'momentjs-rails', '~> 2.17.1' diff --git a/Gemfile.lock b/Gemfile.lock index 8319bd721..38e3270ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -74,6 +74,11 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) + active_model_serializers (0.10.7) + actionpack (>= 4.1, < 6) + activemodel (>= 4.1, < 6) + case_transform (>= 0.2) + jsonapi-renderer (>= 0.1.1.beta1, < 0.3) activejob (5.1.6) activesupport (= 5.1.6) globalid (>= 0.3.6) @@ -149,6 +154,8 @@ GEM capybara-email (2.5.0) capybara (~> 2.4) mail + case_transform (0.2) + activesupport climate_control (0.2.0) cliver (0.3.2) coderay (1.1.2) @@ -256,6 +263,7 @@ GEM addressable (>= 2.4) json_matchers (0.7.2) json-schema (~> 2.7) + jsonapi-renderer (0.2.0) jwt (1.5.6) kaminari (1.1.1) activesupport (>= 4.1.0) @@ -525,6 +533,7 @@ PLATFORMS ruby DEPENDENCIES + active_model_serializers (~> 0.10.7) activerecord-import ajax-datatables-rails (~> 0.3.1) aspector diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb new file mode 100644 index 000000000..ff5eafd77 --- /dev/null +++ b/app/controllers/api/v1/base_controller.rb @@ -0,0 +1,15 @@ +module Api + module V1 + class BaseController < ApiController + rescue_from ActionController::ParameterMissing do |e| + logger.error e.message + render json: {}, status: :bad_request + end + + rescue_from ActiveRecord::RecordNotFound do |e| + logger.error e.message + render json: {}, status: :not_found + end + end + end +end diff --git a/app/controllers/api/v1/teams_controller.rb b/app/controllers/api/v1/teams_controller.rb new file mode 100644 index 000000000..18faa4f61 --- /dev/null +++ b/app/controllers/api/v1/teams_controller.rb @@ -0,0 +1,24 @@ +module Api + module V1 + class TeamsController < BaseController + before_action :load_team, only: :show + + def index + teams = current_user.teams.page(params[:page]).per(params[:page_size]) + render json: teams, each_serializer: TeamSerializer + end + + def show + render json: @team, serializer: TeamSerializer + end + + private + + def load_team + @team = Team.find(params.require(:id)) + return render json: {}, status: :not_found unless @team + return render json: {}, status: :forbidden unless can_read_team?(@team) + end + end + end +end diff --git a/app/serializers/api/v1/team_serializer.rb b/app/serializers/api/v1/team_serializer.rb new file mode 100644 index 000000000..692b66538 --- /dev/null +++ b/app/serializers/api/v1/team_serializer.rb @@ -0,0 +1,7 @@ +module Api + module V1 + class TeamSerializer < ActiveModel::Serializer + attributes :id, :name, :description, :space_taken + end + end +end diff --git a/config/initializers/active_model_serializer.rb b/config/initializers/active_model_serializer.rb new file mode 100644 index 000000000..032c70089 --- /dev/null +++ b/config/initializers/active_model_serializer.rb @@ -0,0 +1,2 @@ +ActiveModelSerializers.config.adapter = :json_api +ActiveModelSerializers.config.key_transform = :unaltered diff --git a/config/initializers/kaminari_config.rb b/config/initializers/kaminari_config.rb new file mode 100644 index 000000000..373150f63 --- /dev/null +++ b/config/initializers/kaminari_config.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +Kaminari.configure do |config| + config.default_per_page = 10 + # config.max_per_page = nil + # config.window = 4 + # config.outer_window = 0 + # config.left = 0 + # config.right = 0 + # config.page_method_name = :page + # config.param_name = :page + # config.params_on_first_page = false +end diff --git a/config/routes.rb b/config/routes.rb index 7807dca3b..ddca6879a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -540,6 +540,10 @@ Rails.application.routes.draw do get 'health', to: 'api#health' get 'status', to: 'api#status' post 'auth/token', to: 'api#authenticate' + namespace :v1 do + resources :teams, only: %i(index show) do + end + end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 518501f4c..9e36e7828 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -91,6 +91,7 @@ RSpec.configure do |config| # Devise config.include Devise::Test::ControllerHelpers, type: :controller config.include ApiHelper, type: :controller + config.include ApiHelper, type: :request config.extend ControllerMacros, type: :controller config.filter_run_excluding broken: true diff --git a/spec/requests/api/v1/teams_controller_spec.rb b/spec/requests/api/v1/teams_controller_spec.rb new file mode 100644 index 000000000..907f42176 --- /dev/null +++ b/spec/requests/api/v1/teams_controller_spec.rb @@ -0,0 +1,54 @@ +require 'rails_helper' + +RSpec.describe "Api::V1::TeamsController", type: :request do + before :all do + @user = create(:user) + @teams = create_list(:team, 3, created_by: @user) + create(:user_team, user: @user, team: @teams.second, role: 2) + create(:user_team, user: @user, team: @teams.third, role: 2) + @valid_headers = + { 'Authorization': 'Bearer ' + generate_token(@user.id) } + end + + describe 'GET teams, #index' do + it 'Response with correct teams' do + hash_body = nil + get api_v1_teams_path, headers: @valid_headers + expect { hash_body = json }.not_to raise_exception + expect(hash_body[:data]).to match( + ActiveModelSerializers::SerializableResource + .new(@user.teams, each_serializer: Api::V1::TeamSerializer) + .as_json[:data] + ) + end + end + + describe 'GET team, #show' do + it 'When valid request, user is member of the team' do + hash_body = nil + get api_v1_team_path(id: @teams.second.id), headers: @valid_headers + expect { hash_body = json }.not_to raise_exception + expect(hash_body[:data]).to match( + ActiveModelSerializers::SerializableResource + .new(@teams.second, serializer: Api::V1::TeamSerializer) + .as_json[:data] + ) + end + + it 'When invalid request, user in not member of the team' do + hash_body = nil + get api_v1_team_path(id: @teams.first.id), headers: @valid_headers + expect(response).to have_http_status(403) + expect { hash_body = json }.not_to raise_exception + expect(hash_body).to match({}) + end + + it 'When invalid request, non existing team' do + hash_body = nil + get api_v1_team_path(id: 123), headers: @valid_headers + expect(response).to have_http_status(404) + expect { hash_body = json }.not_to raise_exception + expect(hash_body).to match({}) + end + end +end diff --git a/spec/support/api_helper.rb b/spec/support/api_helper.rb index fbbd15928..5fc55cf5c 100644 --- a/spec/support/api_helper.rb +++ b/spec/support/api_helper.rb @@ -12,6 +12,6 @@ module ApiHelper end def json - JSON.parse(response.body) + ActiveSupport::HashWithIndifferentAccess.new(JSON.parse(response.body)) end end