assinafy 1.5.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 +7 -0
- data/CHANGELOG.md +168 -0
- data/LICENSE +19 -0
- data/README.md +463 -0
- data/lib/assinafy/client.rb +245 -0
- data/lib/assinafy/configuration.rb +157 -0
- data/lib/assinafy/errors.rb +83 -0
- data/lib/assinafy/null_logger.rb +9 -0
- data/lib/assinafy/resources/account_resource.rb +267 -0
- data/lib/assinafy/resources/assignment_resource.rb +476 -0
- data/lib/assinafy/resources/auth_resource.rb +251 -0
- data/lib/assinafy/resources/base_resource.rb +250 -0
- data/lib/assinafy/resources/document_resource.rb +912 -0
- data/lib/assinafy/resources/field_resource.rb +274 -0
- data/lib/assinafy/resources/signer_document_resource.rb +222 -0
- data/lib/assinafy/resources/signer_resource.rb +422 -0
- data/lib/assinafy/resources/tag_resource.rb +183 -0
- data/lib/assinafy/resources/template_resource.rb +215 -0
- data/lib/assinafy/resources/user_resource.rb +182 -0
- data/lib/assinafy/resources/webhook_resource.rb +222 -0
- data/lib/assinafy/support/webhook_verifier.rb +129 -0
- data/lib/assinafy/utils.rb +122 -0
- data/lib/assinafy/version.rb +5 -0
- data/lib/assinafy.rb +25 -0
- metadata +195 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assinafy
|
|
4
|
+
module Resources
|
|
5
|
+
# Field definitions (reusable input fields) and per-value validation.
|
|
6
|
+
#
|
|
7
|
+
# See https://api.assinafy.com.br/v1/docs#field-definition for the
|
|
8
|
+
# documentation of these endpoints.
|
|
9
|
+
class FieldResource < BaseResource
|
|
10
|
+
# Create a field definition.
|
|
11
|
+
#
|
|
12
|
+
# @param payload [Hash]
|
|
13
|
+
# @option payload [String] :type required — e.g. `text`, `cpf`, `email` — see {#types}
|
|
14
|
+
# @option payload [String] :name required — display label
|
|
15
|
+
# @option payload [String] :regex optional validation regex (text fields)
|
|
16
|
+
# @option payload [Boolean] :is_required default `true`
|
|
17
|
+
# @param account_id_override [String, nil]
|
|
18
|
+
# @return [Hash] the created field definition (envelope `data` unwrapped)
|
|
19
|
+
# @see POST /accounts/{accountId}/fields
|
|
20
|
+
# @example Create a text field
|
|
21
|
+
# field = client.fields.create(type: 'text', name: 'audit-field-1780694479')
|
|
22
|
+
#
|
|
23
|
+
# # Request body the SDK sends:
|
|
24
|
+
# # { "type": "text", "name": "audit-field-1780694479" }
|
|
25
|
+
#
|
|
26
|
+
# # => {
|
|
27
|
+
# # "resource" => "field_definition",
|
|
28
|
+
# # "id" => "1032009e858cc1f859ccf3a61229",
|
|
29
|
+
# # "name" => "audit-field-1780694479",
|
|
30
|
+
# # "type" => "text",
|
|
31
|
+
# # "regex" => nil,
|
|
32
|
+
# # "is_pre_defined" => false,
|
|
33
|
+
# # "is_active" => true,
|
|
34
|
+
# # "is_required" => true,
|
|
35
|
+
# # "is_standard" => false,
|
|
36
|
+
# # "is_read_only" => false,
|
|
37
|
+
# # "is_visible" => true
|
|
38
|
+
# # }
|
|
39
|
+
def create(payload, account_id_override = nil)
|
|
40
|
+
acc_id = account_id(account_id_override)
|
|
41
|
+
body = body_params(require_payload(payload))
|
|
42
|
+
|
|
43
|
+
call('Failed to create field definition') do
|
|
44
|
+
http_post("accounts/#{acc_id}/fields", body)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# List field definitions.
|
|
49
|
+
#
|
|
50
|
+
# NOTE: this endpoint does not send pagination headers, so `meta` is always
|
|
51
|
+
# `nil` here (unlike other list endpoints which return a pagination Hash).
|
|
52
|
+
#
|
|
53
|
+
# @param params [Hash] `include_inactive`, `include_standard`
|
|
54
|
+
# @param account_id_override [String, nil]
|
|
55
|
+
# @return [Hash{Symbol=>Array,nil}] `{ data: [...], meta: nil }`
|
|
56
|
+
# @see GET /accounts/{accountId}/fields
|
|
57
|
+
# @example List including inactive fields
|
|
58
|
+
# result = client.fields.list(include_inactive: true)
|
|
59
|
+
#
|
|
60
|
+
# # => {
|
|
61
|
+
# # data: [
|
|
62
|
+
# # {
|
|
63
|
+
# # "id" => "field-id",
|
|
64
|
+
# # "name" => "Nome",
|
|
65
|
+
# # "type" => "personName",
|
|
66
|
+
# # "regex" => nil,
|
|
67
|
+
# # "is_pre_defined" => true,
|
|
68
|
+
# # "is_active" => true,
|
|
69
|
+
# # "is_required" => false,
|
|
70
|
+
# # "is_standard" => false,
|
|
71
|
+
# # "is_read_only" => false,
|
|
72
|
+
# # "is_visible" => true
|
|
73
|
+
# # }
|
|
74
|
+
# # # ... (one Hash per field definition)
|
|
75
|
+
# # ],
|
|
76
|
+
# # meta: nil # this endpoint sends no pagination headers
|
|
77
|
+
# # }
|
|
78
|
+
def list(params = {}, account_id_override = nil)
|
|
79
|
+
acc_id = account_id(account_id_override)
|
|
80
|
+
|
|
81
|
+
call_list('Failed to list field definitions') do
|
|
82
|
+
http_get("accounts/#{acc_id}/fields", params)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Fetch a field definition by ID.
|
|
87
|
+
#
|
|
88
|
+
# @param field_id [String]
|
|
89
|
+
# @param account_id_override [String, nil]
|
|
90
|
+
# @return [Hash] the field definition (envelope `data` unwrapped)
|
|
91
|
+
# @see GET /accounts/{accountId}/fields/{field_id}
|
|
92
|
+
# @example Fetch one field definition
|
|
93
|
+
# field = client.fields.get('1032009e858cc1f859ccf3a61229')
|
|
94
|
+
#
|
|
95
|
+
# # => {
|
|
96
|
+
# # "resource" => "field_definition",
|
|
97
|
+
# # "id" => "1032009e858cc1f859ccf3a61229",
|
|
98
|
+
# # "name" => "audit-field-1780694479",
|
|
99
|
+
# # "type" => "text",
|
|
100
|
+
# # "regex" => nil,
|
|
101
|
+
# # "is_pre_defined" => false,
|
|
102
|
+
# # "is_active" => true,
|
|
103
|
+
# # "is_required" => true,
|
|
104
|
+
# # "is_standard" => false,
|
|
105
|
+
# # "is_read_only" => false,
|
|
106
|
+
# # "is_visible" => true
|
|
107
|
+
# # }
|
|
108
|
+
def get(field_id, account_id_override = nil)
|
|
109
|
+
acc_id = account_id(account_id_override)
|
|
110
|
+
fid = require_id(field_id, 'Field ID')
|
|
111
|
+
|
|
112
|
+
call('Failed to fetch field definition') do
|
|
113
|
+
http_get("accounts/#{acc_id}/fields/#{fid}")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Update a field definition. The update endpoint accepts only `name`,
|
|
118
|
+
# `regex`, and `is_active` (unlike {#create}, it does not accept `type`
|
|
119
|
+
# or `is_required`).
|
|
120
|
+
#
|
|
121
|
+
# @param field_id [String]
|
|
122
|
+
# @param payload [Hash]
|
|
123
|
+
# @option payload [String] :name display label
|
|
124
|
+
# @option payload [String] :regex validation regex (text fields)
|
|
125
|
+
# @option payload [Boolean] :is_active enable/disable the field
|
|
126
|
+
# @param account_id_override [String, nil]
|
|
127
|
+
# @return [Hash] the updated field definition (envelope `data` unwrapped)
|
|
128
|
+
# @see PUT /accounts/{account_id}/fields/{field_id}
|
|
129
|
+
# @example Rename a field definition
|
|
130
|
+
# field = client.fields.update('1032009e858cc1f859ccf3a61229', name: 'New Field Name')
|
|
131
|
+
#
|
|
132
|
+
# # Request body the SDK sends:
|
|
133
|
+
# # { "name": "New Field Name" }
|
|
134
|
+
#
|
|
135
|
+
# # => {
|
|
136
|
+
# # "resource" => "field_definition",
|
|
137
|
+
# # "id" => "1032009e858cc1f859ccf3a61229",
|
|
138
|
+
# # "name" => "New Field Name",
|
|
139
|
+
# # "type" => "text",
|
|
140
|
+
# # "regex" => nil,
|
|
141
|
+
# # "is_pre_defined" => false,
|
|
142
|
+
# # "is_active" => true,
|
|
143
|
+
# # "is_required" => true,
|
|
144
|
+
# # "is_standard" => false,
|
|
145
|
+
# # "is_read_only" => false,
|
|
146
|
+
# # "is_visible" => true
|
|
147
|
+
# # }
|
|
148
|
+
def update(field_id, payload, account_id_override = nil)
|
|
149
|
+
acc_id = account_id(account_id_override)
|
|
150
|
+
fid = require_id(field_id, 'Field ID')
|
|
151
|
+
raw = require_payload(payload)
|
|
152
|
+
body = body_params(raw)
|
|
153
|
+
body['regex'] = nil if (raw.key?(:regex) && raw[:regex].nil?) ||
|
|
154
|
+
(raw.key?('regex') && raw['regex'].nil?)
|
|
155
|
+
|
|
156
|
+
call('Failed to update field definition') do
|
|
157
|
+
http_put("accounts/#{acc_id}/fields/#{fid}", body)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Delete a field definition. Fields used by any document cannot be deleted.
|
|
162
|
+
#
|
|
163
|
+
# @param field_id [String]
|
|
164
|
+
# @param account_id_override [String, nil]
|
|
165
|
+
# @return [nil] the API returns `data: []`; the SDK normalizes this to `nil`
|
|
166
|
+
# @see DELETE /accounts/{account_id}/fields/{field_id}
|
|
167
|
+
# @example Delete a field definition
|
|
168
|
+
# client.fields.delete('1032009e858cc1f859ccf3a61229')
|
|
169
|
+
# # => nil
|
|
170
|
+
def delete(field_id, account_id_override = nil)
|
|
171
|
+
acc_id = account_id(account_id_override)
|
|
172
|
+
fid = require_id(field_id, 'Field ID')
|
|
173
|
+
|
|
174
|
+
call_void('Failed to delete field definition') do
|
|
175
|
+
http_delete("accounts/#{acc_id}/fields/#{fid}")
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Validate a single value against a field definition.
|
|
180
|
+
#
|
|
181
|
+
# Either workspace Authorization or signer-access-code authentication
|
|
182
|
+
# works; pass `signer_access_code:` for the latter.
|
|
183
|
+
#
|
|
184
|
+
# @param field_id [String]
|
|
185
|
+
# @param value [Object]
|
|
186
|
+
# @param account_id_override [String, nil]
|
|
187
|
+
# @param signer_access_code [String, nil]
|
|
188
|
+
# @return [Hash{String=>Object}] `{ "type" =>, "success" =>, "error_message" => }`
|
|
189
|
+
# @see POST /accounts/{accountId}/fields/{field_id}/validate
|
|
190
|
+
# @example Validate a value (workspace auth)
|
|
191
|
+
# result = client.fields.validate('1032009e858cc1f859ccf3a61229', 'Some text')
|
|
192
|
+
#
|
|
193
|
+
# # Request body the SDK sends:
|
|
194
|
+
# # { "value": "Some text" }
|
|
195
|
+
#
|
|
196
|
+
# # => { "type" => "text", "success" => true, "error_message" => "" }
|
|
197
|
+
#
|
|
198
|
+
# @example Validate as a signer (signer-access-code auth, sent as a query param)
|
|
199
|
+
# client.fields.validate('field-id', 'Some text', signer_access_code: 'signer-access-code')
|
|
200
|
+
# # => { "type" => "text", "success" => true, "error_message" => "" }
|
|
201
|
+
def validate(field_id, value, account_id_override = nil, signer_access_code: nil)
|
|
202
|
+
acc_id = account_id(account_id_override)
|
|
203
|
+
fid = require_id(field_id, 'Field ID')
|
|
204
|
+
|
|
205
|
+
call('Failed to validate field value') do
|
|
206
|
+
http_post("accounts/#{acc_id}/fields/#{fid}/validate", body_params(value: value),
|
|
207
|
+
signer_access_code: signer_access_code)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Validate many `{ field_id:, value: }` pairs in a single call.
|
|
212
|
+
#
|
|
213
|
+
# @param values [Array<Hash>]
|
|
214
|
+
# @param account_id_override [String, nil]
|
|
215
|
+
# @param signer_access_code [String, nil]
|
|
216
|
+
# @return [Array<Hash>] one validation Hash per input, each carrying its `field_id`
|
|
217
|
+
# @see POST /accounts/{accountId}/fields/validate-multiple
|
|
218
|
+
# @example Validate several values at once
|
|
219
|
+
# results = client.fields.validate_multiple([
|
|
220
|
+
# { field_id: '63488ffb7adf435aba319787', value: '1111111111111' },
|
|
221
|
+
# { field_id: '63488ffb0461cebb70775497', value: 'user@example.com' }
|
|
222
|
+
# ])
|
|
223
|
+
#
|
|
224
|
+
# # Request body the SDK sends (an array, not an object):
|
|
225
|
+
# # [
|
|
226
|
+
# # { "field_id": "63488ffb7adf435aba319787", "value": "1111111111111" },
|
|
227
|
+
# # { "field_id": "63488ffb0461cebb70775497", "value": "user@example.com" }
|
|
228
|
+
# # ]
|
|
229
|
+
#
|
|
230
|
+
# # => [
|
|
231
|
+
# # { "field_id" => "63488ffb7adf435aba319787", "type" => "cpf",
|
|
232
|
+
# # "success" => false, "error_message" => "Invalid CPF." },
|
|
233
|
+
# # { "field_id" => "63488ffb0461cebb70775497", "type" => "email",
|
|
234
|
+
# # "success" => true, "error_message" => "" }
|
|
235
|
+
# # ]
|
|
236
|
+
def validate_multiple(values, account_id_override = nil, signer_access_code: nil)
|
|
237
|
+
acc_id = account_id(account_id_override)
|
|
238
|
+
list = require_array(values, 'Field values')
|
|
239
|
+
|
|
240
|
+
call('Failed to validate field values') do
|
|
241
|
+
http_post("accounts/#{acc_id}/fields/validate-multiple",
|
|
242
|
+
list.map { |item| item.is_a?(Hash) ? body_params(item) : item },
|
|
243
|
+
signer_access_code: signer_access_code)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# List the catalog of supported field types.
|
|
248
|
+
#
|
|
249
|
+
# @return [Array<Hash{String=>String}>] each entry is `{ "type" =>, "name" => }`
|
|
250
|
+
# @see GET /field-types
|
|
251
|
+
# @example List supported field types
|
|
252
|
+
# types = client.fields.types
|
|
253
|
+
#
|
|
254
|
+
# # => [
|
|
255
|
+
# # { "type" => "personName", "name" => "Nome" },
|
|
256
|
+
# # { "type" => "cpf", "name" => "CPF" },
|
|
257
|
+
# # { "type" => "phoneNumber", "name" => "Número de Telefone" },
|
|
258
|
+
# # { "type" => "postalCode", "name" => "CEP" },
|
|
259
|
+
# # { "type" => "email", "name" => "E-mail" },
|
|
260
|
+
# # { "type" => "cnpj", "name" => "CNPJ" },
|
|
261
|
+
# # { "type" => "companyName", "name" => "Nome da empresa" },
|
|
262
|
+
# # { "type" => "email", "name" => "E-mail" }, # the live catalog lists "email" twice
|
|
263
|
+
# # { "type" => "text", "name" => "Texto" },
|
|
264
|
+
# # { "type" => "number", "name" => "Número" },
|
|
265
|
+
# # { "type" => "date", "name" => "Data" }
|
|
266
|
+
# # ]
|
|
267
|
+
def types
|
|
268
|
+
call('Failed to list field types') do
|
|
269
|
+
http_get('field-types')
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assinafy
|
|
4
|
+
module Resources
|
|
5
|
+
# Signer-authenticated views over a signer's assigned documents.
|
|
6
|
+
#
|
|
7
|
+
# Authentication varies by endpoint (verified against the live API):
|
|
8
|
+
#
|
|
9
|
+
# - {#current}/{#document}, {#sign_multiple}, {#decline_multiple} require the
|
|
10
|
+
# `signer-access-code` URL parameter.
|
|
11
|
+
# - {#list} accepts EITHER the workspace `X-Api-Key` header OR the access
|
|
12
|
+
# code; pass `signer_access_code:` only when authenticating as the signer.
|
|
13
|
+
# - {#download} only needs the document/artifact IDs.
|
|
14
|
+
#
|
|
15
|
+
# See https://api.assinafy.com.br/v1/docs#signer for the full
|
|
16
|
+
# documentation of these endpoints.
|
|
17
|
+
class SignerDocumentResource < BaseResource
|
|
18
|
+
# Fetch the signer's "current" document (the one referenced by the access code).
|
|
19
|
+
#
|
|
20
|
+
# Resolves the document, assignment, and current signer directly from the
|
|
21
|
+
# access code; it does not require code verification or data confirmation.
|
|
22
|
+
# The shape mirrors the signing endpoints, and `assignment.items` is
|
|
23
|
+
# filtered to only the current signer's items.
|
|
24
|
+
#
|
|
25
|
+
# @param signer_id [String]
|
|
26
|
+
# @param signer_access_code [String]
|
|
27
|
+
# @return [Hash] the document (envelope `data` unwrapped)
|
|
28
|
+
# @see GET /signers/{signer_id}/document
|
|
29
|
+
# @example Fetch the document tied to an access code
|
|
30
|
+
# doc = client.signer_documents.current('signer-id', signer_access_code: 'signer-access-code')
|
|
31
|
+
#
|
|
32
|
+
# # => {
|
|
33
|
+
# # "id" => "document-id",
|
|
34
|
+
# # "account_id" => "account-id",
|
|
35
|
+
# # "name" => "my_document.pdf",
|
|
36
|
+
# # "status" => "metadata_ready",
|
|
37
|
+
# # "artifacts" => { "original" => "https://...", "thumbnail" => "https://..." },
|
|
38
|
+
# # "is_closed" => false,
|
|
39
|
+
# # "signing_url" => "https://app.assinafy.com.br/sign/doc1",
|
|
40
|
+
# # "decline_reason" => nil,
|
|
41
|
+
# # "declined_by" => nil,
|
|
42
|
+
# # "created_at" => "2023-07-21T13:43:17Z",
|
|
43
|
+
# # "updated_at" => "2023-07-21T13:43:17Z",
|
|
44
|
+
# # "current_signer" => { "id" => "signer-id", "full_name" => "Signer Name",
|
|
45
|
+
# # "email" => "signer@example.com",
|
|
46
|
+
# # "verification_method" => "Email", "notification_methods" => ["Email"] },
|
|
47
|
+
# # "assignment" => { "id" => "1", "method" => "virtual", "items" => [{ ... }] }
|
|
48
|
+
# # # ... (see docs for full shape)
|
|
49
|
+
# # }
|
|
50
|
+
def current(signer_id, signer_access_code:)
|
|
51
|
+
sid = require_id(signer_id, 'Signer ID')
|
|
52
|
+
|
|
53
|
+
call('Failed to fetch signer document') do
|
|
54
|
+
http_get("signers/#{sid}/document", signer_access_code: signer_access_code)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
alias document current
|
|
59
|
+
|
|
60
|
+
# List all documents the signer has access to, with pagination metadata.
|
|
61
|
+
# This endpoint accepts either the workspace `X-Api-Key` header or the
|
|
62
|
+
# signer access code, so `signer_access_code:` is optional here; when nil
|
|
63
|
+
# it is omitted from the query and the header auth is used.
|
|
64
|
+
#
|
|
65
|
+
# @param signer_id [String]
|
|
66
|
+
# @param params [Hash] documented `page` and `per_page` query parameters
|
|
67
|
+
# @param signer_access_code [String, nil]
|
|
68
|
+
# @return [Hash{Symbol=>Array,Hash}] `{ data: [...], meta: { current_page:, per_page:, total:, last_page: } }`
|
|
69
|
+
# @see GET /signers/{signer_id}/documents
|
|
70
|
+
# @example List the signer's documents with pagination
|
|
71
|
+
# page = client.signer_documents.list('signer-id',
|
|
72
|
+
# { page: 1, per_page: 15 },
|
|
73
|
+
# signer_access_code: 'signer-access-code')
|
|
74
|
+
#
|
|
75
|
+
# # => {
|
|
76
|
+
# # data: [
|
|
77
|
+
# # {
|
|
78
|
+
# # "id" => "document-id",
|
|
79
|
+
# # "account_id" => "account-id",
|
|
80
|
+
# # "name" => "my_document.pdf",
|
|
81
|
+
# # "status" => "metadata_ready",
|
|
82
|
+
# # "assignment" => { "id" => "1", "method" => "virtual", "signers" => [{ ... }],
|
|
83
|
+
# # "items" => [{ ... }], "summary" => { "signer_count" => 2, ... } },
|
|
84
|
+
# # "artifacts" => { "original" => "https://...", "thumbnail" => "https://..." },
|
|
85
|
+
# # "pages" => [{ "id" => "...", "number" => 1, "height" => 1, "width" => 1,
|
|
86
|
+
# # "download_url" => "https://..." }],
|
|
87
|
+
# # "is_closed" => false
|
|
88
|
+
# # # ... (see docs for full shape)
|
|
89
|
+
# # }
|
|
90
|
+
# # ],
|
|
91
|
+
# # meta: { current_page: 1, per_page: 15, total: 1, last_page: 1 }
|
|
92
|
+
# # }
|
|
93
|
+
def list(signer_id, params = {}, signer_access_code: nil)
|
|
94
|
+
sid = require_id(signer_id, 'Signer ID')
|
|
95
|
+
|
|
96
|
+
call_list('Failed to list signer documents') do
|
|
97
|
+
http_get("signers/#{sid}/documents", params.merge(signer_access_code: signer_access_code))
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Lightweight search over the signer's documents. Like {#list}, this
|
|
102
|
+
# accepts either the workspace `X-Api-Key` header or the signer access
|
|
103
|
+
# code, so `signer_access_code:` is optional.
|
|
104
|
+
#
|
|
105
|
+
# @param signer_id [String]
|
|
106
|
+
# @param query [String] free-text search term
|
|
107
|
+
# @param params [Hash] optional deployment-specific query parameters
|
|
108
|
+
# @param signer_access_code [String, nil]
|
|
109
|
+
# @return [Hash{Symbol=>Array,Hash}] `{ data: [...], meta: {..} | nil }`
|
|
110
|
+
# @see GET /signers/{signer_id}/documents/search
|
|
111
|
+
# @example Search the signer's documents
|
|
112
|
+
# page = client.signer_documents.search('signer-id', 'contract')
|
|
113
|
+
#
|
|
114
|
+
# # Request: GET /signers/{signer_id}/documents/search?search=contract
|
|
115
|
+
# # => {
|
|
116
|
+
# # data: [
|
|
117
|
+
# # { "id" => "document-id", "account_id" => "account-id", "name" => "contract.pdf",
|
|
118
|
+
# # "status" => "pending_signature", "template_id" => nil,
|
|
119
|
+
# # "artifacts" => { "original" => "https://...", "thumbnail" => "https://..." },
|
|
120
|
+
# # "is_closed" => false, "signing_url" => "https://...", "tags" => []
|
|
121
|
+
# # # ... search returns the lightweight document shape (no embedded assignment)
|
|
122
|
+
# # }
|
|
123
|
+
# # ],
|
|
124
|
+
# # meta: nil
|
|
125
|
+
# # }
|
|
126
|
+
def search(signer_id, query, params = {}, signer_access_code: nil)
|
|
127
|
+
sid = require_id(signer_id, 'Signer ID')
|
|
128
|
+
|
|
129
|
+
call_list('Failed to search signer documents') do
|
|
130
|
+
http_get("signers/#{sid}/documents/search",
|
|
131
|
+
params.merge(search: query, signer_access_code: signer_access_code))
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Sign multiple virtual-method documents in a single call.
|
|
136
|
+
#
|
|
137
|
+
# Each document must be prepared for the "virtual" signature method.
|
|
138
|
+
#
|
|
139
|
+
# @param document_ids [Array<String>]
|
|
140
|
+
# @param signer_access_code [String]
|
|
141
|
+
# @return [Array] empty array on success (envelope `data` unwrapped)
|
|
142
|
+
# @see PUT /signers/documents/sign-multiple
|
|
143
|
+
# @example Sign two documents at once
|
|
144
|
+
# client.signer_documents.sign_multiple(%w[document-1 document-2],
|
|
145
|
+
# signer_access_code: 'signer-access-code')
|
|
146
|
+
#
|
|
147
|
+
# # Request body the SDK sends:
|
|
148
|
+
# # { "document_ids": ["document-1", "document-2"] }
|
|
149
|
+
#
|
|
150
|
+
# # => []
|
|
151
|
+
def sign_multiple(document_ids, signer_access_code:)
|
|
152
|
+
ids = require_array(document_ids, 'Document IDs')
|
|
153
|
+
|
|
154
|
+
call('Failed to sign documents') do
|
|
155
|
+
http_put('signers/documents/sign-multiple',
|
|
156
|
+
body_params(document_ids: ids),
|
|
157
|
+
signer_access_code: signer_access_code)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Decline multiple documents in a single call.
|
|
162
|
+
#
|
|
163
|
+
# @param document_ids [Array<String>]
|
|
164
|
+
# @param decline_reason [String]
|
|
165
|
+
# @param signer_access_code [String]
|
|
166
|
+
# @return [Array] empty array on success (envelope `data` unwrapped)
|
|
167
|
+
# @see PUT /signers/documents/decline-multiple
|
|
168
|
+
# @example Decline two documents with a reason
|
|
169
|
+
# client.signer_documents.decline_multiple(%w[document-1 document-2],
|
|
170
|
+
# decline_reason: 'Unfavorable terms.',
|
|
171
|
+
# signer_access_code: 'signer-access-code')
|
|
172
|
+
#
|
|
173
|
+
# # Request body the SDK sends:
|
|
174
|
+
# # { "document_ids": ["document-1", "document-2"], "decline_reason": "Unfavorable terms." }
|
|
175
|
+
#
|
|
176
|
+
# # => []
|
|
177
|
+
def decline_multiple(document_ids, decline_reason:, signer_access_code:)
|
|
178
|
+
ids = require_array(document_ids, 'Document IDs')
|
|
179
|
+
reason = require_present(decline_reason, 'Decline reason')
|
|
180
|
+
|
|
181
|
+
call('Failed to decline documents') do
|
|
182
|
+
http_put('signers/documents/decline-multiple',
|
|
183
|
+
body_params(document_ids: ids, decline_reason: reason),
|
|
184
|
+
signer_access_code: signer_access_code)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Download an artifact for one of the signer's documents.
|
|
189
|
+
#
|
|
190
|
+
# This endpoint is public (no auth required) — only the document and
|
|
191
|
+
# artifact IDs are needed — so `signer_access_code:` is optional and
|
|
192
|
+
# omitted from the query when nil.
|
|
193
|
+
#
|
|
194
|
+
# @param signer_id [String]
|
|
195
|
+
# @param document_id [String]
|
|
196
|
+
# @param artifact_name [String] `original`, `certificated`, `certificate-page`, `pades`, or
|
|
197
|
+
# `bundle`
|
|
198
|
+
# @param signer_access_code [String, nil] optional
|
|
199
|
+
# @return [String] binary file body (ASCII-8BIT), e.g. the raw PDF bytes
|
|
200
|
+
# @see GET /signers/{signer_id}/documents/{document_id}/download/{artifact_name}
|
|
201
|
+
# @example Download the original PDF and write it to disk (no access code needed)
|
|
202
|
+
# pdf = client.signer_documents.download('signer-id', 'document-id', 'original')
|
|
203
|
+
#
|
|
204
|
+
# # Response is the raw artifact body (Content-Type: application/pdf):
|
|
205
|
+
# # => "%PDF-1.7\n..." (binary string)
|
|
206
|
+
# File.binwrite('document.pdf', pdf)
|
|
207
|
+
def download(signer_id, document_id, artifact_name = 'certificated', signer_access_code: nil)
|
|
208
|
+
sid = require_id(signer_id, 'Signer ID')
|
|
209
|
+
did = require_id(document_id, 'Document ID')
|
|
210
|
+
art = require_id(artifact_name, 'Artifact name')
|
|
211
|
+
unless DocumentResource::ARTIFACT_TYPES.include?(art)
|
|
212
|
+
raise ValidationError.new('Invalid artifact type', { artifact_name: artifact_name })
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
call_binary('Failed to download signer document') do
|
|
216
|
+
http_get("signers/#{sid}/documents/#{did}/download/#{art}",
|
|
217
|
+
signer_access_code: signer_access_code)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|