Active Call - Zoho Sign
Zoho Sign exposes the Zoho Sign API endpoints through Active Call service objects.
Installation
Install the gem and add to the application's Gemfile by executing:
bundle add active_call-zoho_sign
If bundler is not being used to manage dependencies, install the gem by executing:
gem install active_call-zoho_sign
Configuration
Create a new Self Client client type from the Zoho Developer Console to retrieve your Client ID and Client Secret.
Choose what you need from the list of Zoho Scopes like ZohoSign.documents.ALL to generate your Grant Token.
Get your Refresh Token by calling ZohoSign::GrantToken::GetService.call(grant_token: '', client_id: '', client_secret: '').refresh_token
Configure your API credentials.
In a Rails application, the standard practice is to place this code in a file named zoho_sign.rb within the config/initializers directory.
require 'active_call-zoho_sign'
ZohoSign::BaseService.configure do |config|
config.client_id = ''
config.client_secret = ''
config.refresh_token = ''
# Optional configuration.
config.cache = Rails.cache # Default: ActiveSupport::Cache::MemoryStore.new
config.logger = Rails.logger # Default: Logger.new($stdout)
config.logger_level = :debug # Default: :info
config.log_headers = true # Default: false
config.log_bodies = true # Default: false
end
While testing, you can set your temporary development OAuth access token in the ZOHO_SIGN_ACCESS_TOKEN environment variable. In your production environment,config.refresh_token will be used.
Usage
Using call
Each service object returned will undergo validation before the call method is invoked to access API endpoints.
After successful validation.
service.success? # => true
service.errors # => #<ActiveModel::Errors []>
After failed validation.
service.success? # => false
service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=name, type=blank, options={}>]>
service.errors. # => ["Name can't be blank"]
After a successful call invocation, the response attribute will contain a Faraday::Response object.
service.success? # => true
service.response # => #<Faraday::Response ...>
service.response.success? # => true
service.response.status # => 200
service.response.body # => {"code"=>0, "message"=>"Document has been retrieved", "status"=>"success", "requests"=>{...}}
At this point you will also have a facade object which will hold all the attributes for the specific resource.
service.facade # => #<ZohoSign::Document::Facade @request_status="inprogress" ...>
service.facade.request_status # => 'inprogress'
For convenience, facade attributes can be accessed directly on the service object.
service.request_status # => 'inprogress'
After a failed call invocation, the response attribute will still contain a Faraday::Response object.
service.success? # => false
service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=Not found, options={}>]>
service.errors. # => ["No match found"]
service.response # => #<Faraday::Response ...>
service.response.success? # => false
service.response.status # => 400
service.response.body # => {"code"=>9004, "message"=>"No match found", "status"=>"failure"}
Using call!
Each service object returned will undergo validation before the call! method is invoked to access API endpoints.
After successful validation.
service.success? # => true
After failed validation, a ZohoSign::ValidationError exception will be raised with an errors attribute which
will contain an ActiveModel::Errors object.
rescue ZohoSign::ValidationError => exception
exception. # => ''
exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=name, type=blank, options={}>]>
exception.errors. # => ["Name can't be blank"]
After a successful call! invocation, the response attribute will contain a Faraday::Response object.
service.success? # => true
service.response # => #<Faraday::Response ...>
service.response.success? # => true
service.response.status # => 200
service.response.body # => {"code"=>0, "message"=>"Document has been retrieved", "status"=>"success", "requests"=>{...}}
At this point you will also have a facade object which will hold all the attributes for the specific resource.
service.facade # => #<ZohoSign::Document::Facade @request_status="inprogress" ...>
service.facade.request_status # => 'inprogress'
For convenience, facade attributes can be accessed directly on the service object.
service.request_status # => 'inprogress'
After a failed call! invocation, a ZohoSign::RequestError will be raised with a response attribute which will contain a Faraday::Response object.
rescue ZohoSign::RequestError => exception
exception. # => ''
exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=Not found, options={}>]>
exception.errors. # => ["No match found"]
exception.response # => #<Faraday::Response ...>
exception.response.status # => 400
exception.response.body # => {"code"=>9004, "message"=>"No match found", "status"=>"failure"}
When to use call or call!
An example of where to use call would be in a controller doing an inline synchronous request.
class SomeController < ApplicationController
def update
@document = ZohoSign::Document::UpdateService.call(**params)
if @document.success?
redirect_to [@document], notice: 'Success', status: :see_other
else
flash.now[:alert] = @document.errors..to_sentence
render :edit, status: :unprocessable_entity
end
end
end
An example of where to use call! would be in a job doing an asynchronous request.
You can use the exceptions to determine which retry strategy to use and which to discard.
class SomeJob < ApplicationJob
discard_on ZohoSign::NotFoundError
retry_on ZohoSign::RequestTimeoutError, wait: 5.minutes, attempts: :unlimited
retry_on ZohoSign::TooManyRequestsError, wait: :polynomially_longer, attempts: 10
def perform
ZohoSign::Document::UpdateService.call!(**params)
end
end
Using lists
If you don't provide the limit argument, multiple API requests will be made untill all records have been returned. You could be rate limited, so use wisely.
Note that the offset argument starts at 1 for the first item.
Service Objects
Documents
### Documents #### List documents ```ruby # https://www.zoho.com/sign/api/document-managment/get-document-list.html ZohoSign::Document::ListService.call(offset: 1, limit: 10).each do |facade| facade.description end ``` Sort by column. ```ruby ZohoSign::Document::ListService.call(sort_column: 'recipient_email', sort_order: 'ASC').map { _1 } ``` Filter by column. ```ruby ZohoSign::Document::ListService.call(search_columns: { recipient_email: '[email protected]' }).map { _1 } ``` Columns to sort and filter by are `request_name`, `folder_name`, `owner_full_name`, `recipient_email`, `form_name` and `created_time`. #### Get a document ```ruby # https://www.zoho.com/sign/api/document-managment/get-details-of-a-particular-document.html service = ZohoSign::Document::GetService.call(id: '') service.request_name service.request_id service.request_status service.owner_email service.owner_first_name service.owner_last_name service.attachments service.sign_percentage ... ``` #### Create a document ```ruby # https://www.zoho.com/sign/api/document-managment/create-document.html ZohoSign::Document::CreateService.call( file: '/path/to/file.pdf', # or File.open('/path/to/file.pdf') file_name: 'file.pdf', file_content_type: 'application/pdf', data: { requests: { request_name: 'Name', is_sequential: false, actions: [{ action_type: 'SIGN', recipient_email: '[email protected]', recipient_name: 'Eric Cartman', verify_recipient: true, verification_type: 'EMAIL' }] } } ) ``` #### Update a document ```ruby # https://www.zoho.com/sign/api/document-managment/update-document.html ZohoSign::Document::UpdateService.call( id: '', data: { requests: { request_name: 'Name Updated', actions: [{ action_id: '', action_type: 'SIGN', recipient_email: '[email protected]', recipient_name: 'Stan Marsh' }] } } ) ``` #### Delete a document ```ruby # https://www.zoho.com/sign/api/document-managment/delete-document.html ZohoSign::Document::DeleteService.call(id: '') ``` #### Sign a document A unique signing URL will be generated, which will be valid for two minutes. If the signing page is not open by then, a new link needs to be generated and it is a one-time usable URL. ```ruby # https://www.zoho.com/sign/api/embedded-signing.html service = ZohoSign::Document::Action::EmbedToken::GetService.call(request_id: '', action_id: '', host: '') service.sign_url ```Folders
### Folders TODO: ...Field Types
### Field Types TODO: ...Request Types
### Request Types TODO: ...Templates
### Templates #### List templates ```ruby # https://www.zoho.com/sign/api/template-managment/get-template-list.html ZohoSign::Template::ListService.call(offset: 1, limit: 10).each do |facade| facade.description end ``` Sort by column. ```ruby ZohoSign::Template::ListService.call(sort_column: 'template_name', sort_order: 'ASC').map { _1 } ``` Filter by column. ```ruby ZohoSign::Template::ListService.call(search_columns: { template_name: 'Eric Template' }).map { _1 } ``` Columns to sort and filter by are `template_name`, `owner_first_name` and `modified_time`. #### Get a template ```ruby # https://www.zoho.com/sign/api/template-managment/get-template-details.html service = ZohoSign::Template::GetService.call(id: '') service.description service.document_fields service.email_reminders service.expiration_days service.folder_name service.folder_id service.owner_email service.template_name service.request_status ... ``` #### Create a template TODO: ... #### Update a template TODO: ... #### Delete a template TODO: ... #### Create document from template The auto filled fields specified in the `field_data` object should be marked as **Prefill by you** when creating the document template. ```ruby # https://www.zoho.com/sign/api/template-managment/send-documents-using-template.html ZohoSign::Template::Document::CreateService.call!( id: '', is_quicksend: true, data: { templates: { request_name: 'Request Document', field_data: { field_text_data: { 'Full name' => 'Eric Cartman', 'Email' => '[email protected]' }, field_boolean_data: { 'Agreed to terms' => true }, field_date_data: { 'Inception date' => '31/01/2025' } }, actions: [{ action_type: 'SIGN', recipient_email: '[email protected]', recipient_name: 'Eric Cartman', verify_recipient: false, delivery_mode: 'EMAIL', action_id: '', role: 'Client' }] } } ) ```Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/kobusjoubert/zoho_sign.
License
The gem is available as open source under the terms of the MIT License.