boldsign 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -2
- data/README.md +39 -10
- data/boldsign.gemspec +1 -1
- data/lib/boldsign/access_token.rb +95 -0
- data/lib/boldsign/client.rb +74 -10
- data/lib/boldsign/resources/document.rb +16 -10
- data/lib/boldsign/version.rb +1 -1
- data/lib/boldsign.rb +38 -4
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f5158801c4b85358c9f7b45376a2767bb98c3374e994b2cb643840245ce641f
|
|
4
|
+
data.tar.gz: d4748c0000aabf9012b0d0403ca54d147a5bf190674cf80c96079302fa97dd95
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b8e468b7897ee9a34c9bed4256cd2fd305a58e43af9ab07c889de9ad5c024d8da6e7b01b4bc1d3ca572e5b785b786ebc9544b4559797b7e95d96cc01cb493ea
|
|
7
|
+
data.tar.gz: 29670ea3951d2adade1a687d6786510409e48707d27844dedc07b2659637b3ea7c5089672ea1f9d550e7fced26d89fcd2f98c4bea29ae324430e83652d807550
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] — 2026-07-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Multi-signer support for multipart `send_document` (file uploads). Array-valued
|
|
14
|
+
body fields (e.g. `signers: [...]`) now emit one JSON-object part per element,
|
|
15
|
+
all under the same field name (`Signers`, `Signers`, …) via
|
|
16
|
+
`Faraday::Multipart::Middleware`'s `flat_encode: true` — confirmed against
|
|
17
|
+
BoldSign's docs as the correct encoding for a multi-recipient envelope sent
|
|
18
|
+
alongside an uploaded file (as opposed to a template reference). Previously
|
|
19
|
+
this raised `NotImplementedError`. Multi-file uploads are still unsupported
|
|
20
|
+
and still raise.
|
|
21
|
+
|
|
22
|
+
## [0.5.0] — 2026-06-02
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- OAuth 2.0 authentication via the **client credentials** grant. Configure
|
|
26
|
+
`client_id` / `client_secret` (on `Boldsign.configure` or `Client.new`) and
|
|
27
|
+
the client fetches a bearer token from the region's account host
|
|
28
|
+
(`https://account.boldsign.com/connect/token` for `:us`), caches it until
|
|
29
|
+
shortly before `expires_in` lapses, and sends `Authorization: Bearer …`
|
|
30
|
+
instead of `X-API-KEY`. Token fetches are mutex-guarded for shared clients.
|
|
31
|
+
- `scope:` (defaults to `BoldSign.Documents.All`) and `token_url:` options to
|
|
32
|
+
override the requested scope and token endpoint.
|
|
33
|
+
- `access_token:` option to supply a pre-obtained bearer token that is used
|
|
34
|
+
as-is (not refreshed).
|
|
35
|
+
- `Boldsign::AccessToken` encapsulating the client-credentials token lifecycle.
|
|
36
|
+
- `TOKEN_REGIONS` / `DEFAULT_TOKEN_BASE_URL` constants and `Client#auth_mode`.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- `Client.new` no longer requires an API key. It accepts any one of
|
|
40
|
+
`client_id`/`client_secret`, `access_token`, or `api_key`, and raises
|
|
41
|
+
`ConfigurationError` only when none is provided. The API-key path is
|
|
42
|
+
unchanged, so existing `api_key:` callers keep working.
|
|
43
|
+
|
|
10
44
|
## [0.4.0] — 2026-05-22
|
|
11
45
|
|
|
12
46
|
### Changed
|
|
@@ -68,5 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
68
102
|
- 100% line and branch test coverage enforced via SimpleCov.
|
|
69
103
|
- GitHub Actions CI on Ruby 3.4 and 4.0.
|
|
70
104
|
|
|
71
|
-
[Unreleased]: https://github.com/
|
|
72
|
-
[0.
|
|
105
|
+
[Unreleased]: https://github.com/EscrowSafe/boldsign-ruby/compare/v0.6.0...HEAD
|
|
106
|
+
[0.6.0]: https://github.com/EscrowSafe/boldsign-ruby/compare/v0.5.0...v0.6.0
|
|
107
|
+
[0.5.0]: https://github.com/EscrowSafe/boldsign-ruby/compare/v0.4.0...v0.5.0
|
|
108
|
+
[0.1.0]: https://github.com/EscrowSafe/boldsign-ruby/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# boldsign-ruby
|
|
2
2
|
|
|
3
|
-
[](https://rubydoc.info/github/
|
|
3
|
+
[](https://github.com/EscrowSafe/boldsign-ruby/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubydoc.info/github/EscrowSafe/boldsign-ruby/main)
|
|
5
5
|
|
|
6
6
|
A Ruby client for the [BoldSign](https://developers.boldsign.com/) e-signature API.
|
|
7
7
|
|
|
@@ -17,6 +17,32 @@ gem "boldsign"
|
|
|
17
17
|
|
|
18
18
|
## Configuration
|
|
19
19
|
|
|
20
|
+
The client authenticates with either an API key or OAuth 2.0.
|
|
21
|
+
|
|
22
|
+
### OAuth 2.0 (client credentials)
|
|
23
|
+
|
|
24
|
+
Register an OAuth app in the BoldSign dashboard (API → OAuth Apps) and configure
|
|
25
|
+
its `client_id` / `client_secret`. The client fetches a bearer token from the
|
|
26
|
+
region's account host, caches it until shortly before it expires, and refreshes
|
|
27
|
+
automatically — no token plumbing on your side.
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
Boldsign.configure do |c|
|
|
31
|
+
c.client_id = ENV["BOLDSIGN_CLIENT_ID"]
|
|
32
|
+
c.client_secret = ENV["BOLDSIGN_CLIENT_SECRET"]
|
|
33
|
+
c.region = :us # :us, :eu, :ca, :au
|
|
34
|
+
# c.scope = "BoldSign.Documents.All" # optional; empty grants all the app allows
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
You can also pass a token you obtained yourself (used as-is, not refreshed):
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
Boldsign::Client.new(access_token: "…", region: :us)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### API key
|
|
45
|
+
|
|
20
46
|
```ruby
|
|
21
47
|
Boldsign.configure do |c|
|
|
22
48
|
c.api_key = ENV["BOLDSIGN_API_KEY"]
|
|
@@ -30,14 +56,17 @@ Or instantiate a client directly:
|
|
|
30
56
|
client = Boldsign::Client.new(api_key: "…", region: :us)
|
|
31
57
|
```
|
|
32
58
|
|
|
33
|
-
Region
|
|
59
|
+
Region hosts:
|
|
60
|
+
|
|
61
|
+
| Region | API base URL | OAuth account host |
|
|
62
|
+
| ------ | ------------ | ------------------ |
|
|
63
|
+
| `:us` | `https://api.boldsign.com` | `https://account.boldsign.com` |
|
|
64
|
+
| `:eu` | `https://api-eu.boldsign.com` | `https://account-eu.boldsign.com` |
|
|
65
|
+
| `:ca` | `https://api-ca.boldsign.com` | `https://account-ca.boldsign.com` |
|
|
66
|
+
| `:au` | `https://api-au.boldsign.com` | `https://account-au.boldsign.com` |
|
|
34
67
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
| `:us` | `https://api.boldsign.com` |
|
|
38
|
-
| `:eu` | `https://api-eu.boldsign.com` |
|
|
39
|
-
| `:ca` | `https://api-ca.boldsign.com` |
|
|
40
|
-
| `:au` | `https://api-au.boldsign.com` |
|
|
68
|
+
The token endpoint is the account host + `/connect/token`; override the full URL
|
|
69
|
+
with `token_url:` if a region differs.
|
|
41
70
|
|
|
42
71
|
## Usage
|
|
43
72
|
|
|
@@ -109,7 +138,7 @@ HTTP errors raise a typed subclass of `Boldsign::Error`:
|
|
|
109
138
|
## Documentation
|
|
110
139
|
|
|
111
140
|
Full YARD API documentation is auto-built and hosted at
|
|
112
|
-
[rubydoc.info/github/
|
|
141
|
+
[rubydoc.info/github/EscrowSafe/boldsign-ruby/main](https://rubydoc.info/github/EscrowSafe/boldsign-ruby/main).
|
|
113
142
|
|
|
114
143
|
To generate locally:
|
|
115
144
|
|
data/boldsign.gemspec
CHANGED
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.summary = "Ruby client for the BoldSign e-signature API"
|
|
10
10
|
spec.description = "A Ruby wrapper for the BoldSign REST API (documents, templates, " \
|
|
11
11
|
"contacts, teams, brands, webhooks, and more)."
|
|
12
|
-
spec.homepage = "https://github.com/
|
|
12
|
+
spec.homepage = "https://github.com/EscrowSafe/boldsign-ruby"
|
|
13
13
|
spec.license = "MIT"
|
|
14
14
|
spec.required_ruby_version = ">= 3.0"
|
|
15
15
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Boldsign
|
|
2
|
+
# Fetches and caches an OAuth 2.0 access token using the client credentials
|
|
3
|
+
# grant (server-to-server; no user interaction).
|
|
4
|
+
#
|
|
5
|
+
# A single instance is shared by a {Client} and may be hit from multiple
|
|
6
|
+
# threads, so token reads/refreshes are guarded by a mutex. The token is
|
|
7
|
+
# cached until shortly before it expires ({EXPIRY_LEEWAY_SECONDS}), then a new
|
|
8
|
+
# one is requested on the next call. The client credentials grant does not
|
|
9
|
+
# issue refresh tokens — expiry is handled by re-requesting.
|
|
10
|
+
#
|
|
11
|
+
# @see https://developers.boldsign.com/authentication/oauth-2-0/
|
|
12
|
+
class AccessToken
|
|
13
|
+
# Token endpoint path, appended to the region's account host.
|
|
14
|
+
TOKEN_PATH = "/connect/token".freeze
|
|
15
|
+
|
|
16
|
+
# Default scope requested for the access token. Covers the document /
|
|
17
|
+
# signature endpoints this gem is primarily used for. Pass a different
|
|
18
|
+
# `scope:` to request others (an empty scope grants all the app is allowed).
|
|
19
|
+
DEFAULT_SCOPE = "BoldSign.Documents.All".freeze
|
|
20
|
+
|
|
21
|
+
# Refresh this many seconds before the token actually expires, so an
|
|
22
|
+
# in-flight request never carries a token that lapses server-side mid-call.
|
|
23
|
+
EXPIRY_LEEWAY_SECONDS = 60
|
|
24
|
+
|
|
25
|
+
# Fallback lifetime (seconds) when the token response omits `expires_in`.
|
|
26
|
+
DEFAULT_EXPIRES_IN = 3600
|
|
27
|
+
|
|
28
|
+
# @param client_id [String] OAuth app client ID.
|
|
29
|
+
# @param client_secret [String] OAuth app client secret.
|
|
30
|
+
# @param token_url [String] Full token endpoint URL (host + {TOKEN_PATH}).
|
|
31
|
+
# @param scope [String] OAuth scope to request.
|
|
32
|
+
# @param adapter [Symbol] Faraday adapter.
|
|
33
|
+
def initialize(client_id:, client_secret:, token_url:, scope: DEFAULT_SCOPE,
|
|
34
|
+
adapter: Faraday.default_adapter)
|
|
35
|
+
@client_id = client_id
|
|
36
|
+
@client_secret = client_secret
|
|
37
|
+
@token_url = token_url
|
|
38
|
+
@scope = scope
|
|
39
|
+
@adapter = adapter
|
|
40
|
+
@mutex = Mutex.new
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [String] a currently-valid bearer token, fetching or refreshing
|
|
44
|
+
# one if the cached token is missing or near expiry.
|
|
45
|
+
def value
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
refresh! if expired?
|
|
48
|
+
@token
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def expired?
|
|
55
|
+
@token.nil? || Time.now >= @expires_at
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def refresh!
|
|
59
|
+
response = connection.post(@token_url) do |req|
|
|
60
|
+
req.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
|
61
|
+
req.body = URI.encode_www_form(
|
|
62
|
+
grant_type: "client_credentials",
|
|
63
|
+
client_id: @client_id,
|
|
64
|
+
client_secret: @client_secret,
|
|
65
|
+
scope: @scope
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
raise_token_error(response) unless response.success?
|
|
69
|
+
|
|
70
|
+
body = JSON.parse(response.body)
|
|
71
|
+
@token = body["access_token"]
|
|
72
|
+
expires_in = (body["expires_in"] || DEFAULT_EXPIRES_IN).to_i
|
|
73
|
+
@expires_at = Time.now + expires_in - EXPIRY_LEEWAY_SECONDS
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def connection
|
|
77
|
+
Faraday.new do |f|
|
|
78
|
+
f.adapter @adapter
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def raise_token_error(response)
|
|
83
|
+
body = begin
|
|
84
|
+
JSON.parse(response.body)
|
|
85
|
+
rescue StandardError
|
|
86
|
+
response.body
|
|
87
|
+
end
|
|
88
|
+
message = body.is_a?(Hash) ? (body["error_description"] || body["error"] || body.to_s) : body.to_s
|
|
89
|
+
raise AuthenticationError.new(
|
|
90
|
+
"BoldSign OAuth token request failed (#{response.status}): #{message}",
|
|
91
|
+
status: response.status, body: body, response: response
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/boldsign/client.rb
CHANGED
|
@@ -8,9 +8,21 @@ module Boldsign
|
|
|
8
8
|
# Low-level verb methods ({#get}, {#post}, {#put}, {#patch}, {#delete},
|
|
9
9
|
# {#download}) are also available for hitting any endpoint directly.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# Authenticates with either an API key (`X-API-KEY`) or OAuth 2.0
|
|
12
|
+
# (`Authorization: Bearer`). For OAuth, pass `client_id`/`client_secret` and
|
|
13
|
+
# the client fetches + caches a token via the client credentials grant; or
|
|
14
|
+
# pass a `access_token` you obtained yourself.
|
|
15
|
+
#
|
|
16
|
+
# @example API key
|
|
12
17
|
# client = Boldsign::Client.new(api_key: ENV["BOLDSIGN_API_KEY"], region: :us)
|
|
13
18
|
# client.documents.send_document(title: "NDA", signers: [...])
|
|
19
|
+
#
|
|
20
|
+
# @example OAuth client credentials
|
|
21
|
+
# client = Boldsign::Client.new(
|
|
22
|
+
# client_id: ENV["BOLDSIGN_CLIENT_ID"],
|
|
23
|
+
# client_secret: ENV["BOLDSIGN_CLIENT_SECRET"],
|
|
24
|
+
# region: :us
|
|
25
|
+
# )
|
|
14
26
|
class Client
|
|
15
27
|
# Default region (US).
|
|
16
28
|
DEFAULT_BASE_URL = "https://api.boldsign.com".freeze
|
|
@@ -18,25 +30,50 @@ module Boldsign
|
|
|
18
30
|
# User-Agent header value sent on every request.
|
|
19
31
|
USER_AGENT = "boldsign-ruby/#{Boldsign::VERSION}".freeze
|
|
20
32
|
|
|
21
|
-
# @return [String] the API key in use.
|
|
33
|
+
# @return [String, nil] the API key in use, when authenticating via API key.
|
|
22
34
|
attr_reader :api_key
|
|
23
35
|
|
|
36
|
+
# @return [Symbol] the resolved auth mode (`:api_key`, `:oauth`, or `:bearer`).
|
|
37
|
+
attr_reader :auth_mode
|
|
38
|
+
|
|
24
39
|
# @return [String] the resolved base URL (region or explicit override).
|
|
25
40
|
attr_reader :base_url
|
|
26
41
|
|
|
27
42
|
# @param api_key [String, nil] BoldSign API key. Falls back to `ENV["BOLDSIGN_API_KEY"]`.
|
|
28
|
-
# @param
|
|
29
|
-
# @param
|
|
43
|
+
# @param client_id [String, nil] OAuth app client ID (enables the client credentials grant).
|
|
44
|
+
# @param client_secret [String, nil] OAuth app client secret (required with `client_id`).
|
|
45
|
+
# @param access_token [String, nil] A pre-obtained OAuth bearer token (used as-is, not refreshed).
|
|
46
|
+
# @param scope [String, nil] OAuth scope to request (defaults to {AccessToken::DEFAULT_SCOPE}).
|
|
47
|
+
# @param token_url [String, nil] Override for the full OAuth token endpoint URL.
|
|
48
|
+
# @param region [Symbol, nil] One of `:us`, `:eu`, `:ca`, `:au`. Ignored if `base_url`/`token_url` is given.
|
|
49
|
+
# @param base_url [String, nil] Explicit API base URL override.
|
|
30
50
|
# @param adapter [Symbol] Faraday adapter (defaults to `Faraday.default_adapter`).
|
|
31
51
|
# @param logger [Logger, nil] Optional logger; when present, Faraday's logger middleware is enabled.
|
|
32
|
-
# @raise [ConfigurationError] when no
|
|
33
|
-
def initialize(api_key: nil,
|
|
52
|
+
# @raise [ConfigurationError] when no usable credentials are provided.
|
|
53
|
+
def initialize(api_key: nil, client_id: nil, client_secret: nil, access_token: nil,
|
|
54
|
+
scope: nil, token_url: nil, region: nil, base_url: nil,
|
|
55
|
+
adapter: Faraday.default_adapter, logger: nil)
|
|
34
56
|
@api_key = api_key || ENV["BOLDSIGN_API_KEY"]
|
|
35
|
-
|
|
36
|
-
|
|
57
|
+
@static_access_token = access_token
|
|
37
58
|
@base_url = base_url || Boldsign::REGIONS[region&.to_sym] || DEFAULT_BASE_URL
|
|
38
59
|
@adapter = adapter
|
|
39
60
|
@logger = logger
|
|
61
|
+
|
|
62
|
+
if present?(client_id) && present?(client_secret)
|
|
63
|
+
@auth_mode = :oauth
|
|
64
|
+
@access_token = AccessToken.new(
|
|
65
|
+
client_id: client_id, client_secret: client_secret,
|
|
66
|
+
token_url: token_url || default_token_url(region),
|
|
67
|
+
scope: scope || AccessToken::DEFAULT_SCOPE, adapter: adapter
|
|
68
|
+
)
|
|
69
|
+
elsif present?(@static_access_token)
|
|
70
|
+
@auth_mode = :bearer
|
|
71
|
+
elsif present?(@api_key)
|
|
72
|
+
@auth_mode = :api_key
|
|
73
|
+
else
|
|
74
|
+
raise ConfigurationError,
|
|
75
|
+
"Missing BoldSign credentials: provide client_id/client_secret, access_token, or api_key"
|
|
76
|
+
end
|
|
40
77
|
end
|
|
41
78
|
|
|
42
79
|
# @return [Resources::Brand]
|
|
@@ -135,9 +172,15 @@ module Boldsign
|
|
|
135
172
|
|
|
136
173
|
def connection(multipart: false)
|
|
137
174
|
Faraday.new(url: @base_url) do |f|
|
|
138
|
-
|
|
175
|
+
# flat_encode: true makes Faraday::Multipart::Middleware emit each
|
|
176
|
+
# Array element as its own part under the *same* field name (e.g. two
|
|
177
|
+
# `Signers` parts) instead of bracket-suffixing it (`Signers[]=`),
|
|
178
|
+
# which BoldSign's /v1/document/send endpoint rejects. See
|
|
179
|
+
# Resources::Document#encode_multipart_value, the caller that relies
|
|
180
|
+
# on this.
|
|
181
|
+
f.request :multipart, flat_encode: true if multipart
|
|
139
182
|
f.request :url_encoded
|
|
140
|
-
f.headers
|
|
183
|
+
apply_auth(f.headers)
|
|
141
184
|
f.headers["Accept"] = "application/json"
|
|
142
185
|
f.headers["User-Agent"] = USER_AGENT
|
|
143
186
|
f.response :logger, @logger if @logger
|
|
@@ -145,6 +188,27 @@ module Boldsign
|
|
|
145
188
|
end
|
|
146
189
|
end
|
|
147
190
|
|
|
191
|
+
def apply_auth(headers)
|
|
192
|
+
if @auth_mode == :api_key
|
|
193
|
+
headers["X-API-KEY"] = @api_key
|
|
194
|
+
else
|
|
195
|
+
headers["Authorization"] = "Bearer #{bearer_token}"
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def bearer_token
|
|
200
|
+
@auth_mode == :oauth ? @access_token.value : @static_access_token
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def present?(value)
|
|
204
|
+
!value.nil? && !value.empty?
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def default_token_url(region)
|
|
208
|
+
base = Boldsign::TOKEN_REGIONS[region&.to_sym] || Boldsign::DEFAULT_TOKEN_BASE_URL
|
|
209
|
+
"#{base}#{AccessToken::TOKEN_PATH}"
|
|
210
|
+
end
|
|
211
|
+
|
|
148
212
|
def parse_body(response)
|
|
149
213
|
return nil if response.body.nil? || response.body.empty?
|
|
150
214
|
content_type = response.headers["content-type"].to_s
|
|
@@ -74,13 +74,16 @@ module Boldsign
|
|
|
74
74
|
private
|
|
75
75
|
|
|
76
76
|
# BoldSign's POST /v1/document/send multipart endpoint is fussy about
|
|
77
|
-
# repeated/array-valued parts.
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
77
|
+
# repeated/array-valued parts. Multi-valued fields (e.g. two signers)
|
|
78
|
+
# must be sent as one multipart part per element, each keyed with the
|
|
79
|
+
# *same* field name and JSON-encoding just that one element — not a
|
|
80
|
+
# single field whose value is a JSON array, and not Faraday's default
|
|
81
|
+
# `Signers[]=` bracket-suffixed encoding (BoldSign rejects both). See
|
|
82
|
+
# `Client#connection`'s `flat_encode: true`, which is what makes
|
|
83
|
+
# `encode_multipart_value`'s Array output emit that way. Files must be
|
|
84
|
+
# sent as a single FilePart value for one upload; Faraday's automatic
|
|
85
|
+
# `Files[]` bracketing breaks the request the same way. Multi-file is
|
|
86
|
+
# not yet supported via this helper and will raise.
|
|
84
87
|
def multipart_send_body(body, files)
|
|
85
88
|
parts = body.each_with_object({}) do |(key, value), acc|
|
|
86
89
|
next if value.nil?
|
|
@@ -95,12 +98,15 @@ module Boldsign
|
|
|
95
98
|
parts
|
|
96
99
|
end
|
|
97
100
|
|
|
101
|
+
# Scalars pass through as-is. A Hash (or single-element Array, once
|
|
102
|
+
# `flat_encode` unwraps it to one part) becomes one JSON-object part.
|
|
103
|
+
# A multi-element Array becomes one JSON-object part *per element*,
|
|
104
|
+
# all sharing the same field name — see `multipart_send_body`'s comment.
|
|
98
105
|
def encode_multipart_value(value)
|
|
99
106
|
return value.to_s if scalar?(value)
|
|
100
|
-
return JSON.generate(Boldsign::CaseConvert.camelize(
|
|
101
|
-
raise NotImplementedError, "multi-element arrays are not supported in multipart bodies yet" if value.size > 1
|
|
107
|
+
return value.map { |v| JSON.generate(Boldsign::CaseConvert.camelize(v)) } if value.is_a?(Array)
|
|
102
108
|
|
|
103
|
-
JSON.generate(Boldsign::CaseConvert.camelize(value
|
|
109
|
+
JSON.generate(Boldsign::CaseConvert.camelize(value))
|
|
104
110
|
end
|
|
105
111
|
|
|
106
112
|
def file_part(file)
|
data/lib/boldsign/version.rb
CHANGED
data/lib/boldsign.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require "faraday"
|
|
2
2
|
require "faraday/multipart"
|
|
3
3
|
require "json"
|
|
4
|
+
require "uri"
|
|
4
5
|
|
|
5
6
|
require_relative "boldsign/version"
|
|
6
7
|
require_relative "boldsign/error"
|
|
7
8
|
require_relative "boldsign/case_convert"
|
|
9
|
+
require_relative "boldsign/access_token"
|
|
8
10
|
require_relative "boldsign/client"
|
|
9
11
|
require_relative "boldsign/resource"
|
|
10
12
|
require_relative "boldsign/resources/brand"
|
|
@@ -24,10 +26,11 @@ require_relative "boldsign/resources/user"
|
|
|
24
26
|
# Configure once at boot and access a shared {Client} via {Boldsign.client}, or
|
|
25
27
|
# instantiate {Client} directly when you need multiple credentials/regions.
|
|
26
28
|
#
|
|
27
|
-
# @example Configure and use the shared client
|
|
29
|
+
# @example Configure with OAuth and use the shared client
|
|
28
30
|
# Boldsign.configure do |c|
|
|
29
|
-
# c.
|
|
30
|
-
# c.
|
|
31
|
+
# c.client_id = ENV["BOLDSIGN_CLIENT_ID"]
|
|
32
|
+
# c.client_secret = ENV["BOLDSIGN_CLIENT_SECRET"]
|
|
33
|
+
# c.region = :us
|
|
31
34
|
# end
|
|
32
35
|
# Boldsign.client.documents.list
|
|
33
36
|
module Boldsign
|
|
@@ -39,10 +42,37 @@ module Boldsign
|
|
|
39
42
|
au: "https://api-au.boldsign.com"
|
|
40
43
|
}.freeze
|
|
41
44
|
|
|
45
|
+
# Region → account host map for the OAuth token endpoint. Non-US hosts follow
|
|
46
|
+
# BoldSign's regional naming; override with `token_url:` if a region differs.
|
|
47
|
+
TOKEN_REGIONS = {
|
|
48
|
+
us: "https://account.boldsign.com",
|
|
49
|
+
eu: "https://account-eu.boldsign.com",
|
|
50
|
+
ca: "https://account-ca.boldsign.com",
|
|
51
|
+
au: "https://account-au.boldsign.com"
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
54
|
+
# Default account host (US) for the OAuth token endpoint.
|
|
55
|
+
DEFAULT_TOKEN_BASE_URL = "https://account.boldsign.com".freeze
|
|
56
|
+
|
|
42
57
|
class << self
|
|
43
58
|
# @return [String, nil] API key used by the shared {client}.
|
|
44
59
|
attr_accessor :api_key
|
|
45
60
|
|
|
61
|
+
# @return [String, nil] OAuth app client ID used by the shared {client}.
|
|
62
|
+
attr_accessor :client_id
|
|
63
|
+
|
|
64
|
+
# @return [String, nil] OAuth app client secret used by the shared {client}.
|
|
65
|
+
attr_accessor :client_secret
|
|
66
|
+
|
|
67
|
+
# @return [String, nil] Pre-obtained OAuth bearer token (used as-is, not refreshed).
|
|
68
|
+
attr_accessor :access_token
|
|
69
|
+
|
|
70
|
+
# @return [String, nil] OAuth scope to request (defaults to {AccessToken::DEFAULT_SCOPE}).
|
|
71
|
+
attr_accessor :scope
|
|
72
|
+
|
|
73
|
+
# @return [String, nil] Override for the full OAuth token endpoint URL.
|
|
74
|
+
attr_accessor :token_url
|
|
75
|
+
|
|
46
76
|
# @return [Symbol, nil] Region key (`:us`, `:eu`, `:ca`, `:au`).
|
|
47
77
|
attr_accessor :region
|
|
48
78
|
|
|
@@ -58,7 +88,11 @@ module Boldsign
|
|
|
58
88
|
|
|
59
89
|
# @return [Client] memoized shared client built from module-level config.
|
|
60
90
|
def client
|
|
61
|
-
@client ||= Client.new(
|
|
91
|
+
@client ||= Client.new(
|
|
92
|
+
api_key: api_key, client_id: client_id, client_secret: client_secret,
|
|
93
|
+
access_token: access_token, scope: scope, token_url: token_url,
|
|
94
|
+
region: region, base_url: base_url
|
|
95
|
+
)
|
|
62
96
|
end
|
|
63
97
|
|
|
64
98
|
# Clears the memoized shared client so the next call to {client} rebuilds it.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: boldsign
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Klein
|
|
@@ -92,6 +92,7 @@ files:
|
|
|
92
92
|
- README.md
|
|
93
93
|
- boldsign.gemspec
|
|
94
94
|
- lib/boldsign.rb
|
|
95
|
+
- lib/boldsign/access_token.rb
|
|
95
96
|
- lib/boldsign/case_convert.rb
|
|
96
97
|
- lib/boldsign/client.rb
|
|
97
98
|
- lib/boldsign/error.rb
|
|
@@ -108,14 +109,14 @@ files:
|
|
|
108
109
|
- lib/boldsign/resources/template.rb
|
|
109
110
|
- lib/boldsign/resources/user.rb
|
|
110
111
|
- lib/boldsign/version.rb
|
|
111
|
-
homepage: https://github.com/
|
|
112
|
+
homepage: https://github.com/EscrowSafe/boldsign-ruby
|
|
112
113
|
licenses:
|
|
113
114
|
- MIT
|
|
114
115
|
metadata:
|
|
115
|
-
homepage_uri: https://github.com/
|
|
116
|
-
source_code_uri: https://github.com/
|
|
117
|
-
changelog_uri: https://github.com/
|
|
118
|
-
bug_tracker_uri: https://github.com/
|
|
116
|
+
homepage_uri: https://github.com/EscrowSafe/boldsign-ruby
|
|
117
|
+
source_code_uri: https://github.com/EscrowSafe/boldsign-ruby
|
|
118
|
+
changelog_uri: https://github.com/EscrowSafe/boldsign-ruby/blob/main/CHANGELOG.md
|
|
119
|
+
bug_tracker_uri: https://github.com/EscrowSafe/boldsign-ruby/issues
|
|
119
120
|
documentation_uri: https://rubydoc.info/gems/boldsign
|
|
120
121
|
rubygems_mfa_required: 'true'
|
|
121
122
|
rdoc_options: []
|