Skip to content

Commit

Permalink
API request tracking & limiting
Browse files Browse the repository at this point in the history
  • Loading branch information
yshmarov committed Apr 23, 2023
1 parent 29d7c64 commit 92812d4
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ MySecretApiToken = api_token.token

```sh
# check connection
curl -X GET localhost:3000/api/v1/home/index.json -H "Authorization: Bearer MySecretApiToken"
curl -v -X GET localhost:3000/api/v1/home/index.json -H "Authorization: Bearer MySecretApiToken"

# posts#index
curl -X GET localhost:3000/api/v1/posts.json -H "Authorization: Bearer MySecretApiToken"
Expand Down
20 changes: 17 additions & 3 deletions app/controllers/api/v1/authenticated_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ class Api::V1::AuthenticatedController < ActionController::Base
protect_from_forgery with: :null_session

before_action :authenticate
before_action :check_api_limit
before_action :log_api_request

attr_reader :current_api_token, :current_user
attr_reader :current_user

def authenticate
authenticate_user_with_token || handle_bad_authentication
Expand All @@ -15,11 +17,23 @@ def authenticate

def authenticate_user_with_token
authenticate_with_http_token do |token, options|
@current_api_token = ApiToken.where(active: true).find_by(token: token)
@current_user = @current_api_token&.user
current_api_token = ApiToken.where(active: true).find_by(token: token)
@current_user = current_api_token&.user
current_api_token.update(last_used_at: Time.zone.now)
end
end

def check_api_limit
if current_user.api_request_limit_exceeded?
render json: { message: "API limit exceeded" }, status: :too_many_requests
end
end

def log_api_request
current_user.api_requests.create!(path: request.path, method: request.method)
response.headers['X-SupeRails-Shop-Api-Call-Limit'] = "#{current_user.api_requests_within_last_30_days}/#{User::MAX_API_REQUESTS_PER_30_DAYS}"
end

def handle_bad_authentication
render json: { message: "Bad credentials" }, status: :unauthorized
end
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/api/v1/home_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Api::V1::HomeController < Api::V1::AuthenticatedController
def index
# render json: { message: "Hello API world!" }
render json: { current_api_token: current_api_token.id, current_user: current_user.email }
# render json: { message: "Hello API world" }
render json: { current_user: current_user.email }
end
end
3 changes: 3 additions & 0 deletions app/models/api_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ApiRequest < ApplicationRecord
belongs_to :user
end
11 changes: 11 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,16 @@ class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :api_tokens
has_many :api_requests
has_many :posts

MAX_API_REQUESTS_PER_30_DAYS = 50

def api_requests_within_last_30_days
api_requests.where("created_at > ?", 30.days.ago).count
end

def api_request_limit_exceeded?
api_requests_within_last_30_days >= MAX_API_REQUESTS_PER_30_DAYS
end
end
11 changes: 11 additions & 0 deletions db/migrate/20230423131830_create_api_requests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateApiRequests < ActiveRecord::Migration[7.0]
def change
create_table :api_requests do |t|
t.references :user, null: false, foreign_key: true
t.string :path, null: false
t.string :method, null: false

t.timestamps
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20230423133015_add_last_used_at_to_api_tokens.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddLastUsedAtToApiTokens < ActiveRecord::Migration[7.0]
def change
add_column :api_tokens, :last_used_at, :datetime
end
end
13 changes: 12 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions test/fixtures/api_requests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
user: one
path: MyString
method: MyString

two:
user: two
path: MyString
method: MyString
7 changes: 7 additions & 0 deletions test/models/api_request_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

class ApiRequestTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

0 comments on commit 92812d4

Please sign in to comment.