Skip to content

Instantly share code, notes, and snippets.

@apotema
Last active October 27, 2015 23:54
Show Gist options
  • Save apotema/2895aafbd8eb4b01caa9 to your computer and use it in GitHub Desktop.
Save apotema/2895aafbd8eb4b01caa9 to your computer and use it in GitHub Desktop.

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment