Class: Api2Convert::Resource::Jobs

Inherits:
Object
  • Object
show all
Defined in:
lib/api2convert/resource/jobs.rb

Overview

Full control over the job lifecycle.

Most users only need client.convert, which is built on top of these methods. Reach for this resource for compound jobs, merges, presets, custom polling or job chaining. Methods are thin: build the request, call the transport, hydrate a model.

Instance Method Summary collapse

Constructor Details

#initialize(transport, uploader) ⇒ Jobs

Returns a new instance of Jobs.



12
13
14
15
# File 'lib/api2convert/resource/jobs.rb', line 12

def initialize(transport, uploader)
  @transport = transport
  @uploader = uploader
end

Instance Method Details

#add_input(job_id, descriptor) ⇒ Object

Attach an input by descriptor, e.g. a remote URL: add_input(job_id, { "type" => "remote", "source" => "https://..." }).



56
57
58
59
60
# File 'lib/api2convert/resource/jobs.rb', line 56

def add_input(job_id, descriptor)
  Model::InputFile.from_hash(
    @transport.request("POST", "/jobs/#{Support::Data.encode_segment(job_id)}/input", descriptor)
  )
end

#cancel(job_id) ⇒ Object

Cancel a job (whether staged or processing).



49
50
51
52
# File 'lib/api2convert/resource/jobs.rb', line 49

def cancel(job_id)
  @transport.request("DELETE", "/jobs/#{Support::Data.encode_segment(job_id)}")
  nil
end

#create(payload, idempotency_key = nil) ⇒ Object

Create a job. Pass { "process" => false } to stage it for uploads, then call #start once inputs are attached. idempotency_key makes the create retry-safe (sent as the Idempotency-Key header).



20
21
22
23
# File 'lib/api2convert/resource/jobs.rb', line 20

def create(payload, idempotency_key = nil)
  headers = idempotency_key.nil? ? nil : { "Idempotency-Key" => idempotency_key }
  Model::Job.from_hash(@transport.request("POST", "/jobs", payload, nil, headers))
end

#get(job_id) ⇒ Object



25
26
27
# File 'lib/api2convert/resource/jobs.rb', line 25

def get(job_id)
  Model::Job.from_hash(@transport.request("GET", "/jobs/#{Support::Data.encode_segment(job_id)}"))
end

#list(status = nil, page = 1) ⇒ Object

List the current key's jobs (paginated, 50 per page).



30
31
32
33
34
35
# File 'lib/api2convert/resource/jobs.rb', line 30

def list(status = nil, page = 1)
  query = { "page" => page.to_s }
  query["status"] = status unless status.nil?
  rows = @transport.request("GET", "/jobs", nil, query)
  hashes(rows).map { |row| Model::Job.from_hash(row) }
end

#outputs(job_id) ⇒ Object

Outputs produced by the job (use #get or #wait first).



96
97
98
99
# File 'lib/api2convert/resource/jobs.rb', line 96

def outputs(job_id)
  rows = @transport.request("GET", "/jobs/#{Support::Data.encode_segment(job_id)}/output")
  hashes(rows).map { |row| Model::OutputFile.from_hash(row) }
end

#start(job_id) ⇒ Object

Start processing a staged job (process => true).



44
45
46
# File 'lib/api2convert/resource/jobs.rb', line 44

def start(job_id)
  update(job_id, { "process" => true })
end

#update(job_id, payload) ⇒ Object



37
38
39
40
41
# File 'lib/api2convert/resource/jobs.rb', line 37

def update(job_id, payload)
  Model::Job.from_hash(
    @transport.request("PATCH", "/jobs/#{Support::Data.encode_segment(job_id)}", payload)
  )
end

#upload(job, file, filename = nil) ⇒ Object

Upload a local file (path or IO) to the job's upload server.



63
64
65
# File 'lib/api2convert/resource/jobs.rb', line 63

def upload(job, file, filename = nil)
  @uploader.upload(job, file, filename)
end

#wait(job_id, timeout_seconds = nil, throw_on_failure = true) ⇒ Object

Block until the job reaches a terminal status, polling with backoff.

Raises ConversionFailedError on a failed/canceled job (unless throw_on_failure is false) and ConversionTimeoutError past the deadline. The interval is floored and the total wait capped, so no configuration can busy-loop or poll unbounded.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/api2convert/resource/jobs.rb', line 73

def wait(job_id, timeout_seconds = nil, throw_on_failure = true)
  config = @transport.config
  # Clamp again here (Config.create already clamps) so a directly-built
  # Config or a per-call override can never busy-loop or poll unbounded.
  requested = timeout_seconds.nil? ? config.poll_timeout : timeout_seconds
  timeout = requested.clamp(0, Config::MAX_POLL_TIMEOUT)
  max_interval = [Config::MIN_POLL_INTERVAL, config.poll_max_interval].max
  interval = [Config::MIN_POLL_INTERVAL, config.poll_interval].max
  deadline = monotonic + timeout

  loop do
    job = get(job_id)

    raise ConversionFailedError.new(job) if (job.failed? || job.canceled?) && throw_on_failure
    return job if job.terminal?
    raise ConversionTimeoutError.new(job, timeout) if monotonic >= deadline

    @transport.pause(interval)
    interval = [max_interval, interval * 1.5].min
  end
end