assinafy 1.5.1 → 1.5.2
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 +19 -0
- data/README.md +4 -1
- data/docs/API_REFERENCE.md +17 -0
- data/lib/assinafy/client.rb +8 -4
- data/lib/assinafy/configuration.rb +29 -5
- data/lib/assinafy/resources/assignment_resource.rb +23 -0
- data/lib/assinafy/resources/auth_resource.rb +5 -5
- data/lib/assinafy/version.rb +1 -1
- data/sig/assinafy.rbs +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37dc8933996b9192a9f12896d772d4d5f745169fcaf0bb13b06cd199e4650bcd
|
|
4
|
+
data.tar.gz: 70fe0b92c876f387aeb3656f18b0e78d3cbc09d41dbefc73bba96bb6177b423b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5bd2d4cfc525e22c84d4de1cc232f4666b769bcdd66e21d56397ad1c4a61bc1c482a7a9b183dcadfde1bd098e624ada2776f847e2c55fcf65e43d20f89437d34
|
|
7
|
+
data.tar.gz: 4586c42fc917ef30c6e7eef7718310fd583ce2817acecdf2de5f3f972b405e8986c9a474aa5dbdd9f3c476b2651a700c641471c8154d710e85f2ba4c7c7ebbc1
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `assinafy` Ruby gem are documented here.
|
|
4
4
|
|
|
5
|
+
## 1.5.2
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `base_url` must now be an absolute `http`/`https` URL with a host. Other schemes, scheme-less
|
|
10
|
+
hosts, and relative paths raise `Assinafy::ValidationError` at construction instead of sending
|
|
11
|
+
credentials to them or surfacing a raw `URI::InvalidURIError`.
|
|
12
|
+
- `AssignmentResource.build_payload` now rejects a non-String `message` or `expires_at` and a
|
|
13
|
+
`copy_receivers` value that is not an array of non-empty signer IDs, instead of forwarding them
|
|
14
|
+
to the API verbatim.
|
|
15
|
+
- `Client#upload_and_request_signatures` validates the whole assignment body before uploading, so
|
|
16
|
+
malformed `message`, `expires_at`, or `copy_receivers` values no longer leave an uploaded
|
|
17
|
+
document and freshly created signers behind.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Authentication and API-key operations route through the shared resource request helpers used by
|
|
22
|
+
every other resource.
|
|
23
|
+
|
|
5
24
|
## 1.5.1
|
|
6
25
|
|
|
7
26
|
### Added
|
data/README.md
CHANGED
|
@@ -84,9 +84,12 @@ client = Assinafy::Client.new(
|
|
|
84
84
|
- `token:` sends `Authorization: Bearer ...` (legacy session token).
|
|
85
85
|
- Configure exactly one credential. If both are supplied, the SDK sends only `X-Api-Key`.
|
|
86
86
|
- A client can also be created with no credentials for authentication and public/signer endpoints.
|
|
87
|
+
- `base_url:` must be an absolute `http`/`https` URL. Anything else — a scheme-less host, a relative path, or
|
|
88
|
+
another scheme — raises `Assinafy::ValidationError` instead of attaching your credentials to it. A trailing
|
|
89
|
+
slash is stripped.
|
|
87
90
|
- Account-scoped methods document a per-call account override for multi-workspace tenants.
|
|
88
91
|
- Provide a `Logger`-compatible `logger:` to observe upload/assignment/webhook lifecycle messages.
|
|
89
|
-
- Requests send `User-Agent: Assinafy-Ruby-SDK/
|
|
92
|
+
- Requests send `User-Agent: Assinafy-Ruby-SDK/v<Assinafy::VERSION>`; the suffix always follows the gem version.
|
|
90
93
|
|
|
91
94
|
`Client.from_config(hash)` accepts string- or symbol-keyed hashes (e.g. parsed YAML).
|
|
92
95
|
|
data/docs/API_REFERENCE.md
CHANGED
|
@@ -168,6 +168,23 @@ environment before making these operations part of a critical workflow.
|
|
|
168
168
|
| DELETE | `/v1/accounts/{account_id}/templates/{template_id}` | `TemplateResource#delete` | Bearer token or `X-Api-Key` | No request body; returns `nil` on success. |
|
|
169
169
|
| GET | `/v1/accounts/{account_id}/templates/{template_id}/pages/{page_id}/download` | `TemplateResource#download_page` | Bearer token or `X-Api-Key` | No request body; returns binary image bytes. |
|
|
170
170
|
|
|
171
|
+
### Assignment listing
|
|
172
|
+
|
|
173
|
+
`GET /v1/assignments` needs an account context that the machine contract does not list. The SDK supplies it as
|
|
174
|
+
the camelCase `accountId` query parameter (`AssignmentResource#list`), taken from the client default or the
|
|
175
|
+
per-call override.
|
|
176
|
+
|
|
177
|
+
### Assignment optional fields
|
|
178
|
+
|
|
179
|
+
`message` and `expires_at` must be strings, and `copy_receivers` must be an array of non-empty signer IDs.
|
|
180
|
+
`AssignmentResource.build_payload` rejects other shapes locally, so `Client#upload_and_request_signatures`
|
|
181
|
+
fails before it uploads a document or creates signers.
|
|
182
|
+
|
|
183
|
+
### Base URL
|
|
184
|
+
|
|
185
|
+
`base_url` must be an absolute `http`/`https` URL with a host. Other schemes, scheme-less hosts, and relative
|
|
186
|
+
paths raise `Assinafy::ValidationError` at construction rather than sending credentials to them.
|
|
187
|
+
|
|
171
188
|
### Document tags
|
|
172
189
|
|
|
173
190
|
`DocumentResource#replace_tags` and `#append_tags` accept arrays of tag IDs. The deployed sandbox also accepts
|
data/lib/assinafy/client.rb
CHANGED
|
@@ -193,6 +193,13 @@ module Assinafy
|
|
|
193
193
|
copy_receivers: nil, account_id: nil)
|
|
194
194
|
validate_signature_workflow!(signers, wait_for_ready)
|
|
195
195
|
|
|
196
|
+
# Placeholder IDs stand in for the signers created below, so the whole
|
|
197
|
+
# assignment body is validated before anything is uploaded or created.
|
|
198
|
+
assignment_payload = { method: 'virtual', signers: Array.new(signers.length, 'preflight'),
|
|
199
|
+
message: message, expires_at: expires_at,
|
|
200
|
+
copy_receivers: copy_receivers }
|
|
201
|
+
Resources::AssignmentResource.build_payload(assignment_payload)
|
|
202
|
+
|
|
196
203
|
@logger.info("Starting upload and signature workflow for #{signers.length} signer(s)")
|
|
197
204
|
|
|
198
205
|
upload_opts = account_id.nil? ? {} : { account_id: account_id }
|
|
@@ -205,10 +212,7 @@ module Assinafy
|
|
|
205
212
|
signer_ids << signer_id!(@signers.create(signer, account_id))
|
|
206
213
|
end
|
|
207
214
|
|
|
208
|
-
|
|
209
|
-
message: message, expires_at: expires_at,
|
|
210
|
-
copy_receivers: copy_receivers }
|
|
211
|
-
assignment = @assignments.create(document['id'], assignment_payload)
|
|
215
|
+
assignment = @assignments.create(document['id'], assignment_payload.merge(signers: signer_ids))
|
|
212
216
|
created_resource_id!(assignment, 'Assignment')
|
|
213
217
|
|
|
214
218
|
@logger.info("Upload and signature workflow completed for document #{document['id']}")
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
3
5
|
require_relative 'errors'
|
|
4
6
|
|
|
5
7
|
module Assinafy
|
|
@@ -24,6 +26,9 @@ module Assinafy
|
|
|
24
26
|
DEFAULT_BASE_URL = 'https://api.assinafy.com.br/v1'
|
|
25
27
|
# Default Faraday open/read timeout, in seconds.
|
|
26
28
|
DEFAULT_TIMEOUT = 30
|
|
29
|
+
# Schemes accepted for {#base_url}. Credentials are attached to every request
|
|
30
|
+
# sent to this host, so anything that is not absolute HTTP(S) is rejected.
|
|
31
|
+
BASE_URL_SCHEMES = %w[http https].freeze
|
|
27
32
|
|
|
28
33
|
# @!attribute [rw] api_key
|
|
29
34
|
# @return [String, nil] sent as `X-Api-Key`
|
|
@@ -65,17 +70,17 @@ module Assinafy
|
|
|
65
70
|
# @example Trailing slash on base_url is stripped
|
|
66
71
|
# Assinafy::Configuration.new(base_url: 'https://api.assinafy.com.br/v1/').base_url
|
|
67
72
|
# # => "https://api.assinafy.com.br/v1"
|
|
73
|
+
#
|
|
74
|
+
# @example A base_url that is not an absolute http(s) URL is rejected
|
|
75
|
+
# Assinafy::Configuration.new(base_url: 'api.assinafy.com.br/v1')
|
|
76
|
+
# # raises Assinafy::ValidationError ("Base URL must be an absolute http(s) URL")
|
|
68
77
|
def initialize(api_key: nil, token: nil, account_id: nil,
|
|
69
78
|
base_url: DEFAULT_BASE_URL, webhook_secret: nil,
|
|
70
79
|
timeout: DEFAULT_TIMEOUT, logger: nil)
|
|
71
80
|
@api_key = api_key
|
|
72
81
|
@token = token
|
|
73
82
|
@account_id = account_id
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@base_url = base_url.strip.sub(%r{/+\z}, '')
|
|
77
|
-
raise ValidationError.new('Base URL is required') if @base_url.empty?
|
|
78
|
-
|
|
83
|
+
@base_url = normalize_base_url(base_url)
|
|
79
84
|
@webhook_secret = webhook_secret
|
|
80
85
|
@timeout = normalize_timeout(timeout)
|
|
81
86
|
@logger = logger
|
|
@@ -146,6 +151,25 @@ module Assinafy
|
|
|
146
151
|
|
|
147
152
|
private
|
|
148
153
|
|
|
154
|
+
# Strip the trailing slash and require an absolute http(s) URL with a host.
|
|
155
|
+
# Without this the SDK would happily attach `X-Api-Key` to an `ftp:` or
|
|
156
|
+
# scheme-less base, or surface a raw URI::InvalidURIError from Faraday.
|
|
157
|
+
def normalize_base_url(value)
|
|
158
|
+
raise ValidationError.new('Base URL is required') unless value.is_a?(String)
|
|
159
|
+
|
|
160
|
+
url = value.strip.sub(%r{/+\z}, '')
|
|
161
|
+
raise ValidationError.new('Base URL is required') if url.empty?
|
|
162
|
+
|
|
163
|
+
uri = URI.parse(url)
|
|
164
|
+
unless BASE_URL_SCHEMES.include?(uri.scheme) && !uri.host.to_s.empty?
|
|
165
|
+
raise ValidationError.new('Base URL must be an absolute http(s) URL', { base_url: value })
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
url
|
|
169
|
+
rescue URI::InvalidURIError => e
|
|
170
|
+
raise ValidationError.new("Base URL is not a valid URL: #{e.message}", { base_url: value })
|
|
171
|
+
end
|
|
172
|
+
|
|
149
173
|
def normalize_timeout(value)
|
|
150
174
|
raw = value.nil? ? DEFAULT_TIMEOUT : value
|
|
151
175
|
seconds = Integer(raw, exception: false) if raw.is_a?(Integer) || raw.is_a?(String)
|
|
@@ -79,6 +79,7 @@ module Assinafy
|
|
|
79
79
|
method = (p[:method] || 'virtual').to_s
|
|
80
80
|
|
|
81
81
|
validate_method!(method, signers, entries, p)
|
|
82
|
+
validate_optional_fields!(p)
|
|
82
83
|
|
|
83
84
|
result = { method: method }
|
|
84
85
|
result[:signers] = signers.map { |ref| normalise_signer_ref(ref, options) } unless signers.empty?
|
|
@@ -106,6 +107,28 @@ module Assinafy
|
|
|
106
107
|
raise ValidationError.new('entries are required for collect assignments')
|
|
107
108
|
end
|
|
108
109
|
|
|
110
|
+
# `message`/`expires_at`/`copy_receivers` are forwarded verbatim, so a
|
|
111
|
+
# wrong type here would only fail server-side — after the caller has
|
|
112
|
+
# already uploaded a document and created signers.
|
|
113
|
+
def validate_optional_fields!(payload)
|
|
114
|
+
%i[message expires_at].each do |key|
|
|
115
|
+
value = payload[key]
|
|
116
|
+
next if value.nil? || value.is_a?(String)
|
|
117
|
+
|
|
118
|
+
raise ValidationError.new("#{key} must be a String", { key => value })
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
receivers = payload[:copy_receivers]
|
|
122
|
+
return if receivers.nil?
|
|
123
|
+
|
|
124
|
+
unless receivers.is_a?(Array) && receivers.all? { |id| id.is_a?(String) && !id.strip.empty? }
|
|
125
|
+
raise ValidationError.new(
|
|
126
|
+
'copy_receivers must be an Array of signer IDs',
|
|
127
|
+
{ copy_receivers: receivers }
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
109
132
|
def extract_signer_refs(payload)
|
|
110
133
|
return payload[:signers] if payload[:signers].is_a?(Array) && !payload[:signers].empty?
|
|
111
134
|
|
|
@@ -98,7 +98,7 @@ module Assinafy
|
|
|
98
98
|
# # => nil
|
|
99
99
|
def link_social_login(provider:, token:)
|
|
100
100
|
call('Failed to link social login') do
|
|
101
|
-
|
|
101
|
+
http_post('auth/link-social-login', body_params(provider: provider, token: token))
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
|
|
@@ -122,7 +122,7 @@ module Assinafy
|
|
|
122
122
|
# # { "api_key" => "api-key-created-once" }
|
|
123
123
|
def create_api_key(password:)
|
|
124
124
|
call('Failed to create API key') do
|
|
125
|
-
|
|
125
|
+
http_post('users/api-keys', body_params(password: password))
|
|
126
126
|
end
|
|
127
127
|
end
|
|
128
128
|
|
|
@@ -146,7 +146,7 @@ module Assinafy
|
|
|
146
146
|
# resource.get_api_key # => nil
|
|
147
147
|
def get_api_key
|
|
148
148
|
call('Failed to get API key') do
|
|
149
|
-
|
|
149
|
+
http_get('users/api-keys')
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
152
|
|
|
@@ -168,7 +168,7 @@ module Assinafy
|
|
|
168
168
|
# # => nil
|
|
169
169
|
def delete_api_key
|
|
170
170
|
call_void('Failed to delete API key') do
|
|
171
|
-
|
|
171
|
+
http_delete('users/api-keys')
|
|
172
172
|
end
|
|
173
173
|
end
|
|
174
174
|
|
|
@@ -192,7 +192,7 @@ module Assinafy
|
|
|
192
192
|
# # { "email" => "user@example.com" }
|
|
193
193
|
def change_password(email:, password:, new_password:)
|
|
194
194
|
call('Failed to change password') do
|
|
195
|
-
|
|
195
|
+
http_put(
|
|
196
196
|
'authentication/change-password',
|
|
197
197
|
body_params(email: email, password: password, new_password: new_password)
|
|
198
198
|
)
|
data/lib/assinafy/version.rb
CHANGED
data/sig/assinafy.rbs
CHANGED