Fellow Procorians,
I have spent most of my time with you working on the QuickBooks microservice and on a microservice to host the current HH2 Sage integration code.
I have seen some points of improvement on the Sage integration, one in particular is related to repeated code used to access HH2. For example:
commitment_change_order_conversation.rb
def flush_hh2_cco_queue(origin_id)
retry_cntr ||= 1
RestClient.post(flush_hh2_cco_url(origin_id), nil, { cookies: account.cookie, content_type: "application/json" })
rescue RestClient::RequestTimeout => e
if (retry_cntr -= 1) > 0
sleep HH2_TIMEOUT_RETRY_INTERVAL
retry
else
Hh2::Integration::SyncEventResponse.new(account).error(sync_event_id, cco_params[:procore_cco_id], 'ChangeOrderPackage', e)
end
rescue RestClient::Exception => e
Hh2::Integration::SyncEventResponse.new(account).error(sync_event_id, cco_params[:procore_cco_id], 'ChangeOrderPackage', e)
end
Similarly:
commitment_conversation.rb
def flush_hh2_queue(origin_id)
retry_cntr ||= 1
RestClient.post(flush_hh2_url(origin_id), nil, { cookies: account.cookie, content_type: "application/json" })
rescue RestClient::RequestTimeout => e
if (retry_cntr -= 1) > 0
sleep HH2_TIMEOUT_RETRY_INTERVAL
retry
else
Hh2::Integration::SyncEventResponse.new(account).error(sync_event_id, commitment_params[:procore_commitment_id], 'Contract', e)
end
rescue RestClient::Exception => e
Hh2::Integration::SyncEventResponse.new(account).error(sync_event_id, commitment_params[:procore_commitment_id], 'Contract', e)
end
As you can see the code used to work with HH2 is distributed throughout the HH2 engine, leading to a lot of repetition.
To tackle this, and make it easier and simpler to work with HH2, I have written a library that would bring together all of the HH2 access code in one place. The first version of this is now available as a gem.
Heres is an example of how this gem could simplify existing code:
class Hh2::Integration::PushCommitment
attr_reader :origin_id, :success
def initialize(account, sync_event_id, commitment_attributes)
@account = account
@sync_event_id = sync_event_id
@commitment_attributes = commitment_attributes
@origin_id = commitment_attributes[:origin_id]
@procore_commitment_id = commitment_attributes[:procore_commitment_id]
@success = true
end
def push
if origin_id.present?
make_request(:put, update_commitment_url)
else
make_request(:post, create_commitment_url)
end
end
private
attr_reader :account, :sync_event_id, :commitment_attributes, :procore_commitment_id
def make_request(request_type, url)
begin
request_parameters = { cookies: account.cookie, content_type: "application/json", accept: :json }
response = RestClient::Request.execute method: request_type, url: url, payload: request_body_json, headers: request_parameters
rescue RestClient::Exception => e
@success = false
Hh2::Integration::SyncEventResponse.new(account).error(sync_event_id, procore_commitment_id, 'Contract', e)
return
end
if response.present?
@origin_id = ActiveSupport::JSON.decode(response)
Hh2::Integration::CommitmentResponse.new(account).update_commitment(id: commitment_attributes[:id], origin_id: origin_id)
end
end
def request_body_json
{
"Date" => nil,
"Description" => commitment_attributes[:description].truncate(1000),
"IsCommitted" => true,
"Name" => commitment_attributes[:title],
"Code" => commitment_attributes[:code],
"RetainagePercent" => commitment_attributes[:retainage_percent],
"Type" => type_value,
"VendorId" => commitment_attributes[:vendor_origin_id]
}.to_json
end
def type_value
commitment_attributes[:type] == "PurchaseOrderContract" ? 1 : 2
end
def update_commitment_url
"http://#{account.root_url}/JobCosting/Api/V1/Commitment.svc/commitments?id=#{origin_id}"
end
def create_commitment_url
"http://#{account.root_url}/JobCosting/Api/V1/Commitment.svc/commitments"
end
end
Can be turned into:
class Hh2::Integration::PushCommitment
attr_reader :origin_id, :success
def initialize(account, sync_event_id, commitment_attributes)
@account = account
@sync_event_id = sync_event_id
@commitment_attributes = commitment_attributes
@origin_id = commitment_attributes[:origin_id]
@procore_commitment_id = commitment_attributes[:procore_commitment_id]
@success = true
end
def push
type = @commitment_attributes[:type] == "PurchaseOrderContract" ? 1 : 2
@commitment_attributes[:type] = type
@commitment_attributes[:date] = nil
@commitment_attributes[:is_commited] = true
make_request
end
private
attr_reader :account, :sync_event_id, :commitment_attributes, :procore_commitment_id
def make_request
begin
Hh2::Connection.credentials!(account.root_url, account.cookie)
if origin_id.present?
Hh2::JobCosting::CommitmentSvc::Commitment.update(origin_id, @commitment_attributes)
else
Hh2::JobCosting::CommitmentSvc::Commitment.create(@commitment_attributes)
end
rescue RestClient::Exception => e
@success = false
Hh2::Integration::SyncEventResponse.new(account).error(sync_event_id, procore_commitment_id, 'Contract', e)
return
end
if response.present?
@origin_id = ActiveSupport::JSON.decode(response)
Hh2::Integration::CommitmentResponse.new(account).update_commitment(id: commitment_attributes[:id], origin_id: origin_id)
end
end
end
The example above has not been tested, it's just to give you an idea of what the code might look like if a library like this were used.
At the moment this gem is private to Procore. Assuming other organisations are working with the HH2 web services, it may be worth considering making this a publicly available as another way of getting the Procore name more widely known within the Ruby and Rails communities.
This is only the first version of this gem, and there is certainly some more work required to get it ready for production use. If the STO team are interested in following an approach like this, then I'm happy to put the work in to finish this off.
If you have a moment, please could you take a look at the gem and let me know what you think!
Thanks!