attio 0.1.1 → 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/ci.yml +39 -15
- data/.github/workflows/coverage.yml +67 -0
- data/.github/workflows/pr_checks.yml +25 -7
- data/.github/workflows/release.yml +27 -13
- data/.github/workflows/tests.yml +67 -0
- data/.rubocop.yml +362 -90
- data/CHANGELOG.md +49 -1
- data/CONCEPTS.md +428 -0
- data/CONTRIBUTING.md +4 -4
- data/Gemfile +8 -5
- data/Gemfile.lock +4 -4
- data/README.md +164 -2
- data/Rakefile +8 -6
- data/attio.gemspec +6 -7
- data/danger/Dangerfile +22 -34
- data/docs/example.rb +30 -29
- 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 +67 -29
- data/lib/attio/connection_pool.rb +26 -14
- data/lib/attio/errors.rb +4 -2
- data/lib/attio/http_client.rb +70 -41
- data/lib/attio/logger.rb +37 -27
- data/lib/attio/resources/attributes.rb +12 -5
- data/lib/attio/resources/base.rb +66 -27
- data/lib/attio/resources/comments.rb +147 -0
- data/lib/attio/resources/lists.rb +21 -24
- data/lib/attio/resources/notes.rb +110 -0
- data/lib/attio/resources/objects.rb +11 -4
- data/lib/attio/resources/records.rb +49 -67
- data/lib/attio/resources/tasks.rb +131 -0
- data/lib/attio/resources/threads.rb +154 -0
- 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 +15 -9
- metadata +13 -18
- data/run_tests.rb +0 -52
- data/test_basic.rb +0 -51
- data/test_typhoeus.rb +0 -31
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/CONTRIBUTING.md
CHANGED
@@ -19,17 +19,17 @@ Thank you for your interest in contributing to the Attio Ruby client! This guide
|
|
19
19
|
1. Fork the repository on GitHub
|
20
20
|
2. Clone your fork locally:
|
21
21
|
```bash
|
22
|
-
git clone https://github.com/YOUR_USERNAME/attio
|
23
|
-
cd attio
|
22
|
+
git clone https://github.com/YOUR_USERNAME/attio.git
|
23
|
+
cd attio
|
24
24
|
```
|
25
25
|
3. Add the upstream remote:
|
26
26
|
```bash
|
27
|
-
git remote add upstream https://github.com/idl3/attio
|
27
|
+
git remote add upstream https://github.com/idl3/attio.git
|
28
28
|
```
|
29
29
|
|
30
30
|
## Development Setup
|
31
31
|
|
32
|
-
1. Install Ruby 3.0 or higher
|
32
|
+
1. Install Ruby 3.0 or higher (tested with Ruby 3.0 - 3.4)
|
33
33
|
2. Install dependencies:
|
34
34
|
```bash
|
35
35
|
bundle install
|
data/Gemfile
CHANGED
@@ -1,17 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in attio.gemspec
|
4
6
|
gemspec
|
5
7
|
|
6
|
-
gem "rake", "~> 13.0"
|
7
8
|
gem "logger" # Required for Ruby 3.5.0+ compatibility
|
9
|
+
gem "rake", "~> 13.0"
|
8
10
|
|
9
11
|
group :development, :test do
|
12
|
+
gem "pry", "~> 0.14"
|
10
13
|
gem "rspec", "~> 3.12"
|
11
|
-
gem "webmock", "~> 3.18"
|
12
|
-
gem "simplecov", "~> 0.22"
|
13
|
-
gem "simplecov-console", "~> 0.9"
|
14
14
|
gem "rubocop", "~> 1.50"
|
15
15
|
gem "rubocop-rspec", "~> 2.19"
|
16
|
-
gem "
|
16
|
+
gem "simplecov", "~> 0.22"
|
17
|
+
gem "simplecov-console", "~> 0.9"
|
18
|
+
gem "webmock", "~> 3.18"
|
19
|
+
gem "yard", "~> 0.9"
|
17
20
|
end
|
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
|
@@ -20,8 +20,8 @@ GEM
|
|
20
20
|
docile (1.4.1)
|
21
21
|
ethon (0.16.0)
|
22
22
|
ffi (>= 1.15.0)
|
23
|
-
ffi (1.17.2)
|
24
23
|
ffi (1.17.2-arm64-darwin)
|
24
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
25
25
|
hashdiff (1.2.0)
|
26
26
|
json (2.13.2)
|
27
27
|
language_server-protocol (3.17.0.5)
|
@@ -108,7 +108,7 @@ GEM
|
|
108
108
|
|
109
109
|
PLATFORMS
|
110
110
|
arm64-darwin-24
|
111
|
-
|
111
|
+
x86_64-linux
|
112
112
|
|
113
113
|
DEPENDENCIES
|
114
114
|
attio!
|
@@ -124,4 +124,4 @@ DEPENDENCIES
|
|
124
124
|
yard (~> 0.9)
|
125
125
|
|
126
126
|
BUNDLED WITH
|
127
|
-
2.
|
127
|
+
2.4.22
|
data/README.md
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
# Attio Ruby Client
|
2
2
|
|
3
|
+
[](https://github.com/idl3/attio/actions/workflows/tests.yml)
|
4
|
+
[](https://github.com/idl3/attio/tree/master/spec)
|
3
5
|
[](https://idl3.github.io/attio)
|
4
6
|
[](https://badge.fury.io/rb/attio)
|
7
|
+
[](https://github.com/idl3/attio/tree/master/spec)
|
5
8
|
|
6
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.
|
7
10
|
|
11
|
+
## Requirements
|
12
|
+
|
13
|
+
- Ruby 3.0 or higher (tested with Ruby 3.0, 3.1, 3.2, 3.3, and 3.4)
|
14
|
+
- Bundler
|
15
|
+
|
8
16
|
## Installation
|
9
17
|
|
10
18
|
Add this line to your application's Gemfile:
|
@@ -146,6 +154,156 @@ client.records.update(
|
|
146
154
|
|
147
155
|
### Working with Other Resources
|
148
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
|
+
|
149
307
|
#### Objects
|
150
308
|
|
151
309
|
```ruby
|
@@ -232,12 +390,16 @@ end
|
|
232
390
|
|
233
391
|
This client supports all major Attio API endpoints:
|
234
392
|
|
235
|
-
- ✅ Records (CRUD operations, querying)
|
393
|
+
- ✅ Records (CRUD operations, querying with filters and sorting)
|
236
394
|
- ✅ Objects (list, get schema)
|
237
|
-
- ✅ Lists (list, get entries)
|
395
|
+
- ✅ Lists (list, get entries, manage list entries)
|
238
396
|
- ✅ Workspaces (list, get current)
|
239
397
|
- ✅ Attributes (list, create, update)
|
240
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)
|
241
403
|
|
242
404
|
## Development
|
243
405
|
|