Module: GRApiManager::BodyParser

Defined in:
lib/gr_api_manager.rb

Overview


BodyParser — detects Content-Type and returns an appropriate parsed result.

Constant Summary collapse

BINARY_MIME_PREFIXES =
%w[
  image/ video/ audio/ application/pdf application/msword
  application/vnd. application/zip application/x-tar
  application/x-rar application/octet-stream
].freeze

Class Method Summary collapse

Class Method Details

.binary_content_type?(ct) ⇒ Boolean

Returns:

  • (Boolean)


458
459
460
# File 'lib/gr_api_manager.rb', line 458

def self.binary_content_type?(ct)
  BINARY_MIME_PREFIXES.any? { |prefix| ct.start_with?(prefix) }
end

.ext_for(content_type) ⇒ Object



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/gr_api_manager.rb', line 462

def self.ext_for(content_type)
  {
    'image/jpeg'          => '.jpg',
    'image/png'           => '.png',
    'image/gif'           => '.gif',
    'image/webp'          => '.webp',
    'image/svg+xml'       => '.svg',
    'image/bmp'           => '.bmp',
    'video/mp4'           => '.mp4',
    'video/webm'          => '.webm',
    'audio/mpeg'          => '.mp3',
    'audio/wav'           => '.wav',
    'application/pdf'     => '.pdf',
    'application/msword'  => '.doc',
    'application/zip'     => '.zip',
    'application/x-tar'   => '.tar',
    'application/octet-stream' => '.bin'
  }.fetch(content_type, '.bin')
end

.parse(request) ⇒ Object

Returns a Hash of parsed body values.



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/gr_api_manager.rb', line 370

def self.parse(request)
  content_type = (request.content_type || '').split(';').first.to_s.strip.downcase

  case content_type
  when 'application/json'
    parse_json(request)

  when 'multipart/form-data'
    parse_multipart(request)

  when 'application/x-www-form-urlencoded'
    {}

  when 'text/plain'
    text = request.body.read.to_s.force_encoding(Encoding::UTF_8)
    { _raw_text: text }

  else
    if binary_content_type?(content_type)
      parse_raw_binary(request, content_type)
    else
      parse_json(request) rescue {}
    end
  end
end

.parse_json(request) ⇒ Object



398
399
400
401
402
403
404
# File 'lib/gr_api_manager.rb', line 398

def self.parse_json(request)
  body = request.body.read.to_s
  return {} if body.strip.empty?
  JSON.parse(body, symbolize_names: true)
rescue JSON::ParserError => e
  raise ArgumentError, "Invalid JSON body: #{e.message}"
end

.parse_multipart(request) ⇒ Object



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/gr_api_manager.rb', line 406

def self.parse_multipart(request)
  result = {}
  files  = {}

  request.params.each do |key, value|
    sym = key.to_sym

    if value.is_a?(Hash) && value.key?(:tempfile)
      files[sym] = FilePayload.new(
        tempfile:     value[:tempfile],
        filename:     value[:filename] || key,
        content_type: value[:type] || 'application/octet-stream'
      )
    elsif value.is_a?(Array)
      files[sym] = value.map do |v|
        if v.is_a?(Hash) && v.key?(:tempfile)
          FilePayload.new(
            tempfile:     v[:tempfile],
            filename:     v[:filename] || key,
            content_type: v[:type] || 'application/octet-stream'
          )
        else
          v
        end
      end
    else
      result[sym] = value
    end
  end

  result[:_files] = files unless files.empty?
  result
end

.parse_raw_binary(request, content_type) ⇒ Object



440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/gr_api_manager.rb', line 440

def self.parse_raw_binary(request, content_type)
  raw = request.body.read
  return {} if raw.nil? || raw.empty?

  disposition = request.env['HTTP_CONTENT_DISPOSITION'] || ''
  filename    = disposition[/filename="?([^";]+)"?/, 1] || "upload#{ext_for(content_type)}"

  io = StringIO.new(raw.force_encoding(Encoding::BINARY))

  payload = FilePayload.new(
    tempfile:     io,
    filename:     filename,
    content_type: content_type
  )

  { _raw_binary: payload }
end