attio 0.1.1 → 0.1.3
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/.github/workflows/ci.yml +39 -15
- data/.github/workflows/coverage.yml +67 -0
- data/.github/workflows/pr_checks.yml +25 -7
- data/.github/workflows/release.yml +25 -7
- data/.github/workflows/tests.yml +67 -0
- data/.rubocop.yml +362 -90
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +4 -4
- data/Gemfile +8 -5
- data/Gemfile.lock +2 -4
- data/README.md +8 -0
- data/Rakefile +8 -6
- data/attio.gemspec +6 -7
- data/danger/Dangerfile +22 -34
- data/docs/example.rb +30 -29
- data/lib/attio/client.rb +31 -29
- data/lib/attio/connection_pool.rb +26 -14
- data/lib/attio/errors.rb +4 -2
- data/lib/attio/http_client.rb +58 -37
- data/lib/attio/logger.rb +37 -27
- data/lib/attio/resources/attributes.rb +12 -5
- data/lib/attio/resources/base.rb +14 -24
- data/lib/attio/resources/lists.rb +16 -7
- data/lib/attio/resources/objects.rb +11 -4
- data/lib/attio/resources/records.rb +42 -47
- data/lib/attio/resources/users.rb +10 -4
- data/lib/attio/resources/workspaces.rb +9 -1
- data/lib/attio/retry_handler.rb +19 -11
- data/lib/attio/version.rb +3 -1
- data/lib/attio.rb +11 -9
- metadata +3 -18
- data/run_tests.rb +0 -52
- data/test_basic.rb +0 -51
- data/test_typhoeus.rb +0 -31
data/lib/attio/logger.rb
CHANGED
@@ -1,7 +1,17 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "json"
|
3
5
|
|
4
6
|
module Attio
|
7
|
+
# Enhanced logger for structured logging with JSON formatting
|
8
|
+
#
|
9
|
+
# This logger extends Ruby's standard Logger to provide structured
|
10
|
+
# logging with contextual information and JSON output.
|
11
|
+
#
|
12
|
+
# @example Basic usage
|
13
|
+
# logger = Attio::Logger.new(STDOUT)
|
14
|
+
# logger.info("API request", method: "GET", path: "/users")
|
5
15
|
class Logger < ::Logger
|
6
16
|
def initialize(logdev, level: ::Logger::INFO, formatter: nil)
|
7
17
|
super(logdev)
|
@@ -25,23 +35,21 @@ module Attio
|
|
25
35
|
super(format_message(message, context))
|
26
36
|
end
|
27
37
|
|
28
|
-
private
|
29
|
-
|
30
|
-
def format_message(message, context)
|
38
|
+
private def format_message(message, context)
|
31
39
|
return message if context.empty?
|
32
|
-
|
40
|
+
|
33
41
|
{
|
34
42
|
message: message,
|
35
|
-
**context
|
43
|
+
**context,
|
36
44
|
}
|
37
45
|
end
|
38
46
|
|
39
|
-
def default_formatter
|
47
|
+
private def default_formatter
|
40
48
|
proc do |severity, datetime, progname, msg|
|
41
49
|
data = {
|
42
50
|
timestamp: datetime.iso8601,
|
43
51
|
level: severity,
|
44
|
-
progname: progname
|
52
|
+
progname: progname,
|
45
53
|
}
|
46
54
|
|
47
55
|
if msg.is_a?(Hash)
|
@@ -55,6 +63,12 @@ module Attio
|
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
66
|
+
# Specialized logger for API request/response logging
|
67
|
+
#
|
68
|
+
# This class provides sanitized logging of HTTP requests and responses,
|
69
|
+
# automatically redacting sensitive information like API keys.
|
70
|
+
#
|
71
|
+
# @api private
|
58
72
|
class RequestLogger
|
59
73
|
attr_reader :logger, :log_level
|
60
74
|
|
@@ -67,39 +81,35 @@ module Attio
|
|
67
81
|
return unless logger
|
68
82
|
|
69
83
|
logger.send(log_level, "API Request",
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
)
|
84
|
+
method: method.to_s.upcase,
|
85
|
+
url: url,
|
86
|
+
headers: sanitize_headers(headers),
|
87
|
+
body: sanitize_body(body))
|
75
88
|
end
|
76
89
|
|
77
90
|
def log_response(response, duration)
|
78
91
|
return unless logger
|
79
92
|
|
80
93
|
logger.send(log_level, "API Response",
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
)
|
94
|
+
status: response.code,
|
95
|
+
duration_ms: (duration * 1000).round(2),
|
96
|
+
headers: response.headers,
|
97
|
+
body_size: response.body&.bytesize)
|
86
98
|
end
|
87
99
|
|
88
|
-
private
|
89
|
-
|
90
|
-
def sanitize_headers(headers)
|
100
|
+
private def sanitize_headers(headers)
|
91
101
|
headers.transform_values do |value|
|
92
|
-
if value.include?(
|
93
|
-
value.gsub(/Bearer\s+[\w\-]+/,
|
102
|
+
if value.include?("Bearer")
|
103
|
+
value.gsub(/Bearer\s+[\w\-]+/, "Bearer [REDACTED]")
|
94
104
|
else
|
95
105
|
value
|
96
106
|
end
|
97
107
|
end
|
98
108
|
end
|
99
109
|
|
100
|
-
def sanitize_body(body)
|
110
|
+
private def sanitize_body(body)
|
101
111
|
return nil unless body
|
102
|
-
|
112
|
+
|
103
113
|
if body.is_a?(String) && body.length > 1000
|
104
114
|
"#{body[0..1000]}... (truncated)"
|
105
115
|
else
|
@@ -107,4 +117,4 @@ module Attio
|
|
107
117
|
end
|
108
118
|
end
|
109
119
|
end
|
110
|
-
end
|
120
|
+
end
|
@@ -1,5 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Attio
|
2
4
|
module Resources
|
5
|
+
# API resource for managing Attio attributes
|
6
|
+
#
|
7
|
+
# Attributes define custom fields that can be added to objects
|
8
|
+
# and records in your Attio workspace.
|
9
|
+
#
|
10
|
+
# @example Listing attributes
|
11
|
+
# client.attributes.list(object: "people")
|
3
12
|
class Attributes < Base
|
4
13
|
def list(object:, **params)
|
5
14
|
validate_object!(object)
|
@@ -12,15 +21,13 @@ module Attio
|
|
12
21
|
request(:get, "objects/#{object}/attributes/#{id_or_slug}")
|
13
22
|
end
|
14
23
|
|
15
|
-
private
|
16
|
-
|
17
|
-
def validate_object!(object)
|
24
|
+
private def validate_object!(object)
|
18
25
|
raise ArgumentError, "Object type is required" if object.nil? || object.to_s.strip.empty?
|
19
26
|
end
|
20
27
|
|
21
|
-
def validate_id_or_slug!(id_or_slug)
|
28
|
+
private def validate_id_or_slug!(id_or_slug)
|
22
29
|
raise ArgumentError, "Attribute ID or slug is required" if id_or_slug.nil? || id_or_slug.to_s.strip.empty?
|
23
30
|
end
|
24
31
|
end
|
25
32
|
end
|
26
|
-
end
|
33
|
+
end
|
data/lib/attio/resources/base.rb
CHANGED
@@ -1,39 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Base class for all API resource classes.
|
4
|
+
#
|
5
|
+
# This class provides common functionality and request handling
|
6
|
+
# for all Attio API resource implementations.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @author Ernest Sim
|
10
|
+
# @since 1.0.0
|
1
11
|
module Attio
|
2
12
|
module Resources
|
3
|
-
# Base class for all API resource classes.
|
4
|
-
#
|
5
|
-
# This class provides common functionality and request handling
|
6
|
-
# for all Attio API resource implementations.
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
# @author Ernest Sim
|
10
|
-
# @since 1.0.0
|
11
13
|
class Base
|
12
14
|
# @return [Client] The API client instance
|
13
15
|
attr_reader :client
|
14
16
|
|
15
17
|
# Initialize a new resource instance.
|
16
|
-
#
|
18
|
+
#
|
17
19
|
# @param client [Client] The API client instance
|
18
20
|
# @raise [ArgumentError] if client is nil
|
19
21
|
def initialize(client)
|
20
22
|
raise ArgumentError, "Client is required" unless client
|
23
|
+
|
21
24
|
@client = client
|
22
25
|
end
|
23
26
|
|
24
|
-
private
|
25
|
-
|
26
|
-
# Make an HTTP request to the API.
|
27
|
-
#
|
28
|
-
# @param method [Symbol] The HTTP method (:get, :post, :patch, :put, :delete)
|
29
|
-
# @param path [String] The API endpoint path
|
30
|
-
# @param params [Hash] Request parameters (default: {})
|
31
|
-
#
|
32
|
-
# @return [Hash] The API response
|
33
|
-
# @raise [ArgumentError] if method is unsupported
|
34
|
-
#
|
35
|
-
# @api private
|
36
|
-
def request(method, path, params = {})
|
27
|
+
private def request(method, path, params = {})
|
37
28
|
case method
|
38
29
|
when :get
|
39
30
|
client.connection.get(path, params)
|
@@ -49,7 +40,6 @@ module Attio
|
|
49
40
|
raise ArgumentError, "Unsupported HTTP method: #{method}"
|
50
41
|
end
|
51
42
|
end
|
52
|
-
|
53
43
|
end
|
54
44
|
end
|
55
|
-
end
|
45
|
+
end
|
@@ -1,5 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Attio
|
2
4
|
module Resources
|
5
|
+
# API resource for managing Attio lists and list entries
|
6
|
+
#
|
7
|
+
# Lists are custom collections for organizing records in your workspace.
|
8
|
+
#
|
9
|
+
# @example Listing all lists
|
10
|
+
# client.lists.list
|
11
|
+
#
|
12
|
+
# @example Adding an entry to a list
|
13
|
+
# client.lists.create_entry(id: "list_id", data: { record_id: "rec_123" })
|
3
14
|
class Lists < Base
|
4
15
|
def list(**params)
|
5
16
|
request(:get, "lists", params)
|
@@ -33,24 +44,22 @@ module Attio
|
|
33
44
|
request(:delete, "lists/#{list_id}/entries/#{entry_id}")
|
34
45
|
end
|
35
46
|
|
36
|
-
private
|
37
|
-
|
38
|
-
def validate_id!(id)
|
47
|
+
private def validate_id!(id)
|
39
48
|
raise ArgumentError, "List ID is required" if id.nil? || id.to_s.strip.empty?
|
40
49
|
end
|
41
50
|
|
42
|
-
def validate_list_id!(list_id)
|
51
|
+
private def validate_list_id!(list_id)
|
43
52
|
raise ArgumentError, "List ID is required" if list_id.nil? || list_id.to_s.strip.empty?
|
44
53
|
end
|
45
54
|
|
46
|
-
def validate_entry_id!(entry_id)
|
55
|
+
private def validate_entry_id!(entry_id)
|
47
56
|
raise ArgumentError, "Entry ID is required" if entry_id.nil? || entry_id.to_s.strip.empty?
|
48
57
|
end
|
49
58
|
|
50
|
-
def validate_data!(data)
|
59
|
+
private def validate_data!(data)
|
51
60
|
raise ArgumentError, "Data is required" if data.nil?
|
52
61
|
raise ArgumentError, "Data must be a hash" unless data.is_a?(Hash)
|
53
62
|
end
|
54
63
|
end
|
55
64
|
end
|
56
|
-
end
|
65
|
+
end
|
@@ -1,5 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Attio
|
2
4
|
module Resources
|
5
|
+
# API resource for managing Attio objects
|
6
|
+
#
|
7
|
+
# Objects define the schema and structure for different types of
|
8
|
+
# records in your Attio workspace (e.g., people, companies).
|
9
|
+
#
|
10
|
+
# @example Listing all objects
|
11
|
+
# client.objects.list
|
3
12
|
class Objects < Base
|
4
13
|
def list(**params)
|
5
14
|
request(:get, "objects", params)
|
@@ -10,11 +19,9 @@ module Attio
|
|
10
19
|
request(:get, "objects/#{id_or_slug}")
|
11
20
|
end
|
12
21
|
|
13
|
-
private
|
14
|
-
|
15
|
-
def validate_id_or_slug!(id_or_slug)
|
22
|
+
private def validate_id_or_slug!(id_or_slug)
|
16
23
|
raise ArgumentError, "Object ID or slug is required" if id_or_slug.nil? || id_or_slug.to_s.strip.empty?
|
17
24
|
end
|
18
25
|
end
|
19
26
|
end
|
20
|
-
end
|
27
|
+
end
|
@@ -1,41 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Handles all record-related API operations.
|
4
|
+
#
|
5
|
+
# Records are the main data entities in Attio, representing things like
|
6
|
+
# people, companies, deals, etc. This class provides methods to create,
|
7
|
+
# read, update, delete, and query records.
|
8
|
+
#
|
9
|
+
# @example List records
|
10
|
+
# records = client.records.list(object: 'people', filters: { name: 'John' })
|
11
|
+
#
|
12
|
+
# @example Create a record
|
13
|
+
# record = client.records.create(
|
14
|
+
# object: 'people',
|
15
|
+
# data: { name: 'Jane Doe', email: 'jane@example.com' }
|
16
|
+
# )
|
17
|
+
#
|
18
|
+
# @author Ernest Sim
|
19
|
+
# @since 1.0.0
|
1
20
|
module Attio
|
2
21
|
module Resources
|
3
|
-
# Handles all record-related API operations.
|
4
|
-
#
|
5
|
-
# Records are the main data entities in Attio, representing things like
|
6
|
-
# people, companies, deals, etc. This class provides methods to create,
|
7
|
-
# read, update, delete, and query records.
|
8
|
-
#
|
9
|
-
# @example List records
|
10
|
-
# records = client.records.list(object: 'people', filters: { name: 'John' })
|
11
|
-
#
|
12
|
-
# @example Create a record
|
13
|
-
# record = client.records.create(
|
14
|
-
# object: 'people',
|
15
|
-
# data: { name: 'Jane Doe', email: 'jane@example.com' }
|
16
|
-
# )
|
17
|
-
#
|
18
|
-
# @author Ernest Sim
|
19
|
-
# @since 1.0.0
|
20
22
|
class Records < Base
|
21
23
|
# Query and list records for a specific object type.
|
22
|
-
#
|
24
|
+
#
|
23
25
|
# This method allows you to retrieve records with optional filtering,
|
24
26
|
# sorting, and pagination parameters.
|
25
|
-
#
|
27
|
+
#
|
26
28
|
# @param object [String] The object type to query (e.g., 'people', 'companies')
|
27
29
|
# @param params [Hash] Query parameters including filters, sorts, and pagination
|
28
30
|
# @option params [Hash] :filters Filtering criteria
|
29
31
|
# @option params [Array] :sorts Sorting options
|
30
32
|
# @option params [Integer] :limit Number of records to return
|
31
33
|
# @option params [String] :cursor Pagination cursor for next page
|
32
|
-
#
|
34
|
+
#
|
33
35
|
# @return [Hash] API response containing records and pagination info
|
34
36
|
# @raise [ArgumentError] if object is nil or empty
|
35
|
-
#
|
37
|
+
#
|
36
38
|
# @example Basic listing
|
37
39
|
# records = client.records.list(object: 'people')
|
38
|
-
#
|
40
|
+
#
|
39
41
|
# @example With filters
|
40
42
|
# records = client.records.list(
|
41
43
|
# object: 'people',
|
@@ -48,13 +50,13 @@ module Attio
|
|
48
50
|
end
|
49
51
|
|
50
52
|
# Retrieve a specific record by ID.
|
51
|
-
#
|
53
|
+
#
|
52
54
|
# @param object [String] The object type (e.g., 'people', 'companies')
|
53
55
|
# @param id [String] The record ID
|
54
|
-
#
|
56
|
+
#
|
55
57
|
# @return [Hash] The record data
|
56
58
|
# @raise [ArgumentError] if object or id is nil or empty
|
57
|
-
#
|
59
|
+
#
|
58
60
|
# @example
|
59
61
|
# record = client.records.get(object: 'people', id: 'abc123')
|
60
62
|
def get(object:, id:)
|
@@ -64,13 +66,13 @@ module Attio
|
|
64
66
|
end
|
65
67
|
|
66
68
|
# Create a new record.
|
67
|
-
#
|
69
|
+
#
|
68
70
|
# @param object [String] The object type to create the record in
|
69
71
|
# @param data [Hash] The record data to create
|
70
|
-
#
|
72
|
+
#
|
71
73
|
# @return [Hash] The created record data
|
72
74
|
# @raise [ArgumentError] if object is nil/empty or data is invalid
|
73
|
-
#
|
75
|
+
#
|
74
76
|
# @example Create a person
|
75
77
|
# record = client.records.create(
|
76
78
|
# object: 'people',
|
@@ -87,14 +89,14 @@ module Attio
|
|
87
89
|
end
|
88
90
|
|
89
91
|
# Update an existing record.
|
90
|
-
#
|
92
|
+
#
|
91
93
|
# @param object [String] The object type
|
92
94
|
# @param id [String] The record ID to update
|
93
95
|
# @param data [Hash] The updated record data
|
94
|
-
#
|
96
|
+
#
|
95
97
|
# @return [Hash] The updated record data
|
96
98
|
# @raise [ArgumentError] if object, id, or data is invalid
|
97
|
-
#
|
99
|
+
#
|
98
100
|
# @example Update a person's name
|
99
101
|
# record = client.records.update(
|
100
102
|
# object: 'people',
|
@@ -109,13 +111,13 @@ module Attio
|
|
109
111
|
end
|
110
112
|
|
111
113
|
# Delete a record.
|
112
|
-
#
|
114
|
+
#
|
113
115
|
# @param object [String] The object type
|
114
116
|
# @param id [String] The record ID to delete
|
115
|
-
#
|
117
|
+
#
|
116
118
|
# @return [Hash] Deletion confirmation
|
117
119
|
# @raise [ArgumentError] if object or id is nil or empty
|
118
|
-
#
|
120
|
+
#
|
119
121
|
# @example
|
120
122
|
# client.records.delete(object: 'people', id: 'abc123')
|
121
123
|
def delete(object:, id:)
|
@@ -124,35 +126,28 @@ module Attio
|
|
124
126
|
request(:delete, "objects/#{object}/records/#{id}")
|
125
127
|
end
|
126
128
|
|
127
|
-
private
|
128
|
-
|
129
|
-
# Validates that the object parameter is present and not empty.
|
130
|
-
#
|
131
|
-
# @param object [String, nil] The object type to validate
|
132
|
-
# @raise [ArgumentError] if object is nil or empty
|
133
|
-
# @api private
|
134
|
-
def validate_object!(object)
|
129
|
+
private def validate_object!(object)
|
135
130
|
raise ArgumentError, "Object type is required" if object.nil? || object.to_s.strip.empty?
|
136
131
|
end
|
137
132
|
|
138
133
|
# Validates that the ID parameter is present and not empty.
|
139
|
-
#
|
134
|
+
#
|
140
135
|
# @param id [String, nil] The record ID to validate
|
141
136
|
# @raise [ArgumentError] if id is nil or empty
|
142
137
|
# @api private
|
143
|
-
def validate_id!(id)
|
138
|
+
private def validate_id!(id)
|
144
139
|
raise ArgumentError, "Record ID is required" if id.nil? || id.to_s.strip.empty?
|
145
140
|
end
|
146
141
|
|
147
142
|
# Validates that the data parameter is present and is a hash.
|
148
|
-
#
|
143
|
+
#
|
149
144
|
# @param data [Hash, nil] The data to validate
|
150
145
|
# @raise [ArgumentError] if data is nil or not a hash
|
151
146
|
# @api private
|
152
|
-
def validate_data!(data)
|
147
|
+
private def validate_data!(data)
|
153
148
|
raise ArgumentError, "Data is required" if data.nil?
|
154
149
|
raise ArgumentError, "Data must be a hash" unless data.is_a?(Hash)
|
155
150
|
end
|
156
151
|
end
|
157
152
|
end
|
158
|
-
end
|
153
|
+
end
|
@@ -1,5 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Attio
|
2
4
|
module Resources
|
5
|
+
# API resource for managing workspace users
|
6
|
+
#
|
7
|
+
# Users are people who have access to your Attio workspace.
|
8
|
+
#
|
9
|
+
# @example Listing all users
|
10
|
+
# client.users.list
|
3
11
|
class Users < Base
|
4
12
|
def list(**params)
|
5
13
|
request(:get, "users", params)
|
@@ -10,11 +18,9 @@ module Attio
|
|
10
18
|
request(:get, "users/#{id}")
|
11
19
|
end
|
12
20
|
|
13
|
-
private
|
14
|
-
|
15
|
-
def validate_id!(id)
|
21
|
+
private def validate_id!(id)
|
16
22
|
raise ArgumentError, "User ID is required" if id.nil? || id.to_s.strip.empty?
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
20
|
-
end
|
26
|
+
end
|
@@ -1,5 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Attio
|
2
4
|
module Resources
|
5
|
+
# API resource for managing workspace information
|
6
|
+
#
|
7
|
+
# Workspaces are the top-level organizational unit in Attio.
|
8
|
+
#
|
9
|
+
# @example Getting workspace information
|
10
|
+
# client.workspaces.get
|
3
11
|
class Workspaces < Base
|
4
12
|
def get
|
5
13
|
request(:get, "workspace")
|
@@ -10,4 +18,4 @@ module Attio
|
|
10
18
|
end
|
11
19
|
end
|
12
20
|
end
|
13
|
-
end
|
21
|
+
end
|
data/lib/attio/retry_handler.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Attio
|
4
|
+
# Handles automatic retry logic for failed API requests
|
5
|
+
#
|
6
|
+
# This class implements exponential backoff retry strategy for
|
7
|
+
# transient errors like timeouts and rate limits.
|
8
|
+
#
|
9
|
+
# @example Using the retry handler
|
10
|
+
# handler = RetryHandler.new(max_retries: 5)
|
11
|
+
# handler.with_retry { api_client.get("/endpoint") }
|
2
12
|
class RetryHandler
|
3
13
|
DEFAULT_MAX_RETRIES = 3
|
4
14
|
DEFAULT_RETRY_DELAY = 1 # seconds
|
@@ -7,12 +17,12 @@ module Attio
|
|
7
17
|
HttpClient::TimeoutError,
|
8
18
|
HttpClient::ConnectionError,
|
9
19
|
ServerError,
|
10
|
-
RateLimitError
|
20
|
+
RateLimitError,
|
11
21
|
].freeze
|
12
22
|
|
13
23
|
attr_reader :max_retries, :retry_delay, :backoff_factor, :logger
|
14
24
|
|
15
|
-
def initialize(max_retries: DEFAULT_MAX_RETRIES,
|
25
|
+
def initialize(max_retries: DEFAULT_MAX_RETRIES,
|
16
26
|
retry_delay: DEFAULT_RETRY_DELAY,
|
17
27
|
backoff_factor: DEFAULT_BACKOFF_FACTOR,
|
18
28
|
logger: nil)
|
@@ -22,7 +32,7 @@ module Attio
|
|
22
32
|
@logger = logger
|
23
33
|
end
|
24
34
|
|
25
|
-
def with_retry
|
35
|
+
def with_retry
|
26
36
|
retries = 0
|
27
37
|
delay = retry_delay
|
28
38
|
|
@@ -30,7 +40,7 @@ module Attio
|
|
30
40
|
yield
|
31
41
|
rescue *RETRIABLE_ERRORS => e
|
32
42
|
retries += 1
|
33
|
-
|
43
|
+
|
34
44
|
if retries <= max_retries
|
35
45
|
log_retry(e, retries, delay)
|
36
46
|
sleep(delay)
|
@@ -43,11 +53,9 @@ module Attio
|
|
43
53
|
end
|
44
54
|
end
|
45
55
|
|
46
|
-
private
|
47
|
-
|
48
|
-
def log_retry(error, attempt, delay)
|
56
|
+
private def log_retry(error, attempt, delay)
|
49
57
|
return unless logger
|
50
|
-
|
58
|
+
|
51
59
|
logger.warn(
|
52
60
|
"Retry attempt #{attempt}/#{max_retries}",
|
53
61
|
error: error.class.name,
|
@@ -56,9 +64,9 @@ module Attio
|
|
56
64
|
)
|
57
65
|
end
|
58
66
|
|
59
|
-
def log_failure(error, attempts)
|
67
|
+
private def log_failure(error, attempts)
|
60
68
|
return unless logger
|
61
|
-
|
69
|
+
|
62
70
|
logger.error(
|
63
71
|
"Max retries exceeded",
|
64
72
|
error: error.class.name,
|
@@ -67,4 +75,4 @@ module Attio
|
|
67
75
|
)
|
68
76
|
end
|
69
77
|
end
|
70
|
-
end
|
78
|
+
end
|
data/lib/attio/version.rb
CHANGED
data/lib/attio.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "typhoeus"
|
2
4
|
|
3
5
|
require "attio/version"
|
@@ -14,32 +16,32 @@ require "attio/resources/attributes"
|
|
14
16
|
require "attio/resources/users"
|
15
17
|
|
16
18
|
# The main Attio module provides access to the Attio API client.
|
17
|
-
#
|
19
|
+
#
|
18
20
|
# This is the primary entry point for interacting with the Attio API.
|
19
|
-
#
|
21
|
+
#
|
20
22
|
# @example Basic usage
|
21
23
|
# client = Attio.client(api_key: 'your-api-key')
|
22
|
-
#
|
24
|
+
#
|
23
25
|
# @example Working with records
|
24
26
|
# # List records for a specific object type
|
25
27
|
# records = client.records.list(object: 'people', filters: { name: 'John' })
|
26
|
-
#
|
28
|
+
#
|
27
29
|
# # Create a new record
|
28
30
|
# new_record = client.records.create(
|
29
31
|
# object: 'people',
|
30
32
|
# data: { name: 'Jane Doe', email: 'jane@example.com' }
|
31
33
|
# )
|
32
|
-
#
|
34
|
+
#
|
33
35
|
# # Get a specific record
|
34
36
|
# record = client.records.get(object: 'people', id: 'record-id')
|
35
|
-
#
|
37
|
+
#
|
36
38
|
# # Update a record
|
37
39
|
# updated = client.records.update(
|
38
40
|
# object: 'people',
|
39
41
|
# id: 'record-id',
|
40
42
|
# data: { name: 'Jane Smith' }
|
41
43
|
# )
|
42
|
-
#
|
44
|
+
#
|
43
45
|
# # Delete a record
|
44
46
|
# client.records.delete(object: 'people', id: 'record-id')
|
45
47
|
#
|
@@ -47,11 +49,11 @@ require "attio/resources/users"
|
|
47
49
|
# @since 1.0.0
|
48
50
|
module Attio
|
49
51
|
# Creates a new Attio API client instance.
|
50
|
-
#
|
52
|
+
#
|
51
53
|
# @param api_key [String] Your Attio API key
|
52
54
|
# @return [Client] A new client instance configured with the provided API key
|
53
55
|
# @raise [ArgumentError] if api_key is nil or empty
|
54
|
-
#
|
56
|
+
#
|
55
57
|
# @example Create a client
|
56
58
|
# client = Attio.client(api_key: 'your-api-key-here')
|
57
59
|
def self.client(api_key:)
|