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
@@ -0,0 +1,348 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "attio"
|
5
|
+
require "time"
|
6
|
+
|
7
|
+
# Example: Complete CRM Workflow
|
8
|
+
#
|
9
|
+
# This example demonstrates a complete CRM workflow including:
|
10
|
+
# - Creating and managing companies and contacts
|
11
|
+
# - Working with lists and workspace management
|
12
|
+
# - Using collaboration features
|
13
|
+
# - Error handling
|
14
|
+
|
15
|
+
# Initialize the client
|
16
|
+
client = Attio.client(api_key: ENV.fetch("ATTIO_API_KEY"))
|
17
|
+
|
18
|
+
puts "Attio Complete CRM Workflow Example"
|
19
|
+
puts "=" * 40
|
20
|
+
|
21
|
+
# Error handling wrapper
|
22
|
+
def safe_execute(description)
|
23
|
+
print "#{description}..."
|
24
|
+
result = yield
|
25
|
+
puts " ✓"
|
26
|
+
result
|
27
|
+
rescue Attio::NotFoundError => e
|
28
|
+
puts " ✗ Not found: #{e.message}"
|
29
|
+
nil
|
30
|
+
rescue Attio::ValidationError => e
|
31
|
+
puts " ✗ Validation error: #{e.message}"
|
32
|
+
nil
|
33
|
+
rescue Attio::Error => e
|
34
|
+
puts " ✗ Error: #{e.message}"
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# 1. Get workspace information
|
39
|
+
puts "\n1. Workspace Information"
|
40
|
+
puts "-" * 20
|
41
|
+
workspace = safe_execute("Getting current workspace") do
|
42
|
+
client.workspaces.get
|
43
|
+
end
|
44
|
+
|
45
|
+
if workspace
|
46
|
+
puts " Workspace: #{workspace.dig('data', 'name')}"
|
47
|
+
puts " ID: #{workspace.dig('data', 'id', 'workspace_id')}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# 2. List available objects and attributes
|
51
|
+
puts "\n2. Schema Information"
|
52
|
+
puts "-" * 20
|
53
|
+
objects = safe_execute("Listing objects") do
|
54
|
+
client.objects.list
|
55
|
+
end
|
56
|
+
|
57
|
+
if objects && objects["data"]
|
58
|
+
puts " Available objects:"
|
59
|
+
objects["data"].each do |obj|
|
60
|
+
puts " - #{obj['api_slug']}: #{obj['name']}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# 3. Create a sales pipeline workflow
|
65
|
+
puts "\n3. Sales Pipeline Setup"
|
66
|
+
puts "-" * 20
|
67
|
+
|
68
|
+
# Create a company
|
69
|
+
company = safe_execute("Creating prospect company") do
|
70
|
+
client.records.create(
|
71
|
+
object: "companies",
|
72
|
+
data: {
|
73
|
+
name: "Innovation Labs Inc",
|
74
|
+
domain: "innovationlabs.example.com",
|
75
|
+
industry: "Technology",
|
76
|
+
description: "Potential enterprise customer",
|
77
|
+
}
|
78
|
+
)
|
79
|
+
end
|
80
|
+
company_id = company&.dig("data", "id", "record_id")
|
81
|
+
|
82
|
+
# Create primary contact
|
83
|
+
contact = safe_execute("Creating primary contact") do
|
84
|
+
client.records.create(
|
85
|
+
object: "people",
|
86
|
+
data: {
|
87
|
+
name: "Michael Chen",
|
88
|
+
email: "m.chen@innovationlabs.example.com",
|
89
|
+
title: "CTO",
|
90
|
+
phone: "+1-555-0123",
|
91
|
+
}
|
92
|
+
)
|
93
|
+
end
|
94
|
+
contact&.dig("data", "id", "record_id")
|
95
|
+
|
96
|
+
# Create additional contacts
|
97
|
+
if company_id
|
98
|
+
safe_execute("Creating additional contact") do
|
99
|
+
client.records.create(
|
100
|
+
object: "people",
|
101
|
+
data: {
|
102
|
+
name: "Lisa Park",
|
103
|
+
email: "l.park@innovationlabs.example.com",
|
104
|
+
title: "VP of Product",
|
105
|
+
}
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# 4. Create and manage lists
|
111
|
+
puts "\n4. List Management"
|
112
|
+
puts "-" * 20
|
113
|
+
|
114
|
+
lists = safe_execute("Getting available lists") do
|
115
|
+
client.lists.list
|
116
|
+
end
|
117
|
+
|
118
|
+
if lists && lists["data"] && !lists["data"].empty?
|
119
|
+
list_id = lists["data"].first["id"]["list_id"]
|
120
|
+
|
121
|
+
# Add company to a list
|
122
|
+
if company_id
|
123
|
+
safe_execute("Adding company to list") do
|
124
|
+
client.lists.create_entry(
|
125
|
+
id: list_id,
|
126
|
+
data: {
|
127
|
+
record_id: company_id,
|
128
|
+
notes: "High priority prospect",
|
129
|
+
}
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Get list entries
|
135
|
+
entries = safe_execute("Getting list entries") do
|
136
|
+
client.lists.entries(id: list_id, limit: 5)
|
137
|
+
end
|
138
|
+
|
139
|
+
puts " List has #{entries['data']&.length || 0} entries" if entries
|
140
|
+
end
|
141
|
+
|
142
|
+
# 5. Collaboration workflow
|
143
|
+
puts "\n5. Collaboration Workflow"
|
144
|
+
puts "-" * 20
|
145
|
+
|
146
|
+
if company_id
|
147
|
+
# Create a discussion thread
|
148
|
+
thread = safe_execute("Creating sales discussion thread") do
|
149
|
+
client.threads.create(
|
150
|
+
parent_object: "companies",
|
151
|
+
parent_record_id: company_id,
|
152
|
+
title: "Sales Opportunity - Innovation Labs",
|
153
|
+
description: "Track all sales activities and discussions"
|
154
|
+
)
|
155
|
+
end
|
156
|
+
thread_id = thread&.dig("data", "id", "thread_id")
|
157
|
+
|
158
|
+
# Add initial comment
|
159
|
+
if thread_id
|
160
|
+
safe_execute("Adding sales strategy comment") do
|
161
|
+
client.comments.create(
|
162
|
+
thread_id: thread_id,
|
163
|
+
content: <<~CONTENT
|
164
|
+
## Opportunity Overview
|
165
|
+
- **Company**: Innovation Labs Inc
|
166
|
+
- **Potential Value**: $100K ARR
|
167
|
+
- **Timeline**: Q1 2025
|
168
|
+
|
169
|
+
## Next Steps
|
170
|
+
1. Schedule discovery call
|
171
|
+
2. Prepare custom demo
|
172
|
+
3. Send pricing proposal
|
173
|
+
CONTENT
|
174
|
+
)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Create tasks
|
179
|
+
safe_execute("Creating discovery call task") do
|
180
|
+
client.tasks.create(
|
181
|
+
parent_object: "companies",
|
182
|
+
parent_record_id: company_id,
|
183
|
+
title: "Schedule discovery call with Michael Chen",
|
184
|
+
due_date: (Date.today + 3).iso8601,
|
185
|
+
description: "Initial discovery call to understand their requirements"
|
186
|
+
)
|
187
|
+
end
|
188
|
+
|
189
|
+
safe_execute("Creating demo prep task") do
|
190
|
+
client.tasks.create(
|
191
|
+
parent_object: "companies",
|
192
|
+
parent_record_id: company_id,
|
193
|
+
title: "Prepare customized product demo",
|
194
|
+
due_date: (Date.today + 7).iso8601
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Create meeting notes
|
199
|
+
safe_execute("Creating initial contact notes") do
|
200
|
+
client.notes.create(
|
201
|
+
parent_object: "companies",
|
202
|
+
parent_record_id: company_id,
|
203
|
+
title: "Initial Contact - #{Date.today}",
|
204
|
+
content: <<~CONTENT
|
205
|
+
## Contact Method
|
206
|
+
Inbound inquiry via website
|
207
|
+
|
208
|
+
## Requirements
|
209
|
+
- Looking for enterprise CRM solution
|
210
|
+
- Team size: 200+ employees
|
211
|
+
- Current solution: Spreadsheets
|
212
|
+
|
213
|
+
## Pain Points
|
214
|
+
- No centralized customer data
|
215
|
+
- Manual reporting processes
|
216
|
+
- Limited collaboration features
|
217
|
+
|
218
|
+
## Budget
|
219
|
+
Approved budget: $100-150K annually
|
220
|
+
|
221
|
+
## Decision Timeline
|
222
|
+
Looking to implement in Q1 2025
|
223
|
+
CONTENT
|
224
|
+
)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# 6. Query and reporting
|
229
|
+
puts "\n6. Queries and Reporting"
|
230
|
+
puts "-" * 20
|
231
|
+
|
232
|
+
# Get all high-priority tasks
|
233
|
+
tasks = safe_execute("Getting pending tasks") do
|
234
|
+
client.tasks.list(status: "pending", limit: 5)
|
235
|
+
end
|
236
|
+
|
237
|
+
if tasks
|
238
|
+
puts " Pending tasks: #{tasks['data']&.length || 0}"
|
239
|
+
tasks["data"]&.each do |task|
|
240
|
+
puts " - #{task['title']}"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Query recent companies
|
245
|
+
recent_companies = safe_execute("Finding recent companies") do
|
246
|
+
client.records.list(
|
247
|
+
object: "companies",
|
248
|
+
sorts: [{ field: "created_at", direction: "desc" }],
|
249
|
+
limit: 5
|
250
|
+
)
|
251
|
+
end
|
252
|
+
|
253
|
+
puts " Recent companies: #{recent_companies['data']&.length || 0}" if recent_companies
|
254
|
+
|
255
|
+
# 7. User management
|
256
|
+
puts "\n7. User Information"
|
257
|
+
puts "-" * 20
|
258
|
+
|
259
|
+
# Get current user
|
260
|
+
me = safe_execute("Getting current user") do
|
261
|
+
client.users.me
|
262
|
+
end
|
263
|
+
|
264
|
+
if me
|
265
|
+
puts " Current user: #{me.dig('data', 'name')}"
|
266
|
+
puts " Email: #{me.dig('data', 'email')}"
|
267
|
+
end
|
268
|
+
|
269
|
+
# List all users
|
270
|
+
users = safe_execute("Listing workspace users") do
|
271
|
+
client.users.list
|
272
|
+
end
|
273
|
+
|
274
|
+
puts " Total users: #{users['data']&.length || 0}" if users
|
275
|
+
|
276
|
+
# 8. Advanced features
|
277
|
+
puts "\n8. Advanced Features"
|
278
|
+
puts "-" * 20
|
279
|
+
|
280
|
+
# Get custom attributes
|
281
|
+
if company_id
|
282
|
+
attributes = safe_execute("Getting company attributes") do
|
283
|
+
client.attributes.list(object: "companies")
|
284
|
+
end
|
285
|
+
|
286
|
+
puts " Company attributes: #{attributes['data']&.length || 0}" if attributes
|
287
|
+
end
|
288
|
+
|
289
|
+
# Demonstrate pagination
|
290
|
+
puts " Demonstrating pagination..."
|
291
|
+
all_records = []
|
292
|
+
cursor = nil
|
293
|
+
pages = 0
|
294
|
+
|
295
|
+
loop do
|
296
|
+
params = { object: "companies", limit: 2 }
|
297
|
+
params[:cursor] = cursor if cursor
|
298
|
+
|
299
|
+
page = safe_execute(" Fetching page #{pages + 1}") do
|
300
|
+
client.records.list(**params)
|
301
|
+
end
|
302
|
+
|
303
|
+
break unless page && page["data"]
|
304
|
+
|
305
|
+
all_records.concat(page["data"])
|
306
|
+
pages += 1
|
307
|
+
|
308
|
+
cursor = page.dig("pagination", "next_cursor")
|
309
|
+
break unless cursor && pages < 3 # Limit to 3 pages for demo
|
310
|
+
end
|
311
|
+
|
312
|
+
puts " Fetched #{all_records.length} records across #{pages} pages"
|
313
|
+
|
314
|
+
# 9. Summary
|
315
|
+
puts "\n9. Workflow Summary"
|
316
|
+
puts "-" * 20
|
317
|
+
puts " ✓ Workspace configured"
|
318
|
+
puts " ✓ Company and contacts created"
|
319
|
+
puts " ✓ Lists managed"
|
320
|
+
puts " ✓ Collaboration tools utilized"
|
321
|
+
puts " ✓ Tasks and notes created"
|
322
|
+
puts " ✓ Queries executed"
|
323
|
+
puts " ✓ User information retrieved"
|
324
|
+
|
325
|
+
puts "\n#{'=' * 40}"
|
326
|
+
puts "Complete workflow example finished!"
|
327
|
+
puts "\nThis example demonstrated:"
|
328
|
+
puts " • Complete CRM setup"
|
329
|
+
puts " • Record creation and management"
|
330
|
+
puts " • List operations"
|
331
|
+
puts " • Collaboration features"
|
332
|
+
puts " • Task management"
|
333
|
+
puts " • Querying and reporting"
|
334
|
+
puts " • User management"
|
335
|
+
puts " • Error handling"
|
336
|
+
puts " • Pagination"
|
337
|
+
|
338
|
+
# Optional cleanup
|
339
|
+
# if company_id
|
340
|
+
# safe_execute("Cleaning up company") do
|
341
|
+
# client.records.delete(object: "companies", id: company_id)
|
342
|
+
# end
|
343
|
+
# end
|
344
|
+
# if contact_id
|
345
|
+
# safe_execute("Cleaning up contact") do
|
346
|
+
# client.records.delete(object: "people", id: contact_id)
|
347
|
+
# end
|
348
|
+
# end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "attio"
|
6
|
+
require "date"
|
7
|
+
|
8
|
+
# Example demonstrating Notes and Tasks functionality
|
9
|
+
#
|
10
|
+
# This example shows how to:
|
11
|
+
# - Create and manage notes on records
|
12
|
+
# - Create and manage tasks
|
13
|
+
# - Work with task assignments and due dates
|
14
|
+
# - Track task completion
|
15
|
+
|
16
|
+
client = Attio.client(api_key: ENV.fetch("ATTIO_API_KEY"))
|
17
|
+
|
18
|
+
puts "📓 Attio Notes and Tasks Example"
|
19
|
+
puts "=" * 50
|
20
|
+
|
21
|
+
begin
|
22
|
+
# First, find or create a person to work with
|
23
|
+
puts "\n👤 Setting up test person..."
|
24
|
+
test_person = client.records.create(
|
25
|
+
object: "people",
|
26
|
+
data: {
|
27
|
+
name: "Jane Smith",
|
28
|
+
email: "jane.smith@example.com",
|
29
|
+
title: "Product Manager",
|
30
|
+
}
|
31
|
+
)
|
32
|
+
person_id = test_person["data"]["id"]
|
33
|
+
puts " Created test person: Jane Smith (#{person_id})"
|
34
|
+
|
35
|
+
# Working with Notes
|
36
|
+
puts "\n📝 Creating Notes:"
|
37
|
+
|
38
|
+
# Create a meeting note
|
39
|
+
meeting_note = client.notes.create(
|
40
|
+
parent_object: "people",
|
41
|
+
parent_record_id: person_id,
|
42
|
+
title: "Initial Meeting - Product Requirements",
|
43
|
+
content: <<~MARKDOWN
|
44
|
+
## Meeting Summary
|
45
|
+
**Date:** #{Date.today}
|
46
|
+
**Attendees:** Jane Smith, Development Team
|
47
|
+
|
48
|
+
### Discussion Points:
|
49
|
+
1. **Q1 Product Roadmap**
|
50
|
+
- Feature A: User authentication improvements
|
51
|
+
- Feature B: New dashboard design
|
52
|
+
- Feature C: API v2 development
|
53
|
+
|
54
|
+
2. **Timeline**
|
55
|
+
- Sprint 1: Jan 15-29
|
56
|
+
- Sprint 2: Jan 30-Feb 12
|
57
|
+
- Release: Feb 15
|
58
|
+
|
59
|
+
3. **Action Items**
|
60
|
+
- [ ] Create detailed specs for Feature A
|
61
|
+
- [ ] Schedule design review for Feature B
|
62
|
+
- [ ] Allocate resources for API development
|
63
|
+
|
64
|
+
### Next Steps:
|
65
|
+
Follow-up meeting scheduled for next week.
|
66
|
+
MARKDOWN
|
67
|
+
)
|
68
|
+
puts " ✅ Created meeting note: #{meeting_note['data']['title']}"
|
69
|
+
|
70
|
+
# Create a follow-up note
|
71
|
+
followup_note = client.notes.create(
|
72
|
+
parent_object: "people",
|
73
|
+
parent_record_id: person_id,
|
74
|
+
title: "Follow-up: Action Items",
|
75
|
+
content: "Confirmed timeline and resource allocation. Jane will provide detailed specs by EOW."
|
76
|
+
)
|
77
|
+
puts " ✅ Created follow-up note"
|
78
|
+
|
79
|
+
# List all notes for the person
|
80
|
+
notes = client.notes.list(
|
81
|
+
parent_object: "people",
|
82
|
+
parent_record_id: person_id
|
83
|
+
)
|
84
|
+
puts "\n 📋 Notes for Jane Smith:"
|
85
|
+
notes["data"].each do |note|
|
86
|
+
puts " - #{note['title']} (created: #{note['created_at']})"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Working with Tasks
|
90
|
+
puts "\n✅ Creating Tasks:"
|
91
|
+
|
92
|
+
# Create tasks based on action items
|
93
|
+
task1 = client.tasks.create(
|
94
|
+
parent_object: "people",
|
95
|
+
parent_record_id: person_id,
|
96
|
+
title: "Create detailed specs for Feature A",
|
97
|
+
description: "Write comprehensive specifications for the user authentication improvements",
|
98
|
+
due_date: (Date.today + 7).iso8601,
|
99
|
+
priority: 1,
|
100
|
+
status: "pending"
|
101
|
+
)
|
102
|
+
puts " ✅ Created task: #{task1['data']['title']}"
|
103
|
+
|
104
|
+
task2 = client.tasks.create(
|
105
|
+
parent_object: "people",
|
106
|
+
parent_record_id: person_id,
|
107
|
+
title: "Schedule design review for Feature B",
|
108
|
+
description: "Coordinate with design team and schedule review meeting",
|
109
|
+
due_date: (Date.today + 3).iso8601,
|
110
|
+
priority: 2,
|
111
|
+
status: "pending"
|
112
|
+
)
|
113
|
+
puts " ✅ Created task: #{task2['data']['title']}"
|
114
|
+
|
115
|
+
task3 = client.tasks.create(
|
116
|
+
parent_object: "people",
|
117
|
+
parent_record_id: person_id,
|
118
|
+
title: "Allocate resources for API development",
|
119
|
+
description: "Determine team members and timeline for API v2",
|
120
|
+
due_date: (Date.today + 10).iso8601,
|
121
|
+
priority: 3,
|
122
|
+
status: "pending"
|
123
|
+
)
|
124
|
+
puts " ✅ Created task: #{task3['data']['title']}"
|
125
|
+
|
126
|
+
# List pending tasks
|
127
|
+
puts "\n 📋 Pending Tasks:"
|
128
|
+
pending_tasks = client.tasks.list(
|
129
|
+
status: "pending",
|
130
|
+
parent_object: "people",
|
131
|
+
parent_record_id: person_id
|
132
|
+
)
|
133
|
+
pending_tasks["data"].each do |task|
|
134
|
+
puts " - #{task['title']} (due: #{task['due_date']})"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Complete a task
|
138
|
+
puts "\n ✅ Completing task..."
|
139
|
+
completed_task = client.tasks.complete(
|
140
|
+
id: task2["data"]["id"],
|
141
|
+
completed_at: DateTime.now.iso8601
|
142
|
+
)
|
143
|
+
puts " Task completed: #{completed_task['data']['title']}"
|
144
|
+
|
145
|
+
# Update a task
|
146
|
+
puts "\n 📝 Updating task..."
|
147
|
+
updated_task = client.tasks.update(
|
148
|
+
id: task1["data"]["id"],
|
149
|
+
description: "Updated: Include security requirements in the specifications",
|
150
|
+
priority: 1
|
151
|
+
)
|
152
|
+
puts " Task updated: #{updated_task['data']['title']}"
|
153
|
+
|
154
|
+
# Get task details
|
155
|
+
puts "\n 🔍 Task Details:"
|
156
|
+
task_detail = client.tasks.get(id: task1["data"]["id"])
|
157
|
+
puts " Title: #{task_detail['data']['title']}"
|
158
|
+
puts " Description: #{task_detail['data']['description']}"
|
159
|
+
puts " Due Date: #{task_detail['data']['due_date']}"
|
160
|
+
puts " Status: #{task_detail['data']['status']}"
|
161
|
+
|
162
|
+
# Update a note
|
163
|
+
puts "\n 📝 Updating note..."
|
164
|
+
client.notes.update(
|
165
|
+
id: followup_note["data"]["id"],
|
166
|
+
content: "Updated: Specs delivered. Design review scheduled for Friday."
|
167
|
+
)
|
168
|
+
puts " Note updated successfully"
|
169
|
+
|
170
|
+
# Cleanup
|
171
|
+
puts "\n🧹 Cleaning up..."
|
172
|
+
|
173
|
+
# Delete tasks
|
174
|
+
[task1, task2, task3].each do |task|
|
175
|
+
client.tasks.delete(id: task["data"]["id"])
|
176
|
+
end
|
177
|
+
puts " ✅ Deleted tasks"
|
178
|
+
|
179
|
+
# Delete notes
|
180
|
+
[meeting_note, followup_note].each do |note|
|
181
|
+
client.notes.delete(id: note["data"]["id"])
|
182
|
+
end
|
183
|
+
puts " ✅ Deleted notes"
|
184
|
+
|
185
|
+
# Delete test person
|
186
|
+
client.records.delete(object: "people", id: person_id)
|
187
|
+
puts " ✅ Deleted test person"
|
188
|
+
rescue Attio::Error => e
|
189
|
+
puts "❌ Error: #{e.message}"
|
190
|
+
# Cleanup on error
|
191
|
+
if defined?(person_id)
|
192
|
+
begin
|
193
|
+
client.records.delete(object: "people", id: person_id)
|
194
|
+
rescue StandardError
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
puts "\n✨ Example completed!"
|
data/lib/attio/client.rb
CHANGED
@@ -116,5 +116,41 @@ module Attio
|
|
116
116
|
def users
|
117
117
|
@users ||= Resources::Users.new(self)
|
118
118
|
end
|
119
|
+
|
120
|
+
# Access to the Notes API resource.
|
121
|
+
#
|
122
|
+
# @return [Resources::Notes] Notes resource instance
|
123
|
+
# @example
|
124
|
+
# notes = client.notes.list(parent_object: 'people', parent_record_id: 'rec_123')
|
125
|
+
def notes
|
126
|
+
@notes ||= Resources::Notes.new(self)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Access to the Tasks API resource.
|
130
|
+
#
|
131
|
+
# @return [Resources::Tasks] Tasks resource instance
|
132
|
+
# @example
|
133
|
+
# tasks = client.tasks.list(status: 'pending')
|
134
|
+
def tasks
|
135
|
+
@tasks ||= Resources::Tasks.new(self)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Access to the Comments API resource.
|
139
|
+
#
|
140
|
+
# @return [Resources::Comments] Comments resource instance
|
141
|
+
# @example
|
142
|
+
# comments = client.comments.list(thread_id: 'thread_123')
|
143
|
+
def comments
|
144
|
+
@comments ||= Resources::Comments.new(self)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Access to the Threads API resource.
|
148
|
+
#
|
149
|
+
# @return [Resources::Threads] Threads resource instance
|
150
|
+
# @example
|
151
|
+
# threads = client.threads.list(parent_object: 'companies', parent_record_id: 'rec_456')
|
152
|
+
def threads
|
153
|
+
@threads ||= Resources::Threads.new(self)
|
154
|
+
end
|
119
155
|
end
|
120
156
|
end
|
data/lib/attio/http_client.rb
CHANGED
@@ -21,8 +21,12 @@ module Attio
|
|
21
21
|
@timeout = timeout
|
22
22
|
end
|
23
23
|
|
24
|
-
def get(path, params =
|
25
|
-
|
24
|
+
def get(path, params = nil)
|
25
|
+
if params && !params.empty?
|
26
|
+
execute_request(:get, path, params: params)
|
27
|
+
else
|
28
|
+
execute_request(:get, path)
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
def post(path, body = {})
|
@@ -37,8 +41,12 @@ module Attio
|
|
37
41
|
execute_request(:put, path, body: body.to_json)
|
38
42
|
end
|
39
43
|
|
40
|
-
def delete(path)
|
41
|
-
|
44
|
+
def delete(path, params = nil)
|
45
|
+
if params
|
46
|
+
execute_request(:delete, path, body: params.to_json)
|
47
|
+
else
|
48
|
+
execute_request(:delete, path)
|
49
|
+
end
|
42
50
|
end
|
43
51
|
|
44
52
|
private def execute_request(method, path, options = {})
|
data/lib/attio/resources/base.rb
CHANGED
@@ -24,22 +24,71 @@ module Attio
|
|
24
24
|
@client = client
|
25
25
|
end
|
26
26
|
|
27
|
+
# Common validation methods that can be used by all resource classes
|
28
|
+
|
29
|
+
# Validates that an ID parameter is present and not empty
|
30
|
+
# @param id [String] The ID to validate
|
31
|
+
# @param resource_name [String] The resource name for the error message
|
32
|
+
# @raise [ArgumentError] if id is nil or empty
|
33
|
+
private def validate_id!(id, resource_name = "Resource")
|
34
|
+
return unless id.nil? || id.to_s.strip.empty?
|
35
|
+
|
36
|
+
raise ArgumentError, "#{resource_name} ID is required"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validates that data is not empty
|
40
|
+
# @param data [Hash] The data to validate
|
41
|
+
# @param operation [String] The operation name for the error message
|
42
|
+
# @raise [ArgumentError] if data is empty
|
43
|
+
private def validate_data!(data, operation = "Operation")
|
44
|
+
raise ArgumentError, "#{operation} data is required" if data.nil? || data.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Validates that a string parameter is present and not empty
|
48
|
+
# @param value [String] The value to validate
|
49
|
+
# @param field_name [String] The field name for the error message
|
50
|
+
# @raise [ArgumentError] if value is nil or empty
|
51
|
+
private def validate_required_string!(value, field_name)
|
52
|
+
return unless value.nil? || value.to_s.strip.empty?
|
53
|
+
|
54
|
+
raise ArgumentError, "#{field_name} is required"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Validates parent object and record ID together
|
58
|
+
# @param parent_object [String] The parent object type
|
59
|
+
# @param parent_record_id [String] The parent record ID
|
60
|
+
# @raise [ArgumentError] if either is missing
|
61
|
+
private def validate_parent!(parent_object, parent_record_id)
|
62
|
+
validate_required_string!(parent_object, "Parent object")
|
63
|
+
validate_required_string!(parent_record_id, "Parent record ID")
|
64
|
+
end
|
65
|
+
|
27
66
|
private def request(method, path, params = {})
|
67
|
+
connection = client.connection
|
68
|
+
|
28
69
|
case method
|
29
70
|
when :get
|
30
|
-
|
71
|
+
handle_get_request(connection, path, params)
|
31
72
|
when :post
|
32
|
-
|
73
|
+
connection.post(path, params)
|
33
74
|
when :patch
|
34
|
-
|
75
|
+
connection.patch(path, params)
|
35
76
|
when :put
|
36
|
-
|
77
|
+
connection.put(path, params)
|
37
78
|
when :delete
|
38
|
-
|
79
|
+
handle_delete_request(connection, path, params)
|
39
80
|
else
|
40
81
|
raise ArgumentError, "Unsupported HTTP method: #{method}"
|
41
82
|
end
|
42
83
|
end
|
84
|
+
|
85
|
+
private def handle_get_request(connection, path, params)
|
86
|
+
params.empty? ? connection.get(path) : connection.get(path, params)
|
87
|
+
end
|
88
|
+
|
89
|
+
private def handle_delete_request(connection, path, params)
|
90
|
+
params.empty? ? connection.delete(path) : connection.delete(path, params)
|
91
|
+
end
|
43
92
|
end
|
44
93
|
end
|
45
94
|
end
|