zoho_sign 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 85bd1a932999943e553720f8e0d866a0b5365bdd2a29406c5016d59b2ae39f7b
4
+ data.tar.gz: 55cd33a724b2d63ecc0c95aa62cf1d2e5a6b114aa1d522244fc8314ccdf30ef6
5
+ SHA512:
6
+ metadata.gz: 57f9062cd8b92ecdfae801fbce3a70bd6a4b0a44d3c06d331dee9d0709006e79ddb4b11ab90804d0c9719c9898768019f8227f78cf15ca9ab613d6271785b163
7
+ data.tar.gz: 9bd95ce532b3f593f764dfb48f4bb8af5a0661f2e67ca935aa493b556828e07aeb38db07e70235ac334faacb5605b26679f75a939fcaea9be28762cd670f8c54
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,31 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Style/Documentation:
14
+ Enabled: false
15
+
16
+ Layout/LineLength:
17
+ Max: 120
18
+
19
+ Metrics/ParameterLists:
20
+ Enabled: false
21
+
22
+ Metrics/MethodLength:
23
+ Enabled: false
24
+
25
+ Metrics/AbcSize:
26
+ Enabled: false
27
+
28
+ Metrics/BlockLength:
29
+ Exclude:
30
+ - 'zoho_sign.gemspec'
31
+ - 'spec/**/*.rb'
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-09-17
4
+
5
+ - Initial release:
6
+ - `ZohoSign::Template.all`
7
+ - `ZohoSign::Template.find`
8
+ - `ZohoSign::Template.create_document`
9
+ - `ZohoSign::Document.all`
10
+ - `ZohoSign::Document.find`
11
+ - `ZohoSign::Document.download_pdf`
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at wakematta@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in zoho_sign.gemspec
6
+ gemspec
7
+
8
+ # Utility
9
+ gem "dotenv"
10
+ gem "rake", "~> 13.0"
11
+
12
+ # Lints
13
+ gem "rubocop", "~> 1.7"
14
+
15
+ # Console
16
+ gem "pry"
17
+
18
+ # Testing
19
+ gem "faker"
20
+ gem "rspec", "~> 3.0"
21
+ gem "webmock"
data/Gemfile.lock ADDED
@@ -0,0 +1,124 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ zoho_sign (0.1.0)
5
+ activesupport
6
+ addressable
7
+ dry-configurable (~> 0.13)
8
+ faraday
9
+ faraday_middleware
10
+ rainbow
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activesupport (6.1.4.1)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 1.6, < 2)
18
+ minitest (>= 5.1)
19
+ tzinfo (~> 2.0)
20
+ zeitwerk (~> 2.3)
21
+ addressable (2.8.0)
22
+ public_suffix (>= 2.0.2, < 5.0)
23
+ ast (2.4.2)
24
+ coderay (1.1.3)
25
+ concurrent-ruby (1.1.9)
26
+ crack (0.4.5)
27
+ rexml
28
+ diff-lcs (1.4.4)
29
+ dotenv (2.7.6)
30
+ dry-configurable (0.13.0)
31
+ concurrent-ruby (~> 1.0)
32
+ dry-core (~> 0.6)
33
+ dry-core (0.7.1)
34
+ concurrent-ruby (~> 1.0)
35
+ faker (2.19.0)
36
+ i18n (>= 1.6, < 2)
37
+ faraday (1.8.0)
38
+ faraday-em_http (~> 1.0)
39
+ faraday-em_synchrony (~> 1.0)
40
+ faraday-excon (~> 1.1)
41
+ faraday-httpclient (~> 1.0.1)
42
+ faraday-net_http (~> 1.0)
43
+ faraday-net_http_persistent (~> 1.1)
44
+ faraday-patron (~> 1.0)
45
+ faraday-rack (~> 1.0)
46
+ multipart-post (>= 1.2, < 3)
47
+ ruby2_keywords (>= 0.0.4)
48
+ faraday-em_http (1.0.0)
49
+ faraday-em_synchrony (1.0.0)
50
+ faraday-excon (1.1.0)
51
+ faraday-httpclient (1.0.1)
52
+ faraday-net_http (1.0.1)
53
+ faraday-net_http_persistent (1.2.0)
54
+ faraday-patron (1.0.0)
55
+ faraday-rack (1.0.0)
56
+ faraday_middleware (1.1.0)
57
+ faraday (~> 1.0)
58
+ hashdiff (1.0.1)
59
+ i18n (1.8.10)
60
+ concurrent-ruby (~> 1.0)
61
+ method_source (1.0.0)
62
+ minitest (5.14.4)
63
+ multipart-post (2.1.1)
64
+ parallel (1.21.0)
65
+ parser (3.0.2.0)
66
+ ast (~> 2.4.1)
67
+ pry (0.14.1)
68
+ coderay (~> 1.1)
69
+ method_source (~> 1.0)
70
+ public_suffix (4.0.6)
71
+ rainbow (3.0.0)
72
+ rake (13.0.6)
73
+ regexp_parser (2.1.1)
74
+ rexml (3.2.5)
75
+ rspec (3.10.0)
76
+ rspec-core (~> 3.10.0)
77
+ rspec-expectations (~> 3.10.0)
78
+ rspec-mocks (~> 3.10.0)
79
+ rspec-core (3.10.1)
80
+ rspec-support (~> 3.10.0)
81
+ rspec-expectations (3.10.1)
82
+ diff-lcs (>= 1.2.0, < 2.0)
83
+ rspec-support (~> 3.10.0)
84
+ rspec-mocks (3.10.2)
85
+ diff-lcs (>= 1.2.0, < 2.0)
86
+ rspec-support (~> 3.10.0)
87
+ rspec-support (3.10.2)
88
+ rubocop (1.21.0)
89
+ parallel (~> 1.10)
90
+ parser (>= 3.0.0.0)
91
+ rainbow (>= 2.2.2, < 4.0)
92
+ regexp_parser (>= 1.8, < 3.0)
93
+ rexml
94
+ rubocop-ast (>= 1.9.1, < 2.0)
95
+ ruby-progressbar (~> 1.7)
96
+ unicode-display_width (>= 1.4.0, < 3.0)
97
+ rubocop-ast (1.12.0)
98
+ parser (>= 3.0.1.1)
99
+ ruby-progressbar (1.11.0)
100
+ ruby2_keywords (0.0.5)
101
+ tzinfo (2.0.4)
102
+ concurrent-ruby (~> 1.0)
103
+ unicode-display_width (2.1.0)
104
+ webmock (3.14.0)
105
+ addressable (>= 2.8.0)
106
+ crack (>= 0.3.2)
107
+ hashdiff (>= 0.4.0, < 2.0.0)
108
+ zeitwerk (2.4.2)
109
+
110
+ PLATFORMS
111
+ x86_64-linux
112
+
113
+ DEPENDENCIES
114
+ dotenv
115
+ faker
116
+ pry
117
+ rake (~> 13.0)
118
+ rspec (~> 3.0)
119
+ rubocop (~> 1.7)
120
+ webmock
121
+ zoho_sign!
122
+
123
+ BUNDLED WITH
124
+ 2.2.27
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Mohamed Ziata
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # ZohoSign
2
+
3
+ Simple wrapper around Zoho Sign, using OAuth 2.0 protocol for authentication.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'zoho_sign'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install zoho_sign
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Initialization
28
+
29
+ Create the following file (or equivalent for your app) and fill it with your credentails.
30
+
31
+ If you don't have them, follow this official guide: https://www.zoho.com/sign/api/#getting-started
32
+
33
+ **`config/initializers/zoho_sign.rb`**
34
+ ```ruby
35
+ require "zoho_sign"
36
+
37
+ ZohoSign.config.debug = true # Default is false. You can enable it to see the requests made by the Gem.
38
+
39
+ ZohoSign.config.update(
40
+ oauth: {
41
+ client_id: ENV["ZOHO_SIGN_CLIENT_ID"],
42
+ client_secret: ENV["ZOHO_SIGN_CLIENT_SECRET"],
43
+ access_token: ENV["ZOHO_SIGN_ACCESS_TOKEN"],
44
+ refresh_token: ENV["ZOHO_SIGN_REFRESH_TOKEN"]
45
+ }
46
+ )
47
+
48
+ params = ZohoSign::Auth.refresh_token(ENV["ZOHO_SIGN_REFRESH_TOKEN"])
49
+ ZohoSign.config.connection = params
50
+ ```
51
+
52
+ ### How to use `ZohoSign::Template`
53
+
54
+ Find all templetes:
55
+
56
+ ```ruby
57
+ templates = ZohoSign::Template.all
58
+ ```
59
+
60
+ Find template by ID:
61
+
62
+ ```ruby
63
+ template = ZohoSign::Template.find("12345678900000000")
64
+ ```
65
+
66
+ Create document from template:
67
+
68
+ ```ruby
69
+ template = ZohoSign::Template.find("12345678900000000")
70
+
71
+ field_data = {
72
+ field_text_data: {
73
+ full_name: "Homer Simpson",
74
+ address: "742 Evergreen Terrace, Springfield, US"
75
+ }
76
+ }
77
+
78
+ recipient_data = [
79
+ {
80
+ role: "OldOwner",
81
+ action_id: "11111111111111111",
82
+ recipient: {
83
+ name: "Moammar Morris 'Moe' Szyslak",
84
+ email: "moe@springfield.com",
85
+ verify: true
86
+ },
87
+ private_notes: "Please sign this agreement"
88
+ },
89
+ {
90
+ role: "NewOwner",
91
+ action_id: "22222222222222222",
92
+ recipient: {
93
+ name: "Homer Simpson",
94
+ email: "homer.simpson@springfield.com",
95
+ verify: true
96
+ },
97
+ private_notes: "Please sign this agreement"
98
+ }
99
+ ]
100
+
101
+ document = ZohoSign::Template.create_document(
102
+ template_id: template.attributes[:template_id],
103
+ field_data: field_data,
104
+ recipient_data: recipient_data,
105
+ document_name: "Agreement (v3)",
106
+ shared_notes: "Agreement to buy Moe's Tavern"
107
+ )
108
+ ```
109
+
110
+ Download document:
111
+
112
+ ```ruby
113
+ document = ZohoSign::Document.find("12345656000000")
114
+ document.download_pdf
115
+
116
+ # OR
117
+
118
+ ZohoSign::Document.download_pdf("12345656000000")
119
+ ```
120
+
121
+ ## Development
122
+
123
+ 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.
124
+
125
+ 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](https://rubygems.org).
126
+
127
+ ## Contributing
128
+
129
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wecasa/zoho_sign. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/wecasa/zoho_sign/blob/master/CODE_OF_CONDUCT.md).
130
+
131
+ ## License
132
+
133
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
134
+
135
+ ## Code of Conduct
136
+
137
+ Everyone interacting in the ZohoSign project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/wecasa/zoho_sign/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "pry"
6
+ require "dotenv/load"
7
+
8
+ require "zoho_sign"
9
+
10
+ ZohoSign.config.debug = true
11
+
12
+ ZohoSign.config.update(
13
+ oauth: {
14
+ client_id: ENV["ZOHO_SIGN_CLIENT_ID"],
15
+ client_secret: ENV["ZOHO_SIGN_CLIENT_SECRET"],
16
+ access_token: ENV["ZOHO_SIGN_ACCESS_TOKEN"],
17
+ refresh_token: ENV["ZOHO_SIGN_REFRESH_TOKEN"]
18
+ }
19
+ )
20
+
21
+ unless ENV["ZOHO_SIGN_REFRESH_TOKEN"]
22
+ params = ZohoSign::Auth.get_token(ENV["ZOHO_SIGN_ACCESS_TOKEN"])
23
+ ZohoSign.config.oauth.refresh_token = params[:refresh_token] if params.key?(:refresh_token)
24
+ end
25
+
26
+ ZohoSign.config.connection = {
27
+ access_token: ZohoSign.config.oauth.access_token,
28
+ refresh_token: ZohoSign.config.oauth.refresh_token,
29
+ api_domain: ZohoSign.config.api.domain,
30
+ api_base_path: ZohoSign.config.api.base_path,
31
+ expires_in: 3600
32
+ }
33
+
34
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "faraday"
5
+ require "faraday_middleware"
6
+ require "addressable"
7
+
8
+ module ZohoSign
9
+ # Class that takes care of authentication using Oauth2 workflow as described here:
10
+ # https://www.zoho.com/crm/help/api/v2/#oauth-request.
11
+ class Auth
12
+ extend Forwardable
13
+
14
+ AUTH_DOMAIN_PATH = "https://accounts.zoho.com"
15
+
16
+ TOKEN_PATH = "/oauth/v2/token"
17
+
18
+ DEFAULT_SCOPES = %w[
19
+ ZohoSign.account.all
20
+ ZohoSign.documents.all
21
+ ZohoSign.templates.all
22
+ ].freeze
23
+
24
+ DEFAULT_ACCESS_TYPE = "offline"
25
+
26
+ def initialize(access_type: DEFAULT_ACCESS_TYPE, scopes: DEFAULT_SCOPES)
27
+ @configuration = ZohoSign.config
28
+ @access_type = access_type
29
+ @scopes = scopes
30
+ end
31
+
32
+ def self.refresh_token(refresh_token)
33
+ new.refresh_token(refresh_token)
34
+ end
35
+
36
+ def refresh_token(refresh_token)
37
+ uri = refresh_url(refresh_token)
38
+
39
+ log "POST #{uri}"
40
+
41
+ result = Faraday.post(uri)
42
+ json = JSON.parse(result.body, symbolize_names: true)
43
+ json.merge(refresh_token: refresh_token)
44
+ end
45
+
46
+ def refresh_url(refresh_token)
47
+ uri = token_full_uri
48
+
49
+ uri.query_values = {
50
+ client_id: @configuration.oauth.client_id,
51
+ client_secret: @configuration.oauth.client_secret,
52
+ refresh_token: refresh_token,
53
+ grant_type: "refresh_token"
54
+ }
55
+
56
+ Addressable::URI.unencode(uri.to_s)
57
+ end
58
+
59
+ def token_full_uri
60
+ Addressable::URI.join(AUTH_DOMAIN_PATH, TOKEN_PATH)
61
+ end
62
+
63
+ def self.get_token(grant_token)
64
+ new.get_token(grant_token)
65
+ end
66
+
67
+ def get_token(grant_token)
68
+ result = Faraday.post(token_url(grant_token))
69
+ JSON.parse(result.body, symbolize_names: true)
70
+ end
71
+
72
+ def token_url(grant_token)
73
+ uri = token_full_uri
74
+
75
+ uri.query_values = {
76
+ client_id: @configuration.oauth.client_id,
77
+ client_secret: @configuration.oauth.client_secret,
78
+ code: grant_token,
79
+ redirect_uri: @configuration.oauth.redirect_uri,
80
+ grant_type: "authorization_code"
81
+ }
82
+
83
+ Addressable::URI.unencode(uri.to_s)
84
+ end
85
+
86
+ private
87
+
88
+ def log(text)
89
+ return unless ZohoSign.config.debug
90
+
91
+ puts Rainbow("[ZohoSign] #{text}").blue.bright
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "active_support/core_ext/object/to_query"
5
+
6
+ module ZohoSign
7
+ # The main class for record classes
8
+ class BaseRecord
9
+ # Default number of records when fetching all.
10
+ DEFAULT_RECORDS_PER_REQUEST = 100
11
+
12
+ # Default starting index point when fetching all.
13
+ DEFAULT_START_INDEX = 1
14
+
15
+ attr_accessor :attributes
16
+
17
+ class << self
18
+ # @param [Integer] :start_index (1) Start Index.
19
+ # @param [Integer] :per_request (100) No of rows to be retrieved. Possible values: 10, 25, 50 or 100.
20
+ # @param [String] :sort_column (nil)
21
+ # @param [String] :sort_order (nil)
22
+ #
23
+ # @return [Array] Array of instances of current class
24
+ def all(start_index: DEFAULT_START_INDEX, per_request: DEFAULT_RECORDS_PER_REQUEST,
25
+ sort_column: nil, sort_order: nil)
26
+ params = build_params_for_index_action(start_index, per_request, sort_column, sort_order)
27
+ body = connection.get(request_path, params)
28
+ response = build_response(body)
29
+ data = response.data(data_key)
30
+
31
+ loop do
32
+ break unless response.more_rows?
33
+
34
+ start_index += response.data(data_key).count
35
+ params = build_params_for_index_action(start_index, per_request, sort_column, sort_order)
36
+ body = connection.get(request_path, params)
37
+ response = build_response(body)
38
+ data += response.data(data_key)
39
+ end
40
+
41
+ data.map { |record_attributes| new(**record_attributes) }
42
+ end
43
+
44
+ # @param [String] zoho_id
45
+ #
46
+ # @return [Class] Instance of current class
47
+ def find(zoho_id)
48
+ body = connection.get("#{request_path}/#{zoho_id}")
49
+ response = build_response(body)
50
+ record_attributes = response.data(data_key)
51
+ new(**record_attributes)
52
+ end
53
+
54
+ private
55
+
56
+ def connection
57
+ ZohoSign.connection
58
+ end
59
+
60
+ def request_path
61
+ raise ZohoSign::Error, "this method should be overwrite in the child class. Please open an issue at https://github.com/wecasa/zoho_sign"
62
+ end
63
+
64
+ def data_key
65
+ raise ZohoSign::Error, "this method should be overwrite in the child class. Please open an issue at https://github.com/wecasa/zoho_sign"
66
+ end
67
+
68
+ def build_response(body)
69
+ response = ZohoSign::ResponseHandler.new(body)
70
+ return response if response.success?
71
+
72
+ raise RecordNotFoundError, response.message if response.not_found_error?
73
+
74
+ # This is a fallback because ZohoSign API could evolve by adding new error messages.
75
+ raise Error, response.detailed_message
76
+ end
77
+
78
+ def build_params_for_index_action(start_index, row_count, sort_column, sort_order)
79
+ {
80
+ data: {
81
+ page_context: {
82
+ start_index: start_index,
83
+ row_count: row_count,
84
+ sort_column: sort_column,
85
+ sort_order: sort_order
86
+ }.compact
87
+ }.compact.to_json
88
+ }.compact
89
+ end
90
+ end
91
+
92
+ def initialize(**attributes)
93
+ @attributes = attributes
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rainbow"
4
+ require "faraday"
5
+ require "faraday_middleware"
6
+
7
+ require_relative "response_handler"
8
+
9
+ module ZohoSign
10
+ # Internal class that has all the logic to stablish a connection with Zoho API
11
+ class Connection
12
+ attr_accessor :access_token, :refresh_token, :api_domain, :api_base_path, :expires_ins
13
+
14
+ def initialize(access_token:, refresh_token: nil, api_domain: nil, api_base_path: nil, expires_in: 3600)
15
+ @access_token = access_token
16
+ @refresh_token ||= refresh_token # Do not overwrite if it's already set
17
+ @api_domain = api_domain || ZohoSign.config.api.domain
18
+ @api_base_path = api_base_path || ZohoSign.config.api.base_path
19
+ @expires_in = expires_in
20
+ end
21
+
22
+ def get(path, params = {})
23
+ log "GET #{path} with #{params}"
24
+
25
+ response = with_refresh { adapter.get(path, params) }
26
+ response.body
27
+ end
28
+
29
+ def post(path, params = {})
30
+ log "POST #{path} with #{params}"
31
+
32
+ response = with_refresh { adapter.post(path, params) }
33
+ response.body
34
+ end
35
+
36
+ def put(path, params = {})
37
+ log "PUT #{path} with #{params}"
38
+
39
+ response = with_refresh { adapter.put(path, params) }
40
+ response.body
41
+ end
42
+
43
+ def delete(path, params = {})
44
+ log "DELETE #{path} with #{params}"
45
+
46
+ response = with_refresh { adapter.delete(path, params) }
47
+ response.body
48
+ end
49
+
50
+ def download(path, params = {})
51
+ log "GET #{path} with #{params}"
52
+
53
+ response = with_refresh { download_adapter.get(path, params) }
54
+ response.body
55
+ end
56
+
57
+ private
58
+
59
+ def log(text)
60
+ return unless ZohoSign.config.debug
61
+
62
+ puts Rainbow("[ZohoSign] #{text}").blue.bright
63
+ end
64
+
65
+ def with_refresh
66
+ http_response = yield
67
+ response = ZohoSign::ResponseHandler.new(http_response.body, http_response.status)
68
+ # Try to refresh the token and try again
69
+ if response.invalid_token_error? && refresh_token?
70
+ log "Refreshing outdated token... #{@access_token}"
71
+ params = ZohoSign::Auth.refresh_token(@refresh_token)
72
+ @access_token = params[:access_token]
73
+ http_response = yield
74
+ response = ZohoSign::ResponseHandler.new(http_response.body, http_response.status)
75
+ end
76
+
77
+ raise ZohoSign::Error, response.detailed_message if response.error?
78
+
79
+ response
80
+ end
81
+
82
+ def base_url
83
+ "#{@api_domain}#{@api_base_path}"
84
+ end
85
+
86
+ def authorization_token
87
+ "Zoho-oauthtoken #{@access_token}"
88
+ end
89
+
90
+ def access_token?
91
+ !@access_token.empty?
92
+ end
93
+
94
+ def refresh_token?
95
+ !@refresh_token.empty?
96
+ end
97
+
98
+ def adapter
99
+ Faraday.new(url: base_url) do |conn|
100
+ conn.headers["Authorization"] = authorization_token if access_token?
101
+ conn.headers["Content-Type"] = "application/x-www-form-urlencoded"
102
+ conn.request :json
103
+ conn.request :retry
104
+ conn.response :json, parser_options: { symbolize_names: true }, content_type: /\bjson$/
105
+ conn.response :logger if ZohoSign.config.debug
106
+ conn.adapter Faraday.default_adapter
107
+ end
108
+ end
109
+
110
+ def download_adapter
111
+ Faraday.new(url: base_url) do |conn|
112
+ conn.headers["Authorization"] = authorization_token if access_token?
113
+ conn.headers["Content-Type"] = "application/x-www-form-urlencoded"
114
+ conn.request :retry
115
+ conn.response :json, parser_options: { symbolize_names: true }, content_type: /\bjson$/
116
+ conn.response :logger if ZohoSign.config.debug
117
+ conn.adapter Faraday.default_adapter
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "base_record"
5
+
6
+ module ZohoSign
7
+ # Record class to interact with Zoho Sign Documents API
8
+ class Document < ZohoSign::BaseRecord
9
+ class << self
10
+ def download_pdf(document_id)
11
+ body = connection.download("#{request_path}/#{document_id}/pdf")
12
+ pdf_tmp = Tempfile.new([tempfile_name, ".pdf"], encoding: "ascii-8bit")
13
+ pdf_tmp.write(body)
14
+ pdf_tmp.rewind
15
+ pdf_tmp
16
+ end
17
+
18
+ private
19
+
20
+ def request_path
21
+ "requests"
22
+ end
23
+
24
+ def data_key
25
+ :requests
26
+ end
27
+
28
+ def tempfile_name
29
+ SecureRandom.uuid
30
+ end
31
+ end
32
+
33
+ def download_pdf
34
+ self.class.download_pdf(@attributes.fetch(:request_id))
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZohoSign
4
+ # Internal class to centralize the code related to the response of the Zoho Sign API
5
+ class ResponseHandler
6
+ def initialize(response, http_response = nil)
7
+ @response = response || {}
8
+ @http_response = http_response
9
+ end
10
+
11
+ def body
12
+ @response
13
+ end
14
+
15
+ def data(key)
16
+ return unless @response.is_a?(Hash)
17
+
18
+ @response[key]
19
+ end
20
+
21
+ def success?
22
+ return false unless @response.is_a?(Hash)
23
+
24
+ @http_response == 200 || @response[:status] == "success"
25
+ end
26
+
27
+ def error?
28
+ return false if @http_response == 200
29
+
30
+ (!@http_response.nil? && @http_response != 200) ||
31
+ @response[:status] == "error" ||
32
+ @response[:status] == "failure"
33
+ end
34
+
35
+ def message
36
+ [
37
+ @response[:message],
38
+ ("error_param: #{@response[:error_param]}" if @response.key?(:error_param))
39
+ ].join(" ")
40
+ end
41
+
42
+ def detailed_message
43
+ @response.to_s
44
+ end
45
+
46
+ def more_rows?
47
+ !!@response.dig(:page_context, :has_more_rows)
48
+ end
49
+
50
+ # @response = { code: 9004, status: "failure", message: "No match found" }
51
+ def not_found_error?
52
+ code?(9004)
53
+ end
54
+
55
+ # @response = {
56
+ # code: 9008,
57
+ # status: "failure",
58
+ # message: "templates occurs less than minimum occurance of 1",
59
+ # error_param: "templates"
60
+ # }
61
+ def missing_param_error?
62
+ code?(9008)
63
+ end
64
+
65
+ # @response = {
66
+ # code: 9015,
67
+ # status: "failure",
68
+ # message: "Extra key found",
69
+ # status: "failure",
70
+ # error_param: "data[page_context][row_count]"
71
+ # }
72
+ def extra_key_found_error?
73
+ code?(9015)
74
+ end
75
+
76
+ # @response = { code: 9031, status: "failure", message: "Ticket invalid" }
77
+ def ticket_invalid_error?
78
+ code?(9031)
79
+ end
80
+
81
+ # @response = { code: 9039, status: "failure", message: "Unable to process your request" }
82
+ def unable_to_process_request_error?
83
+ code?(9039)
84
+ end
85
+
86
+ # @response = { code: 9041, status: "failure", message: "Invalid Oauth token" }
87
+ def invalid_token_error?
88
+ if @response.is_a?(Hash)
89
+ code?(9041)
90
+ else
91
+ @http_response == 401 && @response.include?("Invalid Oauth token")
92
+ end
93
+ end
94
+
95
+ # @response = { code: 12001, status: "failure", message: "Upgrade API credits to access resources using API" }
96
+ def no_more_credits_error?
97
+ code?(12_001)
98
+ end
99
+
100
+ private
101
+
102
+ def code?(code)
103
+ return unless @response.is_a?(Hash)
104
+
105
+ @response.key?(:code) && @response[:code] == code
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_record"
4
+ require_relative "document"
5
+
6
+ module ZohoSign
7
+ # Record class to interact with Zoho Sign Templates API
8
+ class Template < ZohoSign::BaseRecord
9
+ class << self
10
+ # @param [String] :template_id Zoho Sign Template ID
11
+ #
12
+ # @param [Hash] :field_data Data used to prefill the document
13
+ # @option :field_data [Hash] :field_text_data
14
+ # @option :field_data [Hash] :field_date_data
15
+ # @option :field_data [Hash] :field_boolean_data
16
+ #
17
+ # @param [Array] :recipient_data Recipient
18
+ # @option :recipient_data [String] :role
19
+ # @option :recipient_data [String] :private_notes
20
+ # @option :recipient_data [String] :action_id
21
+ # @option :recipient_data [String] :action_type
22
+ # @option :recipient_data [String] :verification_type
23
+ # @option :recipient_data [Hash] :recipient
24
+ # @option :recipient [String] :name
25
+ # @option :recipient [String] :email
26
+ # @option :recipient [String] :phone
27
+ # @option :recipient [String] :country_code
28
+ # @option :recipient [Boolean] :verify
29
+ #
30
+ # @param [String] :shared_notes Text to be shown for all recipients
31
+ # @param [Boolean] :quicksend
32
+ #
33
+ # @param [String] :shared_notes Text to be shown for all recipients
34
+ # @param [Boolean] :quicksend
35
+ #
36
+ # @return [Class] ZohoSign::Document instance
37
+ #
38
+ def create_document(template_id:, field_data: {}, recipient_data: [], shared_notes: "", quicksend: true,
39
+ document_name: nil)
40
+ actions = recipient_data.map do |action_data|
41
+ recipient = action_data.fetch(:recipient)
42
+ {
43
+ role: action_data.fetch(:role),
44
+ recipient_name: recipient.fetch(:name),
45
+ recipient_email: recipient.fetch(:email),
46
+ recipient_phonenumber: recipient[:phone],
47
+ recipient_countrycode: recipient[:country_code],
48
+ verify_recipient: recipient.fetch(:verify, true),
49
+ private_notes: action_data[:private_notes],
50
+ verification_type: action_data.fetch(:verification_type, "EMAIL"),
51
+ action_type: action_data.fetch(:action_type, "SIGN"),
52
+ action_id: action_data.fetch(:action_id)
53
+ }.compact
54
+ end
55
+
56
+ params = {
57
+ is_quicksend: quicksend,
58
+ data: {
59
+ templates: {
60
+ request_name: document_name,
61
+ field_data: field_data,
62
+ actions: actions,
63
+ notes: shared_notes
64
+ }.compact
65
+ }.to_json
66
+ }
67
+
68
+ body = connection.post("#{request_path}/#{template_id}/createdocument", params.to_query)
69
+ response = build_response(body)
70
+ document_attributes = response.data(:requests)
71
+ ZohoSign::Document.new(**document_attributes)
72
+ end
73
+
74
+ private
75
+
76
+ def request_path
77
+ "templates"
78
+ end
79
+
80
+ def data_key
81
+ :templates
82
+ end
83
+ end
84
+
85
+ # - Instance methods
86
+ def create_document(**arguments)
87
+ arguments[:template_id] = @attributes.fetch(:template_id)
88
+ self.class.create_document(**arguments)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZohoSign
4
+ VERSION = "0.1.1"
5
+ end
data/lib/zoho_sign.rb ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-configurable"
4
+
5
+ require_relative "zoho_sign/version"
6
+ require_relative "zoho_sign/auth"
7
+ require_relative "zoho_sign/connection"
8
+
9
+ # Record classes
10
+ require_relative "zoho_sign/template"
11
+ require_relative "zoho_sign/document"
12
+
13
+ # Namespace ZohoSign
14
+ module ZohoSign
15
+ extend Dry::Configurable
16
+
17
+ setting :debug, default: false
18
+
19
+ # Follow the instruction found here to generate your credentails:
20
+ # https://www.zoho.com/sign/api/#getting-started
21
+ setting :oauth do
22
+ setting :client_id
23
+ setting :client_secret
24
+ setting :access_token
25
+ setting :refresh_token
26
+ setting :redirect_uri, default: "https://example.com"
27
+ end
28
+
29
+ setting :api do
30
+ setting :domain, default: "https://sign.zoho.com"
31
+ setting :base_path, default: "/api/v1"
32
+ end
33
+
34
+ setting :connection, reader: true, constructor: lambda { |params|
35
+ return unless params
36
+ raise Error, "ERROR: #{params[:error]}" if params && params[:error]
37
+
38
+ connection_params = params.dup.slice(:access_token, :expires_in, :refresh_token)
39
+ Connection.new(**connection_params)
40
+ }
41
+
42
+ class Error < StandardError; end
43
+
44
+ class RecordNotFoundError < Error; end
45
+ end
data/renovate.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "config:base"
4
+ ]
5
+ }
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zoho_sign
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Wecasa Developers Team
8
+ - Mohamed Ziata
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2021-12-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: addressable
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: dry-configurable
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.13'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.13'
56
+ - !ruby/object:Gem::Dependency
57
+ name: faraday
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: faraday_middleware
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rainbow
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: Ruby gem to allow easy interaction with Zoho Sign API (v1).
99
+ email:
100
+ - tech@wecasa.fr
101
+ - wakematta@gmail.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".rspec"
107
+ - ".rubocop.yml"
108
+ - CHANGELOG.md
109
+ - CODE_OF_CONDUCT.md
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - bin/console
116
+ - bin/setup
117
+ - lib/zoho_sign.rb
118
+ - lib/zoho_sign/auth.rb
119
+ - lib/zoho_sign/base_record.rb
120
+ - lib/zoho_sign/connection.rb
121
+ - lib/zoho_sign/document.rb
122
+ - lib/zoho_sign/response_handler.rb
123
+ - lib/zoho_sign/template.rb
124
+ - lib/zoho_sign/version.rb
125
+ - renovate.json
126
+ homepage: https://www.zoho.com/sign/api
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ homepage_uri: https://www.zoho.com/sign/api
131
+ documentation_uri: https://github.com/wecasa/zoho_sign
132
+ source_code_uri: https://github.com/wecasa/zoho_sign
133
+ changelog_uri: https://github.com/wecasa/zoho_sign/blob/master/CHANGELOG.md
134
+ bug_tracker_uri: https://github.com/wecasa/zoho_sign/issues
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 2.6.0
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubygems_version: 3.2.32
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Zoho Sign API Wrapper
154
+ test_files: []