Class: Api::EndpointHandler
- Inherits:
-
Core::Service
- Object
- Sinatra::Base
- Core::Service
- Api::EndpointHandler
- Defined in:
- app/middleware/api/endpoint_handler.rb
Overview
Sinatra application which provides routing for the V1 API Automatically generates routes from the files listed under app/api/endpoints
used in RootService which in turn gets mounted in config/routes.rb
Constant Summary collapse
- ACTIONS_TO_HTTP_VERBS =
rubocop:enable Metrics/MethodLength
{ create: :post, read: :get, update: :put, delete: :delete }.freeze
Constants inherited from Core::Service
Instance Attribute Summary
Attributes inherited from Core::Service
Class Method Summary collapse
-
.file_action(action, http_method) ⇒ Object
rubocop:todo Metrics/AbcSize, Metrics/MethodLength.
-
.file_addition(action, http_method) ⇒ Object
rubocop:todo Metrics/MethodLength.
- .file_attached(bool) ⇒ Object
-
.file_model_action(_action, http_method) ⇒ Object
rubocop:enable Metrics/MethodLength.
-
.file_model_addition(action, http_method) ⇒ Object
rubocop:todo Metrics/MethodLength.
-
.file_requested(bool) ⇒ Object
We can’t use the built in provides, as the accepted mimetimes are fixed when the route is set up.
-
.instance_action(action, http_method) ⇒ Object
rubocop:todo Metrics/AbcSize, Metrics/MethodLength.
- .model_action(action, http_method) ⇒ Object
- .register_mimetype(mimetype) ⇒ Object
- .registered_mimetypes ⇒ Object
Instance Method Summary collapse
-
#handle_request(handler, http_request, action, parts) ⇒ Object
rubocop:todo Metrics/MethodLength.
- #registered_mimetypes ⇒ Object
-
#return_file(http_request, action, parts) ⇒ Object
rubocop:todo Metrics/MethodLength.
Methods inherited from Core::Service
after_all_actions, #api_path, api_version_path, before_all_actions, #handle_not_found!, #redirect_to, #redirect_to_multiple_locations
Class Method Details
.file_action(action, http_method) ⇒ Object
rubocop:todo Metrics/AbcSize, Metrics/MethodLength
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'app/middleware/api/endpoint_handler.rb', line 100 def file_action(action, http_method) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength send( http_method, %r{/([\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})(?:/([^/]+(?:/[^/]+)*))?}, file_requested: true ) do report('file') do uuid_in_url, parts = params[:captures][0], params[:captures][1].try(:split, '/') || [] uuid = Uuid.find_by(external_id: uuid_in_url) or raise ActiveRecord::RecordNotFound, 'UUID does not exist' file_through = return_file(request, action, parts) do |request| request.io = lookup_for_class(uuid.resource.class) { |e| raise e } request.target = request.io.eager_loading_for(uuid.resource.class).include_uuid.find(uuid.resource_id) end uuid.resource.__send__(file_through) { |file| send_file file.path, filename: file.filename } end end end |
.file_addition(action, http_method) ⇒ Object
rubocop:todo Metrics/MethodLength
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'app/middleware/api/endpoint_handler.rb', line 23 def file_addition(action, http_method) # rubocop:todo Metrics/AbcSize send( http_method, %r{/([\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})(?:/([^/]+(?:/[^/]+)*))?}, file_attached: true ) do if request.acceptable_media_types.prioritize(registered_mimetypes).present? raise Core::Service::ContentFiltering::InvalidRequestedContentTypeOnFile end report('file') do filename = /filename="([^"]*)"/.match(request.env['HTTP_CONTENT_DISPOSITION']).try(:[], 1) || 'unnamed_file' begin file = Tempfile.new(filename) file.binmode file.unlink file.write(request.body.read) # Be kind... file.rewind request.body.rewind uuid_in_url, parts = params[:captures][0], params[:captures][1].try(:split, '/') || [] uuid = Uuid.find_by(external_id: uuid_in_url) or raise ActiveRecord::RecordNotFound, 'UUID does not exist' handle_request(:instance, request, action, parts) do |request| request.io = lookup_for_class(uuid.resource.class) { |e| raise e } request.target = request.io.eager_loading_for(uuid.resource.class).include_uuid.find(uuid.resource_id) request.file = file request.filename = filename end ensure file.close! end end end end |
.file_attached(bool) ⇒ Object
18 19 20 |
# File 'app/middleware/api/endpoint_handler.rb', line 18 def file_attached(bool) condition { registered_mimetypes.include?(request.content_type) == bool } end |
.file_model_action(_action, http_method) ⇒ Object
rubocop:enable Metrics/MethodLength
94 95 96 97 98 |
# File 'app/middleware/api/endpoint_handler.rb', line 94 def file_model_action(_action, http_method) send(http_method, %r{/([^\d/][^/]+(?:/[^/]+){0,2})}, file_requested: true) do report('model') { raise Core::Service::ContentFiltering::InvalidRequestedContentType } end end |
.file_model_addition(action, http_method) ⇒ Object
rubocop:todo Metrics/MethodLength
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'app/middleware/api/endpoint_handler.rb', line 62 def file_model_addition(action, http_method) # rubocop:todo Metrics/AbcSize send(http_method, %r{/([^\d/][^/]+(?:/[^/]+){0,2})}, file_attached: true) do if request.acceptable_media_types.prioritize(registered_mimetypes).present? raise Core::Service::ContentFiltering::InvalidRequestedContentType end report('model') do filename = /filename="([^"]*)"/.match(request.env['HTTP_CONTENT_DISPOSITION']).try(:[], 1) || 'unnamed_file' begin file = Tempfile.new(filename) file.write(request.body.read) # Be kind... file.rewind request.body.rewind determine_model_from_parts(*params[:captures].join.split('/')) do |model, parts| handle_request(:model, request, action, parts) do |request| request.io = lookup_for_class(model) { |_| nil } request.target = model request.file = file request.filename = filename end end ensure file.close! end end end end |
.file_requested(bool) ⇒ Object
We can’t use the built in provides, as the accepted mimetimes are fixed when the route is set up.
14 15 16 |
# File 'app/middleware/api/endpoint_handler.rb', line 14 def file_requested(bool) condition { request.acceptable_media_types.prioritize(registered_mimetypes).present? == bool } end |
.instance_action(action, http_method) ⇒ Object
rubocop:todo Metrics/AbcSize, Metrics/MethodLength
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'app/middleware/api/endpoint_handler.rb', line 120 def instance_action(action, http_method) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength send( http_method, %r{/([\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})(?:/([^/]+(?:/[^/]+)*))?}, file_attached: false, file_requested: false ) do report('instance') do uuid_in_url, parts = params[:captures][0], params[:captures][1].try(:split, '/') || [] uuid = Uuid.find_by(external_id: uuid_in_url) or raise ActiveRecord::RecordNotFound, 'UUID does not exist' handle_request(:instance, request, action, parts) do |request| request.io = lookup_for_class(uuid.resource.class) { |e| raise e } request.target = request.io.eager_loading_for(uuid.resource.class).include_uuid.find(uuid.resource_id) end end end end |
.model_action(action, http_method) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'app/middleware/api/endpoint_handler.rb', line 138 def model_action(action, http_method) send(http_method, %r{/([^\d/][^/]+(?:/[^/]+){0,2})}, file_attached: false, file_requested: false) do report('model') do determine_model_from_parts(*params[:captures].join.split('/')) do |model, parts| handle_request(:model, request, action, parts) do |request| request.io = lookup_for_class(model) { |_| nil } request.target = model end end end end end |
.register_mimetype(mimetype) ⇒ Object
151 152 153 154 |
# File 'app/middleware/api/endpoint_handler.rb', line 151 def register_mimetype(mimetype) @registered_mimetypes ||= [] @registered_mimetypes.push(mimetype).uniq! end |
.registered_mimetypes ⇒ Object
9 10 11 |
# File 'app/middleware/api/endpoint_handler.rb', line 9 def registered_mimetypes @registered_mimetypes || [] end |
Instance Method Details
#handle_request(handler, http_request, action, parts) ⇒ Object
rubocop:todo Metrics/MethodLength
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'app/middleware/api/endpoint_handler.rb', line 200 def handle_request(handler, http_request, action, parts) # rubocop:todo Metrics/AbcSize endpoint_lookup, io_lookup = case handler when :instance %i[endpoint_for_object lookup_for_object] when :model %i[endpoint_for_class lookup_for_class] else raise StandardError, "Unexpected handler #{handler.inspect}" end request = ::Core::Service::Request.new(requested_url = http_request.fullpath) do |request| request.service = self request.path = parts request.json = @json Rails.logger.info("API[payload]: #{@json}") yield(request) end endpoint = send(endpoint_lookup, request.target) Rails.logger.info("API[endpoint]: #{handler}: #{requested_url} handled by #{endpoint.inspect}") body(request.send(handler, action, endpoint)) end |
#registered_mimetypes ⇒ Object
157 158 159 |
# File 'app/middleware/api/endpoint_handler.rb', line 157 def registered_mimetypes self.class.registered_mimetypes end |
#return_file(http_request, action, parts) ⇒ Object
rubocop:todo Metrics/MethodLength
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'app/middleware/api/endpoint_handler.rb', line 228 def return_file(http_request, action, parts) # rubocop:todo Metrics/AbcSize request = ::Core::Service::Request.new(requested_url = http_request.fullpath) do |request| request.service = self request.path = parts request.json = @json yield(request) end endpoint = endpoint_for_object(request.target) file_through = request.instance(action, endpoint).handled_by.file_through(request_accepted) raise Core::Service::ContentFiltering::InvalidRequestedContentType if file_through.nil? Rails.logger.info("API[endpoint]: File: #{requested_url} handled by #{endpoint.inspect}") file_through end |