[RUBY] Build a bulletin board API with authentication authorization in Rails # 13 Add authentication header

Building a bulletin board API with authentication authorization in Rails 6 # 12 Association of user and post

factory fix

I will fix it from the factory.

The one that is flying the most

ActiveRecord::RecordInvalid:
        Validation failed: User must exist

That's the error. This is caused by the user_id becoming nil when create (: post) is done.

Let's crush it by modifying the factory.

spec/factories/posts.rb


   factory :post do
     subject { "MyString" }
     body { "MyText" }
+
+    after(:build) do |obj|
+      obj.user = build(:user) if obj.user.nil?
+    end
   end

ʻAfter (: build)` is executed after build or create. You can eliminate the User must exist error by putting the built user in post.user.

In addition, by doing ʻif obj.user.nil?, When a specific user is passed and created like create (: post, user: user)`, it is prevented from being overwritten by internal processing. I will.

Actually, in a simpler way, most of them can be crushed for the time being.

spec/factories/posts.rb


   factory :post do
     subject { "MyString" }
     body { "MyText" }
+    user
   end

However, this method is good when create (: post) is done, but since user returns with nil when build (: post) is done, the former is supported.

Modify request spec and controller

spec/requests/v1/posts_request_spec.rb


   describe "POST /v1/posts#create" do
+    let(:authorized_headers) do
+      user = create(:user)
+      post v1_user_session_url, params: { email: user.email, password: "password" }
+      headers = {}
+      headers["access-token"] = response.header["access-token"]
+      headers["client"] = response.header["client"]
+      headers["uid"] = response.header["uid"]
+      headers
+    end
     let(:new_post) do
       attributes_for(:post, subject: "create_subject test", body: "create_body test")
     end
     it "Normal response code is returned" do
-      post v1_posts_url, params: new_post
+      post v1_posts_url, params: new_post, headers: authorized_headers
       expect(response.status).to eq 200
     end
     it "One more case will be returned" do
       expect do
-        post v1_posts_url, params: new_post
+        post v1_posts_url, params: new_post, headers: authorized_headers
       end.to change { Post.count }.by(1)
     end
     it "subject,body returns correctly" do
-      post v1_posts_url, params: new_post
+      post v1_posts_url, params: new_post, headers: authorized_headers
       json = JSON.parse(response.body)
       expect(json["post"]["subject"]).to eq("create_subject test")
       expect(json["post"]["body"]).to eq("create_body test")
     end
     it "Errors are returned when the parameter is invalid" do
-      post v1_posts_url, params: {}
+      post v1_posts_url, params: {}, headers: authorized_headers
       json = JSON.parse(response.body)
       expect(json.key?("errors")).to be true
     end
   end

Generate a user and log in based on that user information. By adding the 3 keys for authentication in the response header to headers and posting, access is performed while being authenticated as a create (: user) user. However, the error remains because the controller side has not been fixed yet.

Fix controller

app/controllers/v1/posts_controller.rb


     def create
-      post = Post.new(post_params)
+      post = current_v1_user.posts.new(post_params)
       if post.save

The above fix should pass the test.

To explain the behavior, first of all, since the authentication information is passed by headers, the method called current_v1_user can be used in controller. This will return the logged-in user instance. In other words, current_v1_user.posts.new instantiates the post associated with the logged-in user. This will create a post for the logged in user.

Move rspec authenticated header acquisition process to helper

The test has passed, but when implementing authorization with Pundit in the future, if you write the process to get the authenticated header each time, maintainability will decrease, so move to the helper for spec I will.

The helper for spec is generally placed in spec / support, so create a directory.

$ mkdir spec/support
$ touch spec/support/authorization_spec_helper.rb

I will bring the processing that was suitable for rspec to this place.

spec/support/authorization_spec_helper.rb


# frozen_string_literal: true

#
#Authentication helper
#
module AuthorizationSpecHelper
  def authorized_user_headers
    user = create(:user)
    post v1_user_session_url, params: { email: user.email, password: "password" }
    headers = {}
    headers["access-token"] = response.header["access-token"]
    headers["client"] = response.header["client"]
    headers["uid"] = response.header["uid"]
    headers
  end
end

If you just place it under spec / support, it will not be read by itself, so modify spec / rails_helper.rb.

spec/rails_helper.rb


-# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
+Dir[Rails.root.join("spec", "support", "**", "*.rb")].sort.each { |f| require f }
...
 RSpec.configure do |config|
...

+  config.include(AuthorizationSpecHelper, type: :request)
 end

Enable the process of reading under spec / support that was commented out, and include AuthorizationSpecHelper. By writing as above, only the request spec will be valid.

spec/requests/v1/posts_request_spec.rb


...
 require "rails_helper"
 
 RSpec.describe "V1::Posts", type: :request do
+  let(:authorized_headers) do
+    authorized_user_headers
+  end
...
   describe "POST /v1/posts#create" do
-    let(:authorized_headers) do
-      user = create(:user)
-      post v1_user_session_url, params: { email: user.email, password: "password" }
-      headers = {}
-      headers["access-token"] = response.header["access-token"]
-      headers["client"] = response.header["client"]
-      headers["uid"] = response.header["uid"]
-      headers
-    end
...

The rest is completed with the above correspondence. If the test result remains green, it's OK for the time being.

All the tests pass, but the test code is inadequate in the first place. If you hit #create when you are not authenticated, you will get a 500 error, and the current specifications that you can update or delete posts other than yourself are troublesome, so we will finally add authorization next time.

Next time, we will maintain the seed. That's all for today.

Continued

Building a bulletin board API with authentication authorization in Rails 6 # 14 Seeing execution time display [To the serial table of contents]

Recommended Posts

Build a bulletin board API with authentication authorization in Rails # 13 Add authentication header
Build a bulletin board API with authentication authorization in Rails # 17 Add administrator privileges
Build a bulletin board API with authentication authorization in Rails 6 # 5 controller, routes implementation
Introduced # 10 devise_token_auth to build a bulletin board API with authentication authorization in Rails 6
Introducing # 15 pundit to build a bulletin board API with authentication authorization in Rails 6
Build a bulletin board API with authentication authorization in Rails 6 # 14 seed Execution time display
# 16 policy setting to build bulletin board API with authentication authorization in Rails 6
# 8 seed implementation to build bulletin board API with authentication authorization in Rails 6
Introduced # 9 serializer to build bulletin board API with authentication authorization in Rails 6
Build a bulletin board API with authentication authorization in Rails # 12 Association of user and post
# 6 show, create implementation to build bulletin board API with authentication authorization in Rails 6
Build a bulletin board API with authentication authorization with Rails 6 # 2 Introducing git and rubocop
# 7 update, destroy implementation to build bulletin board API with authentication authorization in Rails 6
Build a bulletin board API with authentication authorization in Rails 6 # 11 User model test and validation added
Build a bulletin board API with authentication and authorization with Rails # 18 ・ Implementation of final user controller
Build a bulletin board API with authentication authorization with Rails 6 # 3 RSpec, FactoryBot introduced and post model
Building a bulletin board API with authentication authorization with Rails 6 Validation and test implementation of # 4 post
Add a search function in Rails.
I tried to make a group function (bulletin board) with Rails
Create a SPA with authentication function with Rails API mode + devise_token_auth + Vue.js 3 (Rails edition)
Try to create a bulletin board in Java
Add a project in any folder with Gradle
How to build API with GraphQL and Rails
[Note] Build a Python3 environment with Docker in EC2
[How to insert a video in haml with Rails]
Build Rails (API) x MySQL x Nuxt.js environment with Docker
[Ruby on Rails] Add a column with a foreign key constraint
Create a SlackBot with AWS lambda & API Gateway in Java
I came across a guy with two dots in Rails
How to set up a proxy with authentication in Feign
Japaneseize using i18n with Rails
Implement LTI authentication in Rails
API creation with Rails + GraphQL
[Implementation procedure] Create a user authentication function using sorcery in Rails
I implemented Rails API with TDD by RSpec. part2 -user authentication-
How to rename a model with foreign key constraints in Rails
I implemented Rails API with TDD by RSpec. part3-Action implementation with authentication-
Steps to build a Ruby on Rails development environment with Vagrant