Skip to content

Commit

Permalink
Beginnings of API v1
Browse files Browse the repository at this point in the history
Move shared code into top level application controller, add Devices#show
and Devices#index using new presentations
  • Loading branch information
timcowlishaw committed Nov 13, 2024
1 parent c084907 commit 33e7e41
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 155 deletions.
72 changes: 72 additions & 0 deletions app/controllers/application_api_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
class ApplicationAPIController < ActionController::API

include ActionController::ImplicitRender
include ActionController::Helpers
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Token::ControllerMethods
include ActionController::Caching

include Pundit::Authorization
include PrettyJSON
include ErrorHandlers

include SharedControllerMethods

include ::UserHelper
helper ::UserHelper

include ::PresentationHelper
helper ::PresentationHelper

before_action :set_rate_limit_whitelist
after_action :verify_authorized, except: :index
respond_to :json

private

def set_rate_limit_whitelist
if current_user(false)&.is_admin_or_researcher?
Rack::Attack.cache.write("throttle_whitelist_#{request.remote_ip}", true, 5.minutes)
end
end

def check_if_authorized!
if current_user.nil?
if params[:access_token]
raise Smartcitizen::Unauthorized.new("Invalid OAuth2 Params")
else
raise Smartcitizen::Unauthorized.new("Authorization required")
end
end
end

def raise_ransack_errors_as_bad_request(&block)
begin
block.call
rescue ArgumentError => e
render json: { message: e.message, status: 400 }, status: 400
end
end

def check_missing_params *params_list
missing_params = []
params_list.each do |param|
individual_params = param.split("||")
missing_params << individual_params.join(" OR ") unless (params.keys & individual_params).any?
end
raise ActionController::ParameterMissing.new(missing_params.to_sentence) if missing_params.any?
end

def check_date_param_format(param_name)
return true if !params[param_name]
return true if params[param_name] =~ /^\d+$/
begin
Time.parse(params[param_name])
return true
rescue
message = "The #{param_name} parameter must be an ISO8601 format date or datetime or an integer number of seconds since the start of the UNIX epoch."
render json: { message: message, status: 400 }, status: 400
return false
end
end
end
73 changes: 1 addition & 72 deletions app/controllers/v0/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,83 +1,12 @@
require_relative '../../helpers/user_helper'
module V0
class ApplicationController < ActionController::API

include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Token::ControllerMethods
include ActionController::Helpers
include ActionController::ImplicitRender
include ActionController::Caching

include PrettyJSON
include ErrorHandlers

helper ::UserHelper
include ::UserHelper

include SharedControllerMethods

helper ::PresentationHelper
include ::PresentationHelper

respond_to :json

class ApplicationController < ::ApplicationAPIController
before_action :prepend_view_paths
before_action :set_rate_limit_whitelist
after_action :verify_authorized, except: :index

protected

def check_missing_params *params_list
missing_params = []
params_list.each do |param|
individual_params = param.split("||")
missing_params << individual_params.join(" OR ") unless (params.keys & individual_params).any?
end
raise ActionController::ParameterMissing.new(missing_params.to_sentence) if missing_params.any?
end

def check_date_param_format(param_name)
return true if !params[param_name]
return true if params[param_name] =~ /^\d+$/
begin
Time.parse(params[param_name])
return true
rescue
message = "The #{param_name} parameter must be an ISO8601 format date or datetime or an integer number of seconds since the start of the UNIX epoch."
render json: { message: message, status: 400 }, status: 400
return false
end
end

private

def raise_ransack_errors_as_bad_request(&block)
begin
block.call
rescue ArgumentError => e
render json: { message: e.message, status: 400 }, status: 400
end
end

def prepend_view_paths
# is this still necessary?
prepend_view_path "app/views/v0"
end

def set_rate_limit_whitelist
if current_user(false)&.is_admin_or_researcher?
Rack::Attack.cache.write("throttle_whitelist_#{request.remote_ip}", true, 5.minutes)
end
end

def check_if_authorized!
if current_user.nil?
if params[:access_token]
raise Smartcitizen::Unauthorized.new("Invalid OAuth2 Params")
else
raise Smartcitizen::Unauthorized.new("Authorization required")
end
end
end
end
end
4 changes: 4 additions & 0 deletions app/controllers/v1/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module V1
class ApplicationController < ::ApplicationAPIController
end
end
35 changes: 35 additions & 0 deletions app/controllers/v1/devices_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module V1
class DevicesController < ApplicationController
def show
@device = Device.includes(
:owner,:tags, {sensors: :measurement}).find(params[:id])
authorize @device
render json: present(@device)
end

#TODO Document breaking API change as detailed in https://github.com/fablabbcn/smartcitizen-api/issues/186
def index
raise_ransack_errors_as_bad_request do
@q = policy_scope(Device)
.includes(:owner, :tags, :components, {sensors: :measurement})
.ransack(params[:q], auth_object: (current_user&.is_admin? ? :admin : nil))

@devices = @q.result(distinct: true)

if params[:near]
if params[:near] =~ /\A(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)\z/
@devices = @devices.near(
params[:near].split(','), (params[:within] || 1000))
else
return render json: { id: "bad_request",
message: "Malformed near parameter",
url: 'https://developer.smartcitizen.me/#get-all-devices',
errors: nil }, status: :bad_request
end
end
@devices = paginate(@devices)
render json: present(@devices)
end
end
end
end
37 changes: 0 additions & 37 deletions app/views/v001/devices/_device.jbuilder

This file was deleted.

1 change: 0 additions & 1 deletion app/views/v001/devices/current_user_index.jbuilder

This file was deleted.

3 changes: 0 additions & 3 deletions app/views/v001/devices/index.jbuilder

This file was deleted.

42 changes: 0 additions & 42 deletions app/views/v001/users/show.jbuilder

This file was deleted.

4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
get 'password_reset/:token', to: 'sessions#password_reset_landing', as: 'password_reset'
end

api_version(module: "V1", path: { value: "v1" }, header: {name: "Accept", value: "application/vnd.smartcitizen; version=1"}, defaults: { format: :json }) do
resources :devices
end

api_version(module: "V0", path: {value: "v0"}, header: {name: "Accept", value: "application/vnd.smartcitizen; version=0"}, default: true, defaults: { format: :json }) do
# devices
resources :devices do
Expand Down

0 comments on commit 33e7e41

Please sign in to comment.