253 lines
8.1 KiB
Ruby
253 lines
8.1 KiB
Ruby
module Dunlop::Ember::ApiBaseController
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
before_action :authenticate_user_from_token!
|
|
before_action :authenticate_user!
|
|
before_action :set_current_user
|
|
before_action :find_record, only: %i[show update destroy]
|
|
respond_to :json
|
|
rescue_from CanCan::AccessDenied do
|
|
render json: {errors: [{status: 403, detail: 'unauthorized'}]}, status: 403
|
|
end
|
|
end
|
|
|
|
def index
|
|
index_safeguard record_class
|
|
result = record_scope.search(query).result
|
|
result = result.includes(*FlatKeys.as_nested_structure(index_include_relations)) if index_include_relations.present?
|
|
result = result.uniq if params[:uniq].present?
|
|
if index_pagination?
|
|
paginated = result.page(params[:page]).per(params[:per_page])
|
|
render json: paginated, meta: {total_pages: paginated.total_pages, total_count: paginated.total_count}
|
|
else
|
|
render json: result
|
|
end
|
|
end
|
|
|
|
def show
|
|
show_safeguard @record
|
|
render json: @record
|
|
end
|
|
|
|
def create
|
|
@record ||= record_class.new(record_params)
|
|
create_safeguard @record
|
|
return if performed?
|
|
if @record.save
|
|
render json: @record
|
|
else
|
|
render json: {errors: @record.errors.map{|attr, errors| {attr => errors.to_s}}}, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def update
|
|
update_safeguard @record
|
|
return if performed?
|
|
@record.update_attributes record_params
|
|
render json: @record
|
|
end
|
|
|
|
def destroy
|
|
destroy_safeguard @record
|
|
@record.destroy
|
|
head :no_content
|
|
end
|
|
|
|
private
|
|
|
|
# used to include relations on database lookup
|
|
# has form:
|
|
# ['comments.replies', 'authors.orginizations']
|
|
#
|
|
def include_relations
|
|
return [] unless includes = params[:include].presence
|
|
includes.split(',')
|
|
end
|
|
|
|
%i[index show].each do |action|
|
|
define_method "#{action}_include_relations" do
|
|
include_relations
|
|
end
|
|
end
|
|
|
|
def publish(event, data = {})
|
|
return
|
|
data = data.merge(event: event)
|
|
data[:client_id] = request.headers['HTTP_CLIENT_ID'] if request.present?
|
|
FayeRails.client(nil).publish('/activity', data)
|
|
end
|
|
|
|
def index_safeguard(model)
|
|
# can be overloaded
|
|
end
|
|
|
|
def show_safeguard(record)
|
|
# can be overloaded
|
|
record_safeguard(record)
|
|
end
|
|
|
|
def create_safeguard(record)
|
|
# can be overloaded
|
|
record_safeguard(record)
|
|
end
|
|
|
|
def update_safeguard(record)
|
|
# can be overloaded
|
|
record_safeguard(record)
|
|
end
|
|
|
|
def destroy_safeguard(record)
|
|
# can be overloaded
|
|
record_safeguard(record)
|
|
end
|
|
|
|
def record_safeguard(record)
|
|
# can be overloaded
|
|
return
|
|
render json: {}, status: :unauthorized unless authorized_ids(record).include?(current_user.id)
|
|
end
|
|
|
|
def authorized_ids(*)
|
|
# can be overloaded
|
|
[]
|
|
end
|
|
|
|
def authenticate_user_from_token!
|
|
# https://romulomachado.github.io/2015/09/28/using-ember-simple-auth-with-devise.html
|
|
authenticate_with_http_token do |token, options|
|
|
user_email = options[:email].presence
|
|
user = user_email && User.find_by_email(user_email)
|
|
|
|
if user && Devise.secure_compare(user.authentication_token, token)
|
|
sign_in user, store: false
|
|
end
|
|
end
|
|
end
|
|
|
|
public def record_class
|
|
deducted_class_name = controller_path.sub(/api\//,'').classify
|
|
deducted_class_name.safe_constantize || deducted_class_name.demodulize.constantize
|
|
end
|
|
|
|
# basic scope for find and index. Can be overloaded to add extra security restrictions
|
|
def record_scope
|
|
record_class.all
|
|
end
|
|
|
|
def find_record(options = {})
|
|
scope = record_scope
|
|
scope = scope.includes(*Array.wrap(options[:includes])) if options[:includes]
|
|
@record = scope.find(params[:id])
|
|
end
|
|
|
|
def record_params
|
|
#params.require(controller_path.sub(/^api\//, '').singularize).permit(*permitted_params)
|
|
params.require(controller_path.sub(/api\//, '').singularize).permit(*permitted_params)
|
|
end
|
|
|
|
def permitted_params
|
|
[]
|
|
end
|
|
|
|
# used for serialization
|
|
def serialize_options
|
|
return {} unless includes = params[:include].presence
|
|
{include: includes.split(',')}
|
|
end
|
|
|
|
# map action specific serialize options to general serialize options
|
|
%i[index show create update].each do |action|
|
|
define_method "#{action}_serialize_options" do
|
|
serialize_options
|
|
end
|
|
end
|
|
|
|
# /(_eq|_eq_any|_eq_all|_not_eq|_not_eq_any|_not_eq_all|_matches|_matches_any|_matches_all|
|
|
# _does_not_match|_does_not_match_any|_does_not_match_all|_lt|_lt_any|_lt_all|_lteq|_lteq_any|
|
|
# _lteq_all|_gt|_gt_any|_gt_all|_gteq|_gteq_any|_gteq_all|_in|_in_any|_in_all|_not_in|_not_in_any|
|
|
# _not_in_all|_cont|_cont_any|_cont_all|_i_cont|_i_cont_any|_i_cont_all|_not_cont|_not_cont_any|
|
|
# _not_cont_all|_i_not_cont|_i_not_cont_any|_i_not_cont_all|_start|_start_any|_start_all|_not_start|
|
|
# _not_start_any|_not_start_all|_end|_end_any|_end_all|_not_end|_not_end_any|_not_end_all|
|
|
# _true|_not_true|_false|_not_false|_present|_blank|_null|_not_null)$/
|
|
PREDICATE_END_MATCHER = Regexp.new("(_#{Ransack.predicates.keys.join('|_')})$")
|
|
|
|
#TODO: make me pretty, specs are in place
|
|
def query
|
|
mapped_query = {}
|
|
(params[:q] || {}).each do |key, value|
|
|
key = "#{key}_eq" unless %w[s sorts].include?(key) or record_class.try(:ransackable_scopes).try(:include?, key.to_sym) or key =~ PREDICATE_END_MATCHER # expect equality when no predicate is given
|
|
if key =~ /^type_/
|
|
# map type to sti_type
|
|
mapped_value = case value
|
|
when Array then value.map{|v| "#{record_class.name}::#{v.to_s.gsub('-', '_').classify}"}
|
|
else
|
|
"#{record_class.name}::#{value.to_s.gsub('-', '_').classify}"
|
|
end
|
|
mapped_query["sti_#{key}"] = mapped_value
|
|
else
|
|
mapped_query[key] = value
|
|
end
|
|
end
|
|
mapped_query
|
|
end
|
|
|
|
def _render_with_renderer_json(resource, options = {})
|
|
return resource.to_json if resource.is_a?(Hash)
|
|
return resource if resource.is_a?(String)
|
|
|
|
action_serialize_options_method = :"#{action_name}_serialize_options"
|
|
#action_serialize_options = respond_to?(action_serialize_options_method) ? __send__(action_serialize_options_method) : {}
|
|
action_serialize_options = __send__(action_serialize_options_method) rescue {}
|
|
action_serialize_options[:include] = options[:include] if options.has_key?(:include)
|
|
action_serialize_options[:meta] = options[:meta] if options.has_key?(:meta)
|
|
|
|
serializer_class = if options.has_key?(:serializer)
|
|
options.delete(:serializer)
|
|
elsif action_serialize_options[:serializer].present?
|
|
action_serialize_options[:serializer]
|
|
elsif resource.is_a?(ActiveRecord::Base)
|
|
#infer based on controller path replacing actual controller part with resouce part /workflow_step_definitions/:id/table
|
|
"#{self.class.name.deconstantize}::#{resource.class.name.demodulize}Serializer".constantize
|
|
elsif resource.is_a?(Array) and resource.first.is_a?(ActiveRecord::Base)
|
|
# infer based in first element of collection
|
|
"#{self.class.name.deconstantize}::#{resource.first.class.name.demodulize}Serializer".constantize
|
|
else
|
|
# infer based on controller path
|
|
"#{controller_path.underscore.classify}Serializer".constantize
|
|
end
|
|
|
|
serializer = if resource.respond_to?(:each)
|
|
ActiveModel::Serializer::CollectionSerializer.new(resource, action_serialize_options.merge(serializer: serializer_class)) #TODO: fix index includes
|
|
else
|
|
serializer_class.new(resource, action_serialize_options)
|
|
end
|
|
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, action_serialize_options)
|
|
res = {}
|
|
time = Benchmark.ms { res = adapter.as_json }
|
|
logger.info "Generated serialized output in #{time.round} ms"
|
|
#binding.pry
|
|
#return res
|
|
return res.is_a?(String) ? res : res.to_json
|
|
end
|
|
|
|
def has_permission_of_type(type, user = current_user)
|
|
Dunlop::Ability.has_permission_of_type(type, user)
|
|
end
|
|
|
|
def any_permission_starts_with(expression, user = current_user)
|
|
Dunlop::Ability.any_permission_starts_with(expression, user)
|
|
end
|
|
|
|
def index_pagination?
|
|
true
|
|
end
|
|
|
|
def set_current_user
|
|
request.env["exception_notifier.exception_data"] = {
|
|
current_user: current_user.try(:inspect)
|
|
}
|
|
User.current = current_user
|
|
end
|
|
end
|