Module: Enconvert::Internal

Included in:
Client, V2
Defined in:
lib/enconvert/internal.rb

Defined Under Namespace

Classes: FilePart, Transport

Class Method Summary collapse

Class Method Details

.build_multipart(fields) ⇒ Object

Build a multipart/form-data body from a flat list of field hashes ({ name:, value:, filename?:, content_type?: }). Multiple entries sharing the same name produce repeated parts — e.g. ingest_files' files field, one part per uploaded file.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/enconvert/internal.rb', line 135

def build_multipart(fields)
  boundary = "EnconvertFormBoundary#{SecureRandom.hex(16)}"
  crlf = "\r\n".b
  buffer = +"".b
  fields.each do |f|
    buffer << "--#{boundary}".b << crlf
    if f[:filename]
      buffer << "Content-Disposition: form-data; name=\"#{f[:name]}\"; filename=\"#{f[:filename]}\"".b << crlf
      buffer << "Content-Type: #{f[:content_type] || 'application/octet-stream'}".b << crlf << crlf
    else
      buffer << "Content-Disposition: form-data; name=\"#{f[:name]}\"".b << crlf << crlf
    end
    buffer << f[:value].to_s.b << crlf
  end
  buffer << "--#{boundary}--".b << crlf
  [buffer, boundary]
end

.extract_error_message(resp) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/enconvert/internal.rb', line 72

def extract_error_message(resp)
  parsed = JSON.parse(resp.body.to_s)
  if parsed.is_a?(Hash)
    parsed["detail"] || parsed["error"] || JSON.generate(parsed)
  else
    JSON.generate(parsed)
  end
rescue JSON::ParserError, TypeError
  text = resp.body.to_s
  text.empty? ? "HTTP #{resp.code}" : text
end

.new_job_idObject

A UUID4 with dashes removed (32 hex chars).



104
105
106
# File 'lib/enconvert/internal.rb', line 104

def new_job_id
  SecureRandom.uuid.delete("-")
end

.raise_for_status(resp) ⇒ Object

Shared error mapping for both V1 and V2. On HTTP >= 400, extracts a message from JSON detail/error/the raw body, then raises the matching typed error: 401/403 -> AuthenticationError, 402 -> QuotaError, 429 -> RateLimitError, else -> APIError(status, message).



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/enconvert/internal.rb', line 60

def raise_for_status(resp)
  status = resp.code.to_i
  return if status < 400

  message = extract_error_message(resp)
  raise Enconvert::AuthenticationError, message if [401, 403].include?(status)
  raise Enconvert::QuotaError, message if status == 402
  raise Enconvert::RateLimitError, message if status == 429

  raise Enconvert::APIError.new(status, message)
end

.serialize_pdf_options(o) ⇒ Object

snake_case-only-if-set serialization of PDF options. Accepts a Hash with symbol keys: page_size, page_width, page_height, orientation, margins, scale, grayscale, header, footer.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/enconvert/internal.rb', line 87

def serialize_pdf_options(o)
  return {} if o.nil?

  out = {}
  out[:page_size] = o[:page_size] if o.key?(:page_size)
  out[:page_width] = o[:page_width] if o.key?(:page_width)
  out[:page_height] = o[:page_height] if o.key?(:page_height)
  out[:orientation] = o[:orientation] if o.key?(:orientation)
  out[:margins] = o[:margins] if o.key?(:margins)
  out[:scale] = o[:scale] if o.key?(:scale)
  out[:grayscale] = o[:grayscale] if o.key?(:grayscale)
  out[:header] = o[:header] if o.key?(:header)
  out[:footer] = o[:footer] if o.key?(:footer)
  out
end

.to_file_part(file) ⇒ Object

Convert a FileInput into a normalized FilePart. Accepts:

- a filesystem path (String)
- raw bytes via any IO-like/readable object (File, StringIO, ...)
- a Hash wrapper { data:, filename:, content_type: }


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/enconvert/internal.rb', line 112

def to_file_part(file)
  if file.is_a?(Hash)
    data = file[:data] || file["data"]
    filename = file[:filename] || file["filename"]
    content_type = file[:content_type] || file["content_type"]
    bytes = data.respond_to?(:read) ? data.read : data.to_s
    FilePart.new(bytes: bytes, filename: filename, content_type: content_type || Formats.mime_for(filename))
  elsif file.is_a?(String)
    bytes = File.binread(file)
    filename = File.basename(file)
    FilePart.new(bytes: bytes, filename: filename, content_type: Formats.mime_for(filename))
  elsif file.respond_to?(:read)
    FilePart.new(bytes: file.read, filename: "upload.bin", content_type: "application/octet-stream")
  else
    raise Enconvert::Error,
          "Unsupported file input. Pass a path string, an IO/readable object, or { data:, filename: }."
  end
end