clicksign-ruby-sdk 0.1.1
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/README.md +535 -0
- data/lib/clicksign/client.rb +143 -0
- data/lib/clicksign/configuration.rb +29 -0
- data/lib/clicksign/error_handler.rb +56 -0
- data/lib/clicksign/errors.rb +53 -0
- data/lib/clicksign/instrumentation.rb +32 -0
- data/lib/clicksign/json_api/atomic_results_parser.rb +61 -0
- data/lib/clicksign/json_api/bulk_operations_client.rb +95 -0
- data/lib/clicksign/json_api/operations/bulk_requirement.rb +89 -0
- data/lib/clicksign/json_api/operations.rb +38 -0
- data/lib/clicksign/json_api/parser.rb +31 -0
- data/lib/clicksign/json_api/query_builder.rb +45 -0
- data/lib/clicksign/json_api/serializer.rb +14 -0
- data/lib/clicksign/resource.rb +263 -0
- data/lib/clicksign/resources/acceptance_term/whatsapp.rb +12 -0
- data/lib/clicksign/resources/access_control_list.rb +35 -0
- data/lib/clicksign/resources/auto_signature/term.rb +12 -0
- data/lib/clicksign/resources/envelope_bulk_creation.rb +9 -0
- data/lib/clicksign/resources/folder.rb +22 -0
- data/lib/clicksign/resources/group.rb +21 -0
- data/lib/clicksign/resources/membership.rb +21 -0
- data/lib/clicksign/resources/notarial/bulk_requirement.rb +67 -0
- data/lib/clicksign/resources/notarial/document.rb +40 -0
- data/lib/clicksign/resources/notarial/envelope.rb +80 -0
- data/lib/clicksign/resources/notarial/event.rb +20 -0
- data/lib/clicksign/resources/notarial/requirement.rb +63 -0
- data/lib/clicksign/resources/notarial/signature_watcher.rb +39 -0
- data/lib/clicksign/resources/notarial/signer.rb +56 -0
- data/lib/clicksign/resources/template.rb +13 -0
- data/lib/clicksign/resources/template_field.rb +9 -0
- data/lib/clicksign/resources/user.rb +15 -0
- data/lib/clicksign/resources/webhook.rb +9 -0
- data/lib/clicksign/services.rb +35 -0
- data/lib/clicksign/version.rb +5 -0
- data/lib/clicksign/webhook.rb +41 -0
- data/lib/clicksign.rb +73 -0
- metadata +81 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module ErrorHandler
|
|
5
|
+
ERROR_MAP = {
|
|
6
|
+
[401, 403] => AuthenticationError,
|
|
7
|
+
[404] => NotFoundError,
|
|
8
|
+
[400, 422] => ValidationError,
|
|
9
|
+
[409] => ConflictError,
|
|
10
|
+
[429] => RateLimitError,
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def self.call(response)
|
|
14
|
+
return nil if (200..299).cover?(response.code.to_i)
|
|
15
|
+
|
|
16
|
+
metadata = extract_metadata(response)
|
|
17
|
+
message = extract_message(response)
|
|
18
|
+
error_class = error_class_for(response.code.to_i)
|
|
19
|
+
raise error_class.new(message, **metadata)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.error_class_for(code)
|
|
23
|
+
ERROR_MAP.each { |codes, klass| return klass if codes.include?(code) }
|
|
24
|
+
return ServerError if (500..599).cover?(code)
|
|
25
|
+
|
|
26
|
+
Error
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.extract_metadata(response)
|
|
30
|
+
{
|
|
31
|
+
status_code: response.code.to_i,
|
|
32
|
+
request_id: response['x-request-id'],
|
|
33
|
+
response_body: response.body,
|
|
34
|
+
response_headers: response.each_header.to_h,
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.extract_message(response)
|
|
39
|
+
return response.message if response.body.nil? || response.body.empty?
|
|
40
|
+
|
|
41
|
+
extract_from_json(response)
|
|
42
|
+
rescue JSON::ParserError
|
|
43
|
+
response.message
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.extract_from_json(response)
|
|
47
|
+
body = JSON.parse(response.body)
|
|
48
|
+
return response.message unless body.is_a?(Hash)
|
|
49
|
+
|
|
50
|
+
errors = body['errors']
|
|
51
|
+
return response.message unless errors.is_a?(Array)
|
|
52
|
+
|
|
53
|
+
errors.filter_map { |e| e['detail'] || e['title'] }.join(', ')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
attr_reader :status_code, :request_id, :response_body, :response_headers
|
|
6
|
+
|
|
7
|
+
def initialize(message = nil, status_code: nil, request_id: nil,
|
|
8
|
+
response_body: nil, response_headers: {})
|
|
9
|
+
super(message)
|
|
10
|
+
@status_code = status_code
|
|
11
|
+
@request_id = request_id
|
|
12
|
+
@response_body = response_body
|
|
13
|
+
@response_headers = response_headers || {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def retryable?
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class AuthenticationError < Error; end
|
|
22
|
+
class NotFoundError < Error; end
|
|
23
|
+
class ValidationError < Error; end
|
|
24
|
+
class ConflictError < Error; end
|
|
25
|
+
|
|
26
|
+
class RateLimitError < Error
|
|
27
|
+
def retryable?
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def rate_limit_remaining
|
|
32
|
+
response_headers['x-ratelimit-remaining']&.to_i
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def rate_limit_reset
|
|
36
|
+
response_headers['x-ratelimit-reset']
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class ServerError < Error
|
|
41
|
+
def retryable?
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class TimeoutError < Error
|
|
47
|
+
def retryable?
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class WebhookSignatureError < Error; end
|
|
53
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module Instrumentation
|
|
5
|
+
EVENTS = %i[request retry error].freeze
|
|
6
|
+
|
|
7
|
+
@callbacks = Hash.new { |h, k| h[k] = [] }
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def on(event, &block)
|
|
11
|
+
unless EVENTS.include?(event)
|
|
12
|
+
raise ArgumentError, "Unknown event: #{event}. Valid: #{EVENTS.join(', ')}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@callbacks[event] << block
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def publish(event, payload)
|
|
19
|
+
@callbacks[event].each do |cb|
|
|
20
|
+
cb.call(payload)
|
|
21
|
+
rescue StandardError
|
|
22
|
+
# Callbacks must not affect the request — errors are silently ignored.
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Removes all registered callbacks — intended for test teardown.
|
|
27
|
+
def clear
|
|
28
|
+
@callbacks = Hash.new { |h, k| h[k] = [] }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module JsonApi
|
|
5
|
+
module AtomicResultsParser
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def parse(raw, envelope_id:, operations:)
|
|
9
|
+
raw ||= {}
|
|
10
|
+
raise ValidationError, format_errors(raw['errors']) if envelope_errors?(raw)
|
|
11
|
+
|
|
12
|
+
Array(raw['atomic:results']).each_with_index.map do |slot, index|
|
|
13
|
+
build_operation_result(
|
|
14
|
+
slot: slot,
|
|
15
|
+
index: index,
|
|
16
|
+
op: operations.dig(index, 'op'),
|
|
17
|
+
envelope_id: envelope_id,
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def envelope_errors?(raw)
|
|
23
|
+
raw.key?('errors') && !raw.key?('atomic:results')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_operation_result(slot:, index:, op:, envelope_id:)
|
|
27
|
+
errors = slot['errors']
|
|
28
|
+
requirement = build_requirement(slot['data'], envelope_id: envelope_id)
|
|
29
|
+
|
|
30
|
+
Resources::Notarial::BulkRequirement::OperationResult.new(
|
|
31
|
+
index: index,
|
|
32
|
+
op: op,
|
|
33
|
+
requirement: requirement,
|
|
34
|
+
errors: errors,
|
|
35
|
+
raw: slot,
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def build_requirement(data, envelope_id:)
|
|
40
|
+
return nil if data.nil? || data.empty?
|
|
41
|
+
|
|
42
|
+
Resources::Notarial::Requirement.send(
|
|
43
|
+
:build_instance,
|
|
44
|
+
{
|
|
45
|
+
'id' => data['id'],
|
|
46
|
+
'type' => data['type'],
|
|
47
|
+
'attributes' => data.fetch('attributes', {}),
|
|
48
|
+
'relationships' => data.fetch('relationships', {}),
|
|
49
|
+
},
|
|
50
|
+
parent_id: envelope_id,
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def format_errors(errors)
|
|
55
|
+
return 'Validation failed' unless errors.is_a?(Array)
|
|
56
|
+
|
|
57
|
+
errors.filter_map { |e| e['detail'] || e['title'] }.join(', ')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Clicksign
|
|
8
|
+
module JsonApi
|
|
9
|
+
class BulkOperationsClient
|
|
10
|
+
HEADERS = {
|
|
11
|
+
'Content-Type' => 'application/vnd.api+json',
|
|
12
|
+
'Accept' => 'application/vnd.api+json',
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
def initialize(api_key:, base_url:, open_timeout: 2, read_timeout: 10,
|
|
16
|
+
write_timeout: 10, max_retries: 0)
|
|
17
|
+
@api_key = api_key
|
|
18
|
+
@base_url = base_url
|
|
19
|
+
@open_timeout = open_timeout
|
|
20
|
+
@read_timeout = read_timeout
|
|
21
|
+
@write_timeout = write_timeout
|
|
22
|
+
@max_retries = max_retries
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def post(path, body:)
|
|
26
|
+
response = perform_post(path, body)
|
|
27
|
+
parsed = parse_response_body(response) || {}
|
|
28
|
+
|
|
29
|
+
return parsed if parsed.key?('atomic:results')
|
|
30
|
+
|
|
31
|
+
ErrorHandler.call(response)
|
|
32
|
+
parsed
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def perform_post(path, body)
|
|
38
|
+
uri = build_uri(path)
|
|
39
|
+
request = build_request(uri, body)
|
|
40
|
+
execute_with_retry(request, uri)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def build_request(uri, body)
|
|
44
|
+
request = Net::HTTP::Post.new(uri, headers)
|
|
45
|
+
request.body = body.to_json
|
|
46
|
+
request
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def execute_with_retry(request, uri)
|
|
50
|
+
attempts = 0
|
|
51
|
+
begin
|
|
52
|
+
attempts += 1
|
|
53
|
+
safe_http_post(request, uri)
|
|
54
|
+
rescue Clicksign::TimeoutError => e
|
|
55
|
+
raise unless e.retryable? && attempts <= @max_retries
|
|
56
|
+
|
|
57
|
+
sleep([0.5 * (2**(attempts - 1)), 30].min)
|
|
58
|
+
retry
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def safe_http_post(request, uri)
|
|
63
|
+
http_post(request, uri)
|
|
64
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED => e
|
|
65
|
+
raise TimeoutError, e.message, e.backtrace
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def http_post(request, uri)
|
|
69
|
+
Net::HTTP.start(uri.host, uri.port,
|
|
70
|
+
use_ssl: uri.scheme == 'https',
|
|
71
|
+
open_timeout: @open_timeout,
|
|
72
|
+
read_timeout: @read_timeout,
|
|
73
|
+
write_timeout: @write_timeout) do |http|
|
|
74
|
+
http.request(request)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def headers
|
|
79
|
+
HEADERS.merge('Authorization' => @api_key)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_uri(path)
|
|
83
|
+
URI.parse("#{@base_url}#{path}")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def parse_response_body(response)
|
|
87
|
+
return nil if response.body.nil? || response.body.empty?
|
|
88
|
+
|
|
89
|
+
JSON.parse(response.body)
|
|
90
|
+
rescue JSON::ParserError
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module JsonApi
|
|
5
|
+
class Operations
|
|
6
|
+
class BulkRequirement < Operations
|
|
7
|
+
VALID_KINDS = %w[initials manuscript].freeze
|
|
8
|
+
|
|
9
|
+
def add_agree(signer_id:, document_id:, role:)
|
|
10
|
+
validate_ids!(signer_id, document_id)
|
|
11
|
+
raise ArgumentError, 'role is required' if role.to_s.empty?
|
|
12
|
+
|
|
13
|
+
add(
|
|
14
|
+
data: build_add_data(
|
|
15
|
+
signer_id: signer_id,
|
|
16
|
+
document_id: document_id,
|
|
17
|
+
attributes: { action: 'agree', role: role },
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_provide_evidence(signer_id:, document_id:, auth:)
|
|
23
|
+
validate_ids!(signer_id, document_id)
|
|
24
|
+
raise ArgumentError, 'auth is required' if auth.to_s.empty?
|
|
25
|
+
|
|
26
|
+
add(
|
|
27
|
+
data: build_add_data(
|
|
28
|
+
signer_id: signer_id,
|
|
29
|
+
document_id: document_id,
|
|
30
|
+
attributes: { action: 'provide_evidence', auth: auth },
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add_rubricate(signer_id:, document_id:, pages: nil, rubric_field: nil,
|
|
36
|
+
kind: nil)
|
|
37
|
+
validate_ids!(signer_id, document_id)
|
|
38
|
+
if pages.nil? && rubric_field.nil?
|
|
39
|
+
raise ArgumentError, 'pages or rubric_field is required'
|
|
40
|
+
end
|
|
41
|
+
if kind && !VALID_KINDS.include?(kind.to_s)
|
|
42
|
+
raise ArgumentError, "kind must be one of: #{VALID_KINDS.join(', ')}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
add(
|
|
46
|
+
data: build_add_data(
|
|
47
|
+
signer_id: signer_id,
|
|
48
|
+
document_id: document_id,
|
|
49
|
+
attributes: { action: 'rubricate', pages: pages,
|
|
50
|
+
rubric_field: rubric_field, kind: kind },
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def remove(requirement_id:)
|
|
56
|
+
raise ArgumentError, 'requirement_id is required' if requirement_id.to_s.empty?
|
|
57
|
+
|
|
58
|
+
super(ref: { type: 'requirements', id: requirement_id })
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def validate_ids!(signer_id, document_id)
|
|
64
|
+
raise ArgumentError, 'signer_id is required' if signer_id.to_s.empty?
|
|
65
|
+
raise ArgumentError, 'document_id is required' if document_id.to_s.empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def build_add_data(signer_id:, document_id:, attributes:)
|
|
69
|
+
{
|
|
70
|
+
type: 'requirements',
|
|
71
|
+
attributes: compact_attributes(attributes),
|
|
72
|
+
relationships: {
|
|
73
|
+
signer: { data: { type: 'signers', id: signer_id.to_s } },
|
|
74
|
+
document: { data: { type: 'documents', id: document_id.to_s } },
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def compact_attributes(attrs)
|
|
80
|
+
attrs.each_with_object({}) do |(key, value), result|
|
|
81
|
+
next if value.nil?
|
|
82
|
+
|
|
83
|
+
result[key.to_s] = value.is_a?(Symbol) ? value.to_s : value
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module JsonApi
|
|
5
|
+
class Operations
|
|
6
|
+
def initialize
|
|
7
|
+
@entries = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add(data:)
|
|
11
|
+
@entries << { 'op' => 'add', 'data' => stringify(data) }
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def remove(ref:)
|
|
16
|
+
@entries << { 'op' => 'remove', 'ref' => stringify(ref) }
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_h
|
|
21
|
+
{ 'atomic:operations' => @entries }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :entries
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def stringify(value)
|
|
29
|
+
case value
|
|
30
|
+
when Hash then value.transform_keys(&:to_s).transform_values { |v| stringify(v) }
|
|
31
|
+
when Array then value.map { |v| stringify(v) }
|
|
32
|
+
when Symbol then value.to_s
|
|
33
|
+
else value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module JsonApi
|
|
5
|
+
module Parser
|
|
6
|
+
def self.parse(raw)
|
|
7
|
+
raw_data = raw['data']
|
|
8
|
+
data = case raw_data
|
|
9
|
+
when Array then raw_data.map { |item| build(item) }
|
|
10
|
+
when Hash then [build(raw_data)]
|
|
11
|
+
else []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
included = Array(raw['included'])
|
|
15
|
+
.select { |item| item.is_a?(Hash) && item['type'] }
|
|
16
|
+
.map { |item| build(item) }
|
|
17
|
+
|
|
18
|
+
{ data: data, included: included }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.build(item)
|
|
22
|
+
{
|
|
23
|
+
'id' => item['id'],
|
|
24
|
+
'type' => item['type'],
|
|
25
|
+
'attributes' => item.fetch('attributes', {}),
|
|
26
|
+
'relationships' => item.fetch('relationships', {}),
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module JsonApi
|
|
5
|
+
class QueryBuilder
|
|
6
|
+
def initialize
|
|
7
|
+
@params = {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def filter(**filters)
|
|
11
|
+
filters.each { |k, v| @params["filter[#{k}]"] = v }
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def include(*types)
|
|
16
|
+
@params['include'] = types.join(',')
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def order(field)
|
|
21
|
+
@params['sort'] = field.to_s
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def page(number)
|
|
26
|
+
@params['page[number]'] = number
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def per(size)
|
|
31
|
+
@params['page[size]'] = size
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fields(**types)
|
|
36
|
+
types.each { |type, list| @params["fields[#{type}]"] = Array(list).join(',') }
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_params
|
|
41
|
+
@params.dup
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Clicksign
|
|
4
|
+
module JsonApi
|
|
5
|
+
module Serializer
|
|
6
|
+
def self.dump(type:, attributes:, id: nil, relationships: {})
|
|
7
|
+
data = { type: type, attributes: attributes }
|
|
8
|
+
data[:id] = id if id
|
|
9
|
+
data[:relationships] = relationships unless relationships.empty?
|
|
10
|
+
{ data: data }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|