attio 0.1.3 → 0.2.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/.github/workflows/release.yml +2 -6
- data/CHANGELOG.md +26 -1
- data/CONCEPTS.md +428 -0
- data/Gemfile.lock +3 -1
- data/README.md +157 -3
- data/examples/advanced_filtering.rb +178 -0
- data/examples/basic_usage.rb +110 -0
- data/examples/collaboration_example.rb +173 -0
- data/examples/full_workflow.rb +348 -0
- data/examples/notes_and_tasks.rb +200 -0
- data/lib/attio/client.rb +36 -0
- data/lib/attio/http_client.rb +12 -4
- data/lib/attio/resources/base.rb +54 -5
- data/lib/attio/resources/comments.rb +147 -0
- data/lib/attio/resources/lists.rb +9 -21
- data/lib/attio/resources/notes.rb +110 -0
- data/lib/attio/resources/records.rb +11 -24
- data/lib/attio/resources/tasks.rb +131 -0
- data/lib/attio/resources/threads.rb +154 -0
- data/lib/attio/version.rb +1 -1
- data/lib/attio.rb +4 -0
- metadata +11 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c28065cbac5cffee49ef7864e60d19543ab116ef2bea68374ec2b995476efbd9
|
4
|
+
data.tar.gz: 31036f5bd6d0fd23b827176fe6a52f620d17042fe1bc6d55f2c5f86bb03a09c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0a5e5fb4654d9bbb579babffde8540d6bbd3dc0b1463a22eb8838c42095e8e368fbe3f712a6e85a49bfdaf8d3adeb2386769591c36ebf0bb035b22bdeb407c6
|
7
|
+
data.tar.gz: fe5085f5f9b20e688ff8f4c7e21f42482432c911fe135f9df5f4acab92e101585c5b5d719f78d8fd349e020e038a0f8fd3d144e80816eeabe1b78b25570e7e1d
|
@@ -72,13 +72,9 @@ jobs:
|
|
72
72
|
echo "GEM_FILE=$(ls attio-*.gem)" >> $GITHUB_ENV
|
73
73
|
|
74
74
|
- name: Publish to RubyGems
|
75
|
+
env:
|
76
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
|
75
77
|
run: |
|
76
|
-
mkdir -p ~/.gem
|
77
|
-
cat << 'EOF' > ~/.gem/credentials
|
78
|
-
---
|
79
|
-
:rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}
|
80
|
-
EOF
|
81
|
-
chmod 0600 ~/.gem/credentials
|
82
78
|
gem push ${{ env.GEM_FILE }}
|
83
79
|
|
84
80
|
- name: Generate changelog
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
-
## [
|
8
|
+
## [0.2.0] - 2025-08-11
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Comments resource with full CRUD operations and emoji reactions
|
12
|
+
- Threads resource with participant management and status control
|
13
|
+
- Tasks resource with assignment and completion tracking
|
14
|
+
- Notes resource for creating and managing notes on records
|
15
|
+
- DELETE with body support in HttpClient for participant management
|
16
|
+
- URL encoding for emoji reactions using CGI.escape
|
17
|
+
- Comprehensive examples for collaboration features
|
18
|
+
- Advanced filtering and querying examples
|
19
|
+
- Complete CRM workflow example
|
20
|
+
|
21
|
+
### Improved
|
22
|
+
- Achieved 100% test coverage (376/376 lines)
|
23
|
+
- Increased test count from 147 to 265 tests
|
24
|
+
- Refactored Base class to reduce code duplication across resources
|
25
|
+
- Extracted common validation methods to base class
|
26
|
+
- Standardized error messages across all resources
|
27
|
+
- Fixed keyword arguments vs options hash issues in test mocks
|
28
|
+
- Updated README with all new features and comprehensive examples
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
- Semantic correctness in all test files
|
32
|
+
- REST convention compliance for DELETE operations
|
33
|
+
- Proper URL encoding for special characters in API paths
|
9
34
|
|
10
35
|
## [0.1.3] - 2025-08-11
|
11
36
|
|
data/CONCEPTS.md
ADDED
@@ -0,0 +1,428 @@
|
|
1
|
+
# Attio Ruby Gem - Architectural Concepts
|
2
|
+
|
3
|
+
This document explains the key architectural decisions, design patterns, and concepts behind the Attio Ruby gem implementation.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
- [Overall Architecture](#overall-architecture)
|
7
|
+
- [Template Method Pattern](#template-method-pattern-in-base-class)
|
8
|
+
- [DELETE with Body Support](#delete-with-body-support)
|
9
|
+
- [URL Encoding for Special Characters](#url-encoding-for-special-characters)
|
10
|
+
- [Collaboration Features Workflow](#collaboration-features-workflow)
|
11
|
+
- [Error Handling Strategy](#error-handling-strategy)
|
12
|
+
- [Testing Strategy](#testing-strategy)
|
13
|
+
- [Key Design Decisions](#key-design-decisions-explained)
|
14
|
+
|
15
|
+
## Overall Architecture
|
16
|
+
|
17
|
+
The Attio Ruby gem follows a modular resource-based architecture with a clear separation of concerns:
|
18
|
+
|
19
|
+
```mermaid
|
20
|
+
graph TD
|
21
|
+
Client[Attio::Client] --> HttpClient[HTTP Client Layer]
|
22
|
+
Client --> Resources[Resource Classes]
|
23
|
+
|
24
|
+
Resources --> Base[Base Resource]
|
25
|
+
Resources --> Records[Records]
|
26
|
+
Resources --> Comments[Comments]
|
27
|
+
Resources --> Threads[Threads]
|
28
|
+
Resources --> Tasks[Tasks]
|
29
|
+
Resources --> Notes[Notes]
|
30
|
+
Resources --> Lists[Lists]
|
31
|
+
Resources --> Objects[Objects]
|
32
|
+
Resources --> Attributes[Attributes]
|
33
|
+
Resources --> Users[Users]
|
34
|
+
Resources --> Workspaces[Workspaces]
|
35
|
+
|
36
|
+
Base --> Validations[Common Validations]
|
37
|
+
Base --> Request[Request Handler]
|
38
|
+
|
39
|
+
HttpClient --> Typhoeus[Typhoeus HTTP]
|
40
|
+
HttpClient --> ErrorHandling[Error Handling]
|
41
|
+
HttpClient --> ConnectionPool[Connection Pooling]
|
42
|
+
```
|
43
|
+
|
44
|
+
### Key Components:
|
45
|
+
- **Client**: Entry point that initializes all resources and manages configuration
|
46
|
+
- **HttpClient**: Handles all HTTP communication, retries, and error responses
|
47
|
+
- **Base Resource**: Provides common functionality to all resource classes
|
48
|
+
- **Resource Classes**: Implement specific API endpoints for each Attio resource type
|
49
|
+
|
50
|
+
## Template Method Pattern in Base Class
|
51
|
+
|
52
|
+
We implemented the Template Method pattern to eliminate code duplication across resources:
|
53
|
+
|
54
|
+
```mermaid
|
55
|
+
classDiagram
|
56
|
+
class Base {
|
57
|
+
-connection
|
58
|
+
+request(method, path, params)
|
59
|
+
#validate_id!(id, resource_name)
|
60
|
+
#validate_required_string!(value, field_name)
|
61
|
+
#validate_required_hash!(value, field_name)
|
62
|
+
#handle_delete_request(connection, path, params)
|
63
|
+
}
|
64
|
+
|
65
|
+
class Comments {
|
66
|
+
+list(params)
|
67
|
+
+create(data)
|
68
|
+
+react(id, emoji)
|
69
|
+
+unreact(id, emoji)
|
70
|
+
}
|
71
|
+
|
72
|
+
class Threads {
|
73
|
+
+list(params)
|
74
|
+
+create(data)
|
75
|
+
+add_participant(id, member_id)
|
76
|
+
+remove_participant(id, member_id)
|
77
|
+
+close(id)
|
78
|
+
+reopen(id)
|
79
|
+
}
|
80
|
+
|
81
|
+
class Tasks {
|
82
|
+
+list(params)
|
83
|
+
+create(data)
|
84
|
+
+complete(id, completed_at)
|
85
|
+
+uncomplete(id)
|
86
|
+
}
|
87
|
+
|
88
|
+
Base <|-- Comments
|
89
|
+
Base <|-- Threads
|
90
|
+
Base <|-- Tasks
|
91
|
+
Base <|-- Notes
|
92
|
+
Base <|-- Records
|
93
|
+
Base <|-- Lists
|
94
|
+
```
|
95
|
+
|
96
|
+
### Benefits:
|
97
|
+
- **Code Reuse**: ~150 lines of duplicated validation code eliminated
|
98
|
+
- **Consistency**: Standardized error messages and validation logic
|
99
|
+
- **Maintainability**: Changes to common functionality only need to be made once
|
100
|
+
- **Extensibility**: New resources can easily inherit common behavior
|
101
|
+
|
102
|
+
## DELETE with Body Support
|
103
|
+
|
104
|
+
A key architectural decision for REST compliance when managing thread participants:
|
105
|
+
|
106
|
+
```mermaid
|
107
|
+
sequenceDiagram
|
108
|
+
participant Client
|
109
|
+
participant Threads
|
110
|
+
participant Base
|
111
|
+
participant HttpClient
|
112
|
+
participant API
|
113
|
+
|
114
|
+
Client->>Threads: remove_participant(id, member_id)
|
115
|
+
Threads->>Threads: validate_id!(id)
|
116
|
+
Threads->>Threads: validate_required_string!(member_id)
|
117
|
+
Threads->>Base: request(:delete, path, params)
|
118
|
+
Base->>Base: handle_delete_request
|
119
|
+
Base->>HttpClient: delete(path, {member_id: ...})
|
120
|
+
HttpClient->>HttpClient: Check if params exist
|
121
|
+
alt Has Parameters
|
122
|
+
HttpClient->>API: DELETE with JSON body
|
123
|
+
Note over HttpClient,API: Content-Type: application/json
|
124
|
+
else No Parameters
|
125
|
+
HttpClient->>API: DELETE without body
|
126
|
+
end
|
127
|
+
API-->>HttpClient: 200 OK
|
128
|
+
HttpClient-->>Client: Response
|
129
|
+
```
|
130
|
+
|
131
|
+
### Implementation Details:
|
132
|
+
```ruby
|
133
|
+
# In HttpClient
|
134
|
+
def delete(path, params = nil)
|
135
|
+
if params
|
136
|
+
execute_request(:delete, path, body: params.to_json)
|
137
|
+
else
|
138
|
+
execute_request(:delete, path)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# In Base
|
143
|
+
private def handle_delete_request(connection, path, params)
|
144
|
+
params.empty? ? connection.delete(path) : connection.delete(path, params)
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
## URL Encoding for Special Characters
|
149
|
+
|
150
|
+
Critical for emoji reactions support and other special characters in URLs:
|
151
|
+
|
152
|
+
```mermaid
|
153
|
+
flowchart LR
|
154
|
+
Input["Emoji Input: 👍"] --> Validate[Validate Required String]
|
155
|
+
Validate --> Encode[CGI.escape emoji]
|
156
|
+
Encode --> Build[Build URL Path]
|
157
|
+
Build --> URL["comments/123/reactions/%F0%9F%91%8D"]
|
158
|
+
URL --> Request[HTTP DELETE Request]
|
159
|
+
Request --> API[Attio API]
|
160
|
+
```
|
161
|
+
|
162
|
+
### Example Implementation:
|
163
|
+
```ruby
|
164
|
+
def unreact(id:, emoji:)
|
165
|
+
validate_id!(id, "Comment")
|
166
|
+
validate_required_string!(emoji, "Emoji")
|
167
|
+
|
168
|
+
# CGI.escape properly encodes the emoji for URL usage
|
169
|
+
request(:delete, "comments/#{id}/reactions/#{CGI.escape(emoji)}")
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
### Why CGI.escape?
|
174
|
+
- Properly handles UTF-8 characters like emojis
|
175
|
+
- Converts special characters to percent-encoded format
|
176
|
+
- Ensures URL compatibility across different systems
|
177
|
+
- Standard Ruby library method, no additional dependencies
|
178
|
+
|
179
|
+
## Collaboration Features Workflow
|
180
|
+
|
181
|
+
The interconnected nature of collaboration resources in a CRM context:
|
182
|
+
|
183
|
+
```mermaid
|
184
|
+
graph TB
|
185
|
+
Record[Record<br/>Company/Person] --> Thread[Discussion Thread]
|
186
|
+
Record --> Task[Task]
|
187
|
+
Record --> Note[Note]
|
188
|
+
|
189
|
+
Thread --> Comment[Comment]
|
190
|
+
Comment --> Reaction[Emoji Reaction]
|
191
|
+
Thread --> Participants[Thread Participants]
|
192
|
+
Thread --> Status[Thread Status<br/>Open/Closed]
|
193
|
+
|
194
|
+
Task --> Assignment[Task Assignment]
|
195
|
+
Task --> DueDate[Due Date]
|
196
|
+
Task --> Completion[Completion Status]
|
197
|
+
|
198
|
+
Note --> Title[Note Title]
|
199
|
+
Note --> Content[Rich Text Content]
|
200
|
+
Note --> Timestamps[Created/Updated]
|
201
|
+
|
202
|
+
style Record fill:#f9f,stroke:#333,stroke-width:2px
|
203
|
+
style Thread fill:#bbf,stroke:#333,stroke-width:2px
|
204
|
+
style Task fill:#bfb,stroke:#333,stroke-width:2px
|
205
|
+
style Note fill:#ffb,stroke:#333,stroke-width:2px
|
206
|
+
```
|
207
|
+
|
208
|
+
### Resource Relationships:
|
209
|
+
- **Records** (Companies/People) serve as parent objects for collaboration features
|
210
|
+
- **Threads** enable team discussions with participant management
|
211
|
+
- **Comments** support rich text and emoji reactions within threads
|
212
|
+
- **Tasks** track actionable items with assignments and due dates
|
213
|
+
- **Notes** capture meeting notes and important information
|
214
|
+
|
215
|
+
## Error Handling Strategy
|
216
|
+
|
217
|
+
Comprehensive error handling with specific exception types and retry logic:
|
218
|
+
|
219
|
+
```mermaid
|
220
|
+
flowchart TD
|
221
|
+
Request[API Request] --> Response{Response Status}
|
222
|
+
Response -->|200-299| Success[Parse & Return Data]
|
223
|
+
Response -->|400| Validation[ValidationError]
|
224
|
+
Response -->|401| Auth[AuthenticationError]
|
225
|
+
Response -->|404| NotFound[NotFoundError]
|
226
|
+
Response -->|429| RateLimit[RateLimitError]
|
227
|
+
Response -->|500+| Server[ServerError]
|
228
|
+
Response -->|Other| Generic[Attio::Error]
|
229
|
+
|
230
|
+
Validation --> Details[Extract Error Details]
|
231
|
+
Auth --> Details
|
232
|
+
NotFound --> Details
|
233
|
+
RateLimit --> Details
|
234
|
+
Server --> Details
|
235
|
+
Generic --> Details
|
236
|
+
|
237
|
+
Details --> Retry{Retryable?}
|
238
|
+
|
239
|
+
Retry -->|Rate Limit| Backoff[Exponential Backoff]
|
240
|
+
Retry -->|Server Error| Backoff
|
241
|
+
Retry -->|No| Raise[Raise Exception]
|
242
|
+
|
243
|
+
Backoff --> Wait[Wait Period]
|
244
|
+
Wait --> Request
|
245
|
+
|
246
|
+
Raise --> Client[Client Handles Error]
|
247
|
+
```
|
248
|
+
|
249
|
+
### Error Classes:
|
250
|
+
```ruby
|
251
|
+
module Attio
|
252
|
+
class Error < StandardError; end
|
253
|
+
class ValidationError < Error; end
|
254
|
+
class AuthenticationError < Error; end
|
255
|
+
class NotFoundError < Error; end
|
256
|
+
class RateLimitError < Error; end
|
257
|
+
class ServerError < Error; end
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
### Retry Logic:
|
262
|
+
- **Rate Limits**: Automatic retry with exponential backoff
|
263
|
+
- **Server Errors**: Configurable retry attempts (default: 3)
|
264
|
+
- **Network Errors**: Automatic retry with connection pooling
|
265
|
+
- **Client Errors**: No retry, immediate failure
|
266
|
+
|
267
|
+
## Testing Strategy
|
268
|
+
|
269
|
+
Our approach to achieving 100% test coverage with semantic correctness:
|
270
|
+
|
271
|
+
```mermaid
|
272
|
+
graph LR
|
273
|
+
Tests[Test Suite<br/>265 Tests] --> Unit[Unit Tests]
|
274
|
+
Tests --> Integration[Integration Mocks]
|
275
|
+
Tests --> Edge[Edge Cases]
|
276
|
+
|
277
|
+
Unit --> Doubles[Instance Doubles<br/>Type Safety]
|
278
|
+
Unit --> Stubs[Method Stubs<br/>Behavior Verification]
|
279
|
+
Unit --> Assertions[Response Assertions]
|
280
|
+
|
281
|
+
Integration --> HTTPMocks[HTTP Response Mocks]
|
282
|
+
Integration --> ErrorSim[Error Simulation]
|
283
|
+
Integration --> Params[Parameter Validation]
|
284
|
+
|
285
|
+
Edge --> NilValues[Nil/Empty Values]
|
286
|
+
Edge --> Special[Special Characters]
|
287
|
+
Edge --> Boundary[Boundary Conditions]
|
288
|
+
|
289
|
+
Coverage[SimpleCov] --> Report[100% Coverage<br/>376/376 Lines]
|
290
|
+
|
291
|
+
style Tests fill:#9f9,stroke:#333,stroke-width:2px
|
292
|
+
style Report fill:#9f9,stroke:#333,stroke-width:2px
|
293
|
+
```
|
294
|
+
|
295
|
+
### Testing Principles:
|
296
|
+
1. **Instance Doubles**: Use RSpec's `instance_double` for type safety
|
297
|
+
2. **Semantic Correctness**: Ensure mocks match actual implementation behavior
|
298
|
+
3. **Edge Case Coverage**: Test nil values, empty strings, special characters
|
299
|
+
4. **Error Simulation**: Test all error paths and exception handling
|
300
|
+
5. **Parameter Validation**: Verify both required and optional parameters
|
301
|
+
|
302
|
+
### Example Test Pattern:
|
303
|
+
```ruby
|
304
|
+
RSpec.describe Attio::Resources::Comments do
|
305
|
+
let(:connection) { instance_double(Attio::HttpClient) }
|
306
|
+
let(:comments) { described_class.new(connection) }
|
307
|
+
|
308
|
+
describe "#unreact" do
|
309
|
+
it "properly encodes emoji in URL" do
|
310
|
+
expect(connection).to receive(:delete)
|
311
|
+
.with("comments/123/reactions/%F0%9F%91%8D")
|
312
|
+
.and_return({ "success" => true })
|
313
|
+
|
314
|
+
comments.unreact(id: "123", emoji: "👍")
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
```
|
319
|
+
|
320
|
+
## Key Design Decisions Explained
|
321
|
+
|
322
|
+
### 1. Base Class Extraction
|
323
|
+
**Problem**: Massive code duplication across resource classes (~150 lines per resource)
|
324
|
+
|
325
|
+
**Solution**: Extract common validation and request handling into Base class
|
326
|
+
|
327
|
+
**Benefits**:
|
328
|
+
- Reduced codebase by ~1,500 lines
|
329
|
+
- Standardized error messages
|
330
|
+
- Single source of truth for validations
|
331
|
+
- Easier to add new resources
|
332
|
+
|
333
|
+
### 2. DELETE with Body Support
|
334
|
+
**Problem**: Thread participant management requires sending data with DELETE requests
|
335
|
+
|
336
|
+
**Solution**: Modified HttpClient to optionally accept body parameters for DELETE
|
337
|
+
|
338
|
+
**Benefits**:
|
339
|
+
- REST-compliant participant removal
|
340
|
+
- Consistent with other HTTP methods
|
341
|
+
- Clean API interface
|
342
|
+
|
343
|
+
### 3. URL Encoding Strategy
|
344
|
+
**Problem**: Special characters (emojis) in URL paths causing API errors
|
345
|
+
|
346
|
+
**Solution**: Use `CGI.escape` for proper URL encoding
|
347
|
+
|
348
|
+
**Benefits**:
|
349
|
+
- Full Unicode support
|
350
|
+
- Standard library solution
|
351
|
+
- Cross-platform compatibility
|
352
|
+
|
353
|
+
### 4. Keyword Arguments Correction
|
354
|
+
**Problem**: Test mocks used keyword arguments but implementation used positional arguments
|
355
|
+
|
356
|
+
**Solution**: Corrected all test mocks to use hash as second positional argument
|
357
|
+
|
358
|
+
**Example**:
|
359
|
+
```ruby
|
360
|
+
# Before (incorrect)
|
361
|
+
expect(connection).to receive(:get).with("comments", thread_id: "123")
|
362
|
+
|
363
|
+
# After (correct)
|
364
|
+
expect(connection).to receive(:get).with("comments", { thread_id: "123" })
|
365
|
+
```
|
366
|
+
|
367
|
+
### 5. Resource Modularity
|
368
|
+
**Problem**: Need to support diverse Attio API resources with different requirements
|
369
|
+
|
370
|
+
**Solution**: Each resource is self-contained with specific validations while inheriting common functionality
|
371
|
+
|
372
|
+
**Benefits**:
|
373
|
+
- Clear separation of concerns
|
374
|
+
- Easy to understand each resource
|
375
|
+
- Simple to add new endpoints
|
376
|
+
- Maintainable and testable
|
377
|
+
|
378
|
+
### 6. Comprehensive Error Handling
|
379
|
+
**Problem**: Need to handle various API error conditions gracefully
|
380
|
+
|
381
|
+
**Solution**: Specific error classes with detailed messages and retry logic
|
382
|
+
|
383
|
+
**Benefits**:
|
384
|
+
- Clear error messages for debugging
|
385
|
+
- Automatic retry for transient failures
|
386
|
+
- Proper error propagation to client code
|
387
|
+
|
388
|
+
## Performance Considerations
|
389
|
+
|
390
|
+
### Connection Pooling
|
391
|
+
- Reuses HTTP connections for better performance
|
392
|
+
- Configurable pool size based on usage patterns
|
393
|
+
- Automatic connection management
|
394
|
+
|
395
|
+
### Request Optimization
|
396
|
+
- Batch operations where supported by API
|
397
|
+
- Efficient parameter serialization
|
398
|
+
- Minimal memory footprint
|
399
|
+
|
400
|
+
### Caching Strategy
|
401
|
+
- Response caching for read-heavy operations (future enhancement)
|
402
|
+
- ETag support for conditional requests (future enhancement)
|
403
|
+
|
404
|
+
## Future Enhancements
|
405
|
+
|
406
|
+
### Planned Improvements
|
407
|
+
1. **Webhook Support**: Handle Attio webhook events
|
408
|
+
2. **Bulk Operations**: Batch create/update for better performance
|
409
|
+
3. **Async Operations**: Non-blocking API calls for long-running operations
|
410
|
+
4. **Response Caching**: Intelligent caching with cache invalidation
|
411
|
+
5. **Rate Limit Management**: Proactive rate limit handling with queuing
|
412
|
+
|
413
|
+
### API Coverage Expansion
|
414
|
+
- Custom field types support
|
415
|
+
- Advanced query builders
|
416
|
+
- Webhook event processing
|
417
|
+
- Real-time subscriptions
|
418
|
+
|
419
|
+
## Conclusion
|
420
|
+
|
421
|
+
The Attio Ruby gem architecture prioritizes:
|
422
|
+
- **Maintainability**: Clean, DRY code with clear separation of concerns
|
423
|
+
- **Reliability**: Comprehensive error handling and retry logic
|
424
|
+
- **Usability**: Intuitive API matching Ruby conventions
|
425
|
+
- **Testability**: 100% test coverage with semantic correctness
|
426
|
+
- **Extensibility**: Easy to add new features and resources
|
427
|
+
|
428
|
+
This architecture provides a solid foundation for building CRM integrations with Attio while maintaining code quality and developer experience.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
attio (0.
|
4
|
+
attio (0.2.0)
|
5
5
|
typhoeus (~> 1.4)
|
6
6
|
|
7
7
|
GEM
|
@@ -21,6 +21,7 @@ GEM
|
|
21
21
|
ethon (0.16.0)
|
22
22
|
ffi (>= 1.15.0)
|
23
23
|
ffi (1.17.2-arm64-darwin)
|
24
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
24
25
|
hashdiff (1.2.0)
|
25
26
|
json (2.13.2)
|
26
27
|
language_server-protocol (3.17.0.5)
|
@@ -107,6 +108,7 @@ GEM
|
|
107
108
|
|
108
109
|
PLATFORMS
|
109
110
|
arm64-darwin-24
|
111
|
+
x86_64-linux
|
110
112
|
|
111
113
|
DEPENDENCIES
|
112
114
|
attio!
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
[](https://github.com/idl3/attio/tree/master/spec)
|
5
5
|
[](https://idl3.github.io/attio)
|
6
6
|
[](https://badge.fury.io/rb/attio)
|
7
|
-
[](https://github.com/idl3/attio/tree/master/spec)
|
8
8
|
|
9
9
|
Ruby client for the [Attio CRM API](https://developers.attio.com/). This library provides easy access to the Attio API, allowing you to manage records, objects, lists, and more.
|
10
10
|
|
@@ -154,6 +154,156 @@ client.records.update(
|
|
154
154
|
|
155
155
|
### Working with Other Resources
|
156
156
|
|
157
|
+
#### Comments
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
# List comments on a record
|
161
|
+
comments = client.comments.list(
|
162
|
+
parent_object: 'people',
|
163
|
+
parent_record_id: 'person-123'
|
164
|
+
)
|
165
|
+
|
166
|
+
# List comments in a thread
|
167
|
+
thread_comments = client.comments.list(thread_id: 'thread-456')
|
168
|
+
|
169
|
+
# Create a comment on a record
|
170
|
+
comment = client.comments.create(
|
171
|
+
parent_object: 'people',
|
172
|
+
parent_record_id: 'person-123',
|
173
|
+
content: 'This is a comment with **markdown** support!'
|
174
|
+
)
|
175
|
+
|
176
|
+
# Create a comment in a thread
|
177
|
+
thread_comment = client.comments.create(
|
178
|
+
thread_id: 'thread-456',
|
179
|
+
content: 'Following up on this discussion'
|
180
|
+
)
|
181
|
+
|
182
|
+
# Update a comment
|
183
|
+
updated_comment = client.comments.update(
|
184
|
+
id: 'comment-123',
|
185
|
+
content: 'Updated comment content'
|
186
|
+
)
|
187
|
+
|
188
|
+
# React to a comment
|
189
|
+
client.comments.react(id: 'comment-123', emoji: '👍')
|
190
|
+
|
191
|
+
# Remove reaction
|
192
|
+
client.comments.unreact(id: 'comment-123', emoji: '👍')
|
193
|
+
|
194
|
+
# Delete a comment
|
195
|
+
client.comments.delete(id: 'comment-123')
|
196
|
+
```
|
197
|
+
|
198
|
+
#### Threads
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
# List threads on a record
|
202
|
+
threads = client.threads.list(
|
203
|
+
parent_object: 'companies',
|
204
|
+
parent_record_id: 'company-123'
|
205
|
+
)
|
206
|
+
|
207
|
+
# Get a thread with comments
|
208
|
+
thread = client.threads.get(id: 'thread-123', include_comments: true)
|
209
|
+
|
210
|
+
# Create a thread
|
211
|
+
thread = client.threads.create(
|
212
|
+
parent_object: 'companies',
|
213
|
+
parent_record_id: 'company-123',
|
214
|
+
title: 'Q4 Planning Discussion',
|
215
|
+
description: 'Thread for Q4 planning discussions',
|
216
|
+
participant_ids: ['user-1', 'user-2']
|
217
|
+
)
|
218
|
+
|
219
|
+
# Update thread title
|
220
|
+
client.threads.update(id: 'thread-123', title: 'Updated Q4 Planning')
|
221
|
+
|
222
|
+
# Manage participants
|
223
|
+
client.threads.add_participants(id: 'thread-123', user_ids: ['user-3', 'user-4'])
|
224
|
+
client.threads.remove_participants(id: 'thread-123', user_ids: ['user-2'])
|
225
|
+
|
226
|
+
# Close and reopen threads
|
227
|
+
client.threads.close(id: 'thread-123')
|
228
|
+
client.threads.reopen(id: 'thread-123')
|
229
|
+
|
230
|
+
# Delete a thread
|
231
|
+
client.threads.delete(id: 'thread-123')
|
232
|
+
```
|
233
|
+
|
234
|
+
#### Tasks
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
# List all tasks
|
238
|
+
tasks = client.tasks.list
|
239
|
+
|
240
|
+
# List tasks with filters
|
241
|
+
my_tasks = client.tasks.list(
|
242
|
+
assignee_id: 'user-123',
|
243
|
+
status: 'pending'
|
244
|
+
)
|
245
|
+
|
246
|
+
# Get a specific task
|
247
|
+
task = client.tasks.get(id: 'task-123')
|
248
|
+
|
249
|
+
# Create a task
|
250
|
+
task = client.tasks.create(
|
251
|
+
parent_object: 'people',
|
252
|
+
parent_record_id: 'person-123',
|
253
|
+
title: 'Follow up with customer',
|
254
|
+
due_date: '2025-02-01',
|
255
|
+
assignee_id: 'user-456'
|
256
|
+
)
|
257
|
+
|
258
|
+
# Update a task
|
259
|
+
client.tasks.update(
|
260
|
+
id: 'task-123',
|
261
|
+
title: 'Updated task title',
|
262
|
+
status: 'in_progress'
|
263
|
+
)
|
264
|
+
|
265
|
+
# Complete a task
|
266
|
+
client.tasks.complete(id: 'task-123', completed_at: Time.now.iso8601)
|
267
|
+
|
268
|
+
# Reopen a task
|
269
|
+
client.tasks.reopen(id: 'task-123')
|
270
|
+
|
271
|
+
# Delete a task
|
272
|
+
client.tasks.delete(id: 'task-123')
|
273
|
+
```
|
274
|
+
|
275
|
+
#### Notes
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
# List notes on a record
|
279
|
+
notes = client.notes.list(
|
280
|
+
parent_object: 'companies',
|
281
|
+
parent_record_id: 'company-123'
|
282
|
+
)
|
283
|
+
|
284
|
+
# Get a specific note
|
285
|
+
note = client.notes.get(id: 'note-123')
|
286
|
+
|
287
|
+
# Create a note
|
288
|
+
note = client.notes.create(
|
289
|
+
parent_object: 'companies',
|
290
|
+
parent_record_id: 'company-123',
|
291
|
+
title: 'Meeting Notes - Q4 Planning',
|
292
|
+
content: 'Discussed roadmap and resource allocation...',
|
293
|
+
tags: ['important', 'quarterly-planning']
|
294
|
+
)
|
295
|
+
|
296
|
+
# Update a note
|
297
|
+
client.notes.update(
|
298
|
+
id: 'note-123',
|
299
|
+
title: 'Updated Meeting Notes',
|
300
|
+
content: 'Added action items from discussion'
|
301
|
+
)
|
302
|
+
|
303
|
+
# Delete a note
|
304
|
+
client.notes.delete(id: 'note-123')
|
305
|
+
```
|
306
|
+
|
157
307
|
#### Objects
|
158
308
|
|
159
309
|
```ruby
|
@@ -240,12 +390,16 @@ end
|
|
240
390
|
|
241
391
|
This client supports all major Attio API endpoints:
|
242
392
|
|
243
|
-
- ✅ Records (CRUD operations, querying)
|
393
|
+
- ✅ Records (CRUD operations, querying with filters and sorting)
|
244
394
|
- ✅ Objects (list, get schema)
|
245
|
-
- ✅ Lists (list, get entries)
|
395
|
+
- ✅ Lists (list, get entries, manage list entries)
|
246
396
|
- ✅ Workspaces (list, get current)
|
247
397
|
- ✅ Attributes (list, create, update)
|
248
398
|
- ✅ Users (list, get current user)
|
399
|
+
- ✅ Comments (CRUD operations, reactions on records and threads)
|
400
|
+
- ✅ Threads (CRUD operations, participant management, status control)
|
401
|
+
- ✅ Tasks (CRUD operations, assignment, completion tracking)
|
402
|
+
- ✅ Notes (CRUD operations on records)
|
249
403
|
|
250
404
|
## Development
|
251
405
|
|