lib/ecoportal/api/v1/people.rb in ecoportal-api-0.9.7 vs lib/ecoportal/api/v1/people.rb in ecoportal-api-0.10.0

- old
+ new

@@ -1,15 +1,16 @@ module Ecoportal module API class V1 - # @attr_reader client [Common::Client] a `Common::Client` object that holds the configuration of the api connection. + # @attr_reader client [Common::Client] a `Common::Client` object that + # holds the configuration of the api connection. class People extend Common::BaseClass - include Enumerable include Common::DocHelpers + include Enumerable - JOB_TIMEOUT = 240 + JOB_TIMEOUT = 240 DELAY_STATUS_CHECK = 5 class_resolver :person_class, "Ecoportal::API::V1::Person" attr_reader :client @@ -28,39 +29,53 @@ # @option params [String] :per_page the number of people you get per request. # @option params [String] :q some text to search. Omit this parameter to target all the people. # @param silent [Boolean] `false` to show percentage of progress. # @yield [person] does some stuff with the person. # @yieldparam person [Person] - def each(params: {}, silent: false, &block) - return to_enum(:each, params: params, silent: silent) unless block - cursor_id = nil; results = 0 + def each(params: {}, silent: false) # rubocop:disable Metrics/AbcSize + return to_enum(:each, params: params, silent: silent) unless block_given? + + cursor_id = nil + results = 0 + puts "\n" unless silent + loop do params.update(cursor_id: cursor_id) if cursor_id - body = nil; response = nil; count = 5 + + body = nil + response = nil + count = 5 + loop do response = client.get("/people", params: params) body = response && body_data(response.body) break if response.success? || count <= 0 + puts "Request failed - Status #{response.status}: #{body}" count -= 1 + sleep(0.5) end + raise "Request failed - Status #{response.status}: #{body}" unless response.success? - unless silent || (total = body["total_results"]) == 0 + unless silent || (total = body["total_results"])&.zero? results += body["results"].length percent = results * 100 / total + msg = "People GET" - msg += " (search=#{params[:q]})" if params.key?(:q) - print "#{msg}: #{percent.round}% (of #{total})\r" + msg << " (search=#{params[:q]})" if params.key?(:q) + + print "#{msg}: #{percent.round}% (of #{total}): #{results}\r" $stdout.flush end body["results"].each do |person| yield person_class.new(person) end + break unless (cursor_id = body["cursor_id"]) end self end @@ -79,23 +94,24 @@ # @note if the request has `success?` the returned `object.result` gives an object with that `Person`. # @param doc [String, Hash, Person] data containing an `id` (internal or external) of the target person. # @return [Person] the person with `id` (internal or external) contained in `doc`. def get(doc) id = get_id(doc) - response = client.get("/people/"+CGI.escape(id)) + response = client.get("/people/#{CGI.escape(id)}") body = body_data(response.body) return person_class.new(body) if response.success? + raise "Could not get person #{id} - Error #{response.status}: #{body}" end # Requests an update of a person via api. # @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person. # @return [Response] an object with the api response. def update(doc) body = get_body(doc) id = get_id(doc) - client.patch("/people/"+CGI.escape(id), data: body) + client.patch("/people/#{CGI.escape(id)}", data: body) end # Requests to create a person via api. # @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person. # @return [Response] an object with the api response. @@ -108,48 +124,64 @@ # @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person. # @return [Response] an object with the api response. def upsert(doc) body = get_body(doc) id = get_id(doc) - client.post("/people/"+CGI.escape(id), data: body) + client.post("/people/#{CGI.escape(id)}", data: body) end # Requests to completelly remove from an organization an existing person via api. # @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person. # @return [Response] an object with the api response. def delete(doc) id = get_id(doc) - client.delete("/people/"+CGI.escape(id)) + client.delete("/people/#{CGI.escape(id)}") end # Creates a `BatchOperation` and yields it to the given bock. # @yield [batch_op] adds multiple api requests for the current batch. # @yieldparam batch_op [BatchOperation] # @param job_mode [Boolean] whether or not it should use batch jobs # @return [Ecoportal::API::Common::Response] the results of the batch def batch(job_mode: true, &block) return job(&block) if job_mode - operation = Common::BatchOperation.new("/people", person_class, logger: client.logger) + + operation = Common::BatchOperation.new( + "/people", + person_class, + logger: client.logger + ) + yield operation + # The batch operation is responsible for logging the output client.post("/people/batch", data: operation.as_json).tap do |response| operation.process_response(response) end end # @return [Ecoportal::API::Common::Response] the results of the batch job def job - operation = Common::BatchOperation.new("/people", person_class, logger: client.logger) + operation = Common::BatchOperation.new( + "/people", + person_class, + logger: client.logger + ) + yield operation + job_id = create_job(operation) status = wait_for_job_completion(job_id) if status&.complete? job_result(job_id, operation) else - msg = "Job `#{job_id}` not complete. Probably timeout after #{JOB_TIMEOUT} seconds. Current status: #{status}" - raise API::Errors::TimeOut.new msg + msg = "Job `#{job_id}` not complete. " + msg << "Probably timeout after #{JOB_TIMEOUT} seconds. " + msg << "Current status: #{status}" + + raise API::Errors::TimeOut, msg end end # Creates a new `Person` object. # @return [Person] new empty person object of the current version. @@ -158,14 +190,19 @@ end private JobStatus = Struct.new(:id, :complete?, :errored?, :progress) + def job_status(job_id) response = client.get("/people/job/#{CGI.escape(job_id)}/status") body = response && body_data(response.body) - raise "Status error (#{response.status}) - Errors: #{body}" unless response.success? + + msg = "Status error (#{response.status}) - " + msg << "Errors: #{body}" + raise msg unless response.success? + JobStatus.new( body["id"], body["complete"], body["errored"], body["progress"] @@ -181,14 +218,16 @@ def wait_for_job_completion(job_id) # timeout library is evil. So we make poor-man timeout. # https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/ before = Time.now - while true + + loop do status = job_status(job_id) break status if status.complete? break status if Time.now >= before + JOB_TIMEOUT + sleep(DELAY_STATUS_CHECK) status end end @@ -205,10 +244,9 @@ # Hook for other api versions to obtain the raw data of a response # @note this was introduced to allow `v2` to reuse this class def body_data(body) body end - end end end end