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,178 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "attio"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
# Example: Advanced Filtering and Querying
|
8
|
+
#
|
9
|
+
# This example demonstrates how to use advanced filtering and sorting
|
10
|
+
# capabilities when querying records in Attio.
|
11
|
+
|
12
|
+
# Initialize the client
|
13
|
+
client = Attio.client(api_key: ENV.fetch("ATTIO_API_KEY"))
|
14
|
+
|
15
|
+
puts "Attio Advanced Filtering Example"
|
16
|
+
puts "=" * 40
|
17
|
+
|
18
|
+
# 1. Query with simple filters
|
19
|
+
puts "\n1. Finding people by email domain..."
|
20
|
+
people_from_domain = client.records.list(
|
21
|
+
object: "people",
|
22
|
+
filters: {
|
23
|
+
email: { contains: "@example.com" },
|
24
|
+
},
|
25
|
+
limit: 10
|
26
|
+
)
|
27
|
+
puts " Found #{people_from_domain['data']&.length || 0} people from example.com"
|
28
|
+
|
29
|
+
# 2. Query with multiple filters (AND condition)
|
30
|
+
puts "\n2. Finding high-value companies in technology sector..."
|
31
|
+
high_value_tech = client.records.list(
|
32
|
+
object: "companies",
|
33
|
+
filters: {
|
34
|
+
industry: { equals: "Technology" },
|
35
|
+
annual_revenue: { greater_than: 1_000_000 },
|
36
|
+
},
|
37
|
+
sorts: [
|
38
|
+
{ field: "annual_revenue", direction: "desc" },
|
39
|
+
],
|
40
|
+
limit: 5
|
41
|
+
)
|
42
|
+
puts " Found #{high_value_tech['data']&.length || 0} high-value tech companies"
|
43
|
+
|
44
|
+
# 3. Query with date range filters
|
45
|
+
puts "\n3. Finding recently created records..."
|
46
|
+
recent_date = (Date.today - 30).iso8601
|
47
|
+
recent_records = client.records.list(
|
48
|
+
object: "people",
|
49
|
+
filters: {
|
50
|
+
created_at: { greater_than: recent_date },
|
51
|
+
},
|
52
|
+
sorts: [
|
53
|
+
{ field: "created_at", direction: "desc" },
|
54
|
+
]
|
55
|
+
)
|
56
|
+
puts " Found #{recent_records['data']&.length || 0} people created in last 30 days"
|
57
|
+
|
58
|
+
# 4. Query with relationship filters
|
59
|
+
puts "\n4. Finding people associated with specific companies..."
|
60
|
+
# First, get a company
|
61
|
+
companies = client.records.list(object: "companies", limit: 1)
|
62
|
+
if companies.dig("data", 0)
|
63
|
+
company_id = companies.dig("data", 0, "id", "record_id")
|
64
|
+
|
65
|
+
people_at_company = client.records.list(
|
66
|
+
object: "people",
|
67
|
+
filters: {
|
68
|
+
company: {
|
69
|
+
target_object: "companies",
|
70
|
+
target_record_id: company_id,
|
71
|
+
},
|
72
|
+
}
|
73
|
+
)
|
74
|
+
puts " Found #{people_at_company['data']&.length || 0} people at the company"
|
75
|
+
else
|
76
|
+
puts " No companies found for demo"
|
77
|
+
end
|
78
|
+
|
79
|
+
# 5. Query with null/not null filters
|
80
|
+
puts "\n5. Finding records with missing data..."
|
81
|
+
missing_email = client.records.list(
|
82
|
+
object: "people",
|
83
|
+
filters: {
|
84
|
+
email: { is_null: true },
|
85
|
+
},
|
86
|
+
limit: 10
|
87
|
+
)
|
88
|
+
puts " Found #{missing_email['data']&.length || 0} people without email addresses"
|
89
|
+
|
90
|
+
# 6. Complex sorting with multiple fields
|
91
|
+
puts "\n6. Sorting by multiple criteria..."
|
92
|
+
sorted_companies = client.records.list(
|
93
|
+
object: "companies",
|
94
|
+
sorts: [
|
95
|
+
{ field: "industry", direction: "asc" },
|
96
|
+
{ field: "annual_revenue", direction: "desc" },
|
97
|
+
{ field: "name", direction: "asc" },
|
98
|
+
],
|
99
|
+
limit: 20
|
100
|
+
)
|
101
|
+
puts " Retrieved #{sorted_companies['data']&.length || 0} companies sorted by industry, revenue, and name"
|
102
|
+
|
103
|
+
# 7. Pagination example
|
104
|
+
puts "\n7. Paginating through results..."
|
105
|
+
page_size = 5
|
106
|
+
total_fetched = 0
|
107
|
+
cursor = nil
|
108
|
+
|
109
|
+
3.times do |page|
|
110
|
+
params = {
|
111
|
+
object: "people",
|
112
|
+
limit: page_size,
|
113
|
+
}
|
114
|
+
params[:cursor] = cursor if cursor
|
115
|
+
|
116
|
+
page_results = client.records.list(**params)
|
117
|
+
fetched = page_results["data"]&.length || 0
|
118
|
+
total_fetched += fetched
|
119
|
+
|
120
|
+
puts " Page #{page + 1}: fetched #{fetched} records"
|
121
|
+
|
122
|
+
cursor = page_results.dig("pagination", "next_cursor")
|
123
|
+
break unless cursor
|
124
|
+
end
|
125
|
+
puts " Total fetched across pages: #{total_fetched}"
|
126
|
+
|
127
|
+
# 8. Query tasks with status filter
|
128
|
+
puts "\n8. Finding pending tasks..."
|
129
|
+
pending_tasks = client.tasks.list(
|
130
|
+
status: "pending",
|
131
|
+
limit: 10
|
132
|
+
)
|
133
|
+
puts " Found #{pending_tasks['data']&.length || 0} pending tasks"
|
134
|
+
|
135
|
+
# 9. Query with custom field filters
|
136
|
+
puts "\n9. Filtering by custom fields..."
|
137
|
+
# This assumes you have custom fields set up
|
138
|
+
client.records.list(
|
139
|
+
object: "people",
|
140
|
+
filters: {
|
141
|
+
# Replace with your actual custom field API slug
|
142
|
+
# custom_field: { equals: "some_value" }
|
143
|
+
},
|
144
|
+
limit: 10
|
145
|
+
)
|
146
|
+
puts " Custom field filtering available for your specific schema"
|
147
|
+
|
148
|
+
# 10. Export query for analysis
|
149
|
+
puts "\n10. Exporting query results..."
|
150
|
+
export_data = client.records.list(
|
151
|
+
object: "companies",
|
152
|
+
filters: {
|
153
|
+
industry: { not_null: true },
|
154
|
+
},
|
155
|
+
limit: 100
|
156
|
+
)
|
157
|
+
|
158
|
+
if export_data["data"] && !export_data["data"].empty?
|
159
|
+
# Simple CSV-like export
|
160
|
+
puts " Sample export (first 3 records):"
|
161
|
+
export_data["data"].take(3).each do |record|
|
162
|
+
name = record.dig("values", "name", 0, "value") || "N/A"
|
163
|
+
industry = record.dig("values", "industry", 0, "value") || "N/A"
|
164
|
+
puts " - #{name}: #{industry}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
puts "\n#{'=' * 40}"
|
169
|
+
puts "Example completed successfully!"
|
170
|
+
puts "\nThis example demonstrated:"
|
171
|
+
puts " • Simple and complex filtering"
|
172
|
+
puts " • Multi-field sorting"
|
173
|
+
puts " • Date range queries"
|
174
|
+
puts " • Relationship filters"
|
175
|
+
puts " • Null/not-null checks"
|
176
|
+
puts " • Pagination"
|
177
|
+
puts " • Task filtering"
|
178
|
+
puts " • Custom field queries"
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "attio"
|
6
|
+
|
7
|
+
# Basic usage example for the Attio Ruby gem
|
8
|
+
#
|
9
|
+
# This example demonstrates:
|
10
|
+
# - Client initialization
|
11
|
+
# - Working with records (people, companies)
|
12
|
+
# - Creating relationships between records
|
13
|
+
# - Error handling
|
14
|
+
|
15
|
+
# Initialize the client with your API key
|
16
|
+
# You can get your API key from: https://app.attio.com/settings/api-keys
|
17
|
+
client = Attio.client(api_key: ENV.fetch("ATTIO_API_KEY"))
|
18
|
+
|
19
|
+
puts "🚀 Attio Ruby Client - Basic Usage Example"
|
20
|
+
puts "=" * 50
|
21
|
+
|
22
|
+
begin
|
23
|
+
# List all available objects in your workspace
|
24
|
+
puts "\n📋 Available Objects:"
|
25
|
+
objects = client.objects.list
|
26
|
+
objects["data"].each do |object|
|
27
|
+
puts " - #{object['name']} (#{object['id']})"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Working with People records
|
31
|
+
puts "\n👥 Working with People:"
|
32
|
+
|
33
|
+
# List existing people (first 5)
|
34
|
+
people = client.records.list(object: "people", limit: 5)
|
35
|
+
puts " Found #{people['data'].length} people records"
|
36
|
+
|
37
|
+
# Create a new person
|
38
|
+
new_person = client.records.create(
|
39
|
+
object: "people",
|
40
|
+
data: {
|
41
|
+
name: "John Doe",
|
42
|
+
email: "john.doe@example.com",
|
43
|
+
phone: "+1-555-0123",
|
44
|
+
title: "Software Engineer",
|
45
|
+
}
|
46
|
+
)
|
47
|
+
puts " ✅ Created person: #{new_person['data']['name']} (ID: #{new_person['data']['id']})"
|
48
|
+
|
49
|
+
# Update the person
|
50
|
+
updated_person = client.records.update(
|
51
|
+
object: "people",
|
52
|
+
id: new_person["data"]["id"],
|
53
|
+
data: { title: "Senior Software Engineer" }
|
54
|
+
)
|
55
|
+
puts " ✅ Updated title to: #{updated_person['data']['title']}"
|
56
|
+
|
57
|
+
# Working with Companies
|
58
|
+
puts "\n🏢 Working with Companies:"
|
59
|
+
|
60
|
+
# Create a company
|
61
|
+
new_company = client.records.create(
|
62
|
+
object: "companies",
|
63
|
+
data: {
|
64
|
+
name: "Acme Corp",
|
65
|
+
domain: "acme.com",
|
66
|
+
employee_count: 100,
|
67
|
+
description: "Leading provider of innovative solutions",
|
68
|
+
}
|
69
|
+
)
|
70
|
+
puts " ✅ Created company: #{new_company['data']['name']}"
|
71
|
+
|
72
|
+
# Link person to company (create relationship)
|
73
|
+
# Note: This requires the person and company to have a relationship field configured
|
74
|
+
puts "\n🔗 Creating Relationships:"
|
75
|
+
# This would typically be done through a reference field
|
76
|
+
# The exact implementation depends on your Attio workspace configuration
|
77
|
+
|
78
|
+
# Working with Lists
|
79
|
+
puts "\n📝 Working with Lists:"
|
80
|
+
lists = client.lists.list(limit: 5)
|
81
|
+
if lists["data"].any?
|
82
|
+
first_list = lists["data"].first
|
83
|
+
puts " Found list: #{first_list['name']}"
|
84
|
+
|
85
|
+
# Get entries in the list
|
86
|
+
entries = client.lists.entries(id: first_list["id"], limit: 5)
|
87
|
+
puts " List has #{entries['data'].length} entries"
|
88
|
+
else
|
89
|
+
puts " No lists found in workspace"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Cleanup - Delete the test records
|
93
|
+
puts "\n🧹 Cleaning up test data:"
|
94
|
+
client.records.delete(object: "people", id: new_person["data"]["id"])
|
95
|
+
puts " ✅ Deleted test person record"
|
96
|
+
|
97
|
+
client.records.delete(object: "companies", id: new_company["data"]["id"])
|
98
|
+
puts " ✅ Deleted test company record"
|
99
|
+
rescue Attio::AuthenticationError => e
|
100
|
+
puts "❌ Authentication failed: #{e.message}"
|
101
|
+
puts " Please check your API key"
|
102
|
+
rescue Attio::NotFoundError => e
|
103
|
+
puts "❌ Resource not found: #{e.message}"
|
104
|
+
rescue Attio::ValidationError => e
|
105
|
+
puts "❌ Validation error: #{e.message}"
|
106
|
+
rescue Attio::Error => e
|
107
|
+
puts "❌ API error: #{e.message}"
|
108
|
+
end
|
109
|
+
|
110
|
+
puts "\n✨ Example completed!"
|
@@ -0,0 +1,173 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "attio"
|
5
|
+
require "time"
|
6
|
+
|
7
|
+
# Example: Collaboration Features (Comments, Threads, Tasks, Notes)
|
8
|
+
#
|
9
|
+
# This example demonstrates how to use Attio's collaboration features
|
10
|
+
# to manage team communication and tasks on customer records.
|
11
|
+
|
12
|
+
# Initialize the client
|
13
|
+
client = Attio.client(api_key: ENV.fetch("ATTIO_API_KEY"))
|
14
|
+
|
15
|
+
puts "Attio Collaboration Features Example"
|
16
|
+
puts "=" * 40
|
17
|
+
|
18
|
+
# 1. Create a company and contact for our example
|
19
|
+
puts "\n1. Creating sample company and contact..."
|
20
|
+
company = client.records.create(
|
21
|
+
object: "companies",
|
22
|
+
data: {
|
23
|
+
name: "TechCorp Solutions",
|
24
|
+
domain: "techcorp.example.com",
|
25
|
+
industry: "Software Development",
|
26
|
+
}
|
27
|
+
)
|
28
|
+
puts " ✓ Created company: #{company.dig('data', 'values', 'name', 0, 'value')}"
|
29
|
+
|
30
|
+
contact = client.records.create(
|
31
|
+
object: "people",
|
32
|
+
data: {
|
33
|
+
name: "Sarah Johnson",
|
34
|
+
email: "sarah@techcorp.example.com",
|
35
|
+
title: "VP of Engineering",
|
36
|
+
}
|
37
|
+
)
|
38
|
+
puts " ✓ Created contact: #{contact.dig('data', 'values', 'name', 0, 'value')}"
|
39
|
+
|
40
|
+
company_id = company.dig("data", "id", "record_id")
|
41
|
+
contact_id = contact.dig("data", "id", "record_id")
|
42
|
+
|
43
|
+
# 2. Create a thread for discussion
|
44
|
+
puts "\n2. Creating a discussion thread..."
|
45
|
+
thread = client.threads.create(
|
46
|
+
parent_object: "companies",
|
47
|
+
parent_record_id: company_id,
|
48
|
+
title: "Q1 2025 Contract Renewal Discussion",
|
49
|
+
description: "Thread to track all discussions related to the Q1 2025 contract renewal"
|
50
|
+
)
|
51
|
+
thread_id = thread.dig("data", "id", "thread_id")
|
52
|
+
puts " ✓ Created thread: #{thread.dig('data', 'title')}"
|
53
|
+
|
54
|
+
# 3. Add comments to the thread
|
55
|
+
puts "\n3. Adding comments to the thread..."
|
56
|
+
client.comments.create(
|
57
|
+
thread_id: thread_id,
|
58
|
+
content: "Initial meeting scheduled for next Monday. Key topics:\n\n" \
|
59
|
+
"- Review current usage metrics\n" \
|
60
|
+
"- Discuss expansion opportunities\n" \
|
61
|
+
"- Address any concerns"
|
62
|
+
)
|
63
|
+
puts " ✓ Added initial comment"
|
64
|
+
|
65
|
+
comment2 = client.comments.create(
|
66
|
+
thread_id: thread_id,
|
67
|
+
content: "Sarah confirmed attendance. She mentioned interest in our new API features."
|
68
|
+
)
|
69
|
+
puts " ✓ Added follow-up comment"
|
70
|
+
|
71
|
+
# 4. React to a comment
|
72
|
+
puts "\n4. Adding reactions to comments..."
|
73
|
+
client.comments.react(id: comment2.dig("data", "id", "comment_id"), emoji: "👍")
|
74
|
+
puts " ✓ Added 👍 reaction"
|
75
|
+
|
76
|
+
# 5. Create tasks
|
77
|
+
puts "\n5. Creating tasks..."
|
78
|
+
task1 = client.tasks.create(
|
79
|
+
parent_object: "companies",
|
80
|
+
parent_record_id: company_id,
|
81
|
+
title: "Prepare renewal proposal",
|
82
|
+
due_date: (Date.today + 7).iso8601,
|
83
|
+
description: "Create comprehensive renewal proposal including pricing and new features"
|
84
|
+
)
|
85
|
+
puts " ✓ Created task: #{task1.dig('data', 'title')}"
|
86
|
+
|
87
|
+
task2 = client.tasks.create(
|
88
|
+
parent_object: "people",
|
89
|
+
parent_record_id: contact_id,
|
90
|
+
title: "Schedule follow-up call",
|
91
|
+
due_date: (Date.today + 14).iso8601
|
92
|
+
)
|
93
|
+
puts " ✓ Created task: #{task2.dig('data', 'title')}"
|
94
|
+
|
95
|
+
# 6. Create meeting notes
|
96
|
+
puts "\n6. Creating meeting notes..."
|
97
|
+
client.notes.create(
|
98
|
+
parent_object: "companies",
|
99
|
+
parent_record_id: company_id,
|
100
|
+
title: "Contract Renewal Meeting - #{Date.today}",
|
101
|
+
content: <<~CONTENT
|
102
|
+
## Attendees
|
103
|
+
- Sarah Johnson (TechCorp)
|
104
|
+
- Our team: Sales, Customer Success
|
105
|
+
|
106
|
+
## Key Points Discussed
|
107
|
+
1. Current contract value: $50,000/year
|
108
|
+
2. Usage has grown 40% in last quarter
|
109
|
+
3. Interest in API expansion package
|
110
|
+
|
111
|
+
## Action Items
|
112
|
+
- [ ] Send updated pricing proposal by EOW
|
113
|
+
- [ ] Schedule technical demo for API features
|
114
|
+
- [ ] Review SLA requirements
|
115
|
+
|
116
|
+
## Next Steps
|
117
|
+
Follow-up call scheduled for next week
|
118
|
+
CONTENT
|
119
|
+
)
|
120
|
+
puts " ✓ Created meeting notes"
|
121
|
+
|
122
|
+
# 7. List all collaboration items
|
123
|
+
puts "\n7. Listing collaboration items..."
|
124
|
+
|
125
|
+
# List threads
|
126
|
+
threads = client.threads.list(
|
127
|
+
parent_object: "companies",
|
128
|
+
parent_record_id: company_id
|
129
|
+
)
|
130
|
+
puts " Threads on company: #{threads['data']&.length || 0}"
|
131
|
+
|
132
|
+
# List comments in thread
|
133
|
+
comments = client.comments.list(thread_id: thread_id)
|
134
|
+
puts " Comments in thread: #{comments['data']&.length || 0}"
|
135
|
+
|
136
|
+
# List tasks
|
137
|
+
all_tasks = client.tasks.list
|
138
|
+
puts " Total tasks: #{all_tasks['data']&.length || 0}"
|
139
|
+
|
140
|
+
# List notes
|
141
|
+
notes = client.notes.list(
|
142
|
+
parent_object: "companies",
|
143
|
+
parent_record_id: company_id
|
144
|
+
)
|
145
|
+
puts " Notes on company: #{notes['data']&.length || 0}"
|
146
|
+
|
147
|
+
# 8. Update task status
|
148
|
+
puts "\n8. Updating task status..."
|
149
|
+
client.tasks.complete(
|
150
|
+
id: task1.dig("data", "id", "task_id"),
|
151
|
+
completed_at: Time.now.iso8601
|
152
|
+
)
|
153
|
+
puts " ✓ Marked task as complete"
|
154
|
+
|
155
|
+
# 9. Close the thread
|
156
|
+
puts "\n9. Closing the discussion thread..."
|
157
|
+
client.threads.close(id: thread_id)
|
158
|
+
puts " ✓ Thread closed"
|
159
|
+
|
160
|
+
puts "\n#{'=' * 40}"
|
161
|
+
puts "Example completed successfully!"
|
162
|
+
puts "\nThis example demonstrated:"
|
163
|
+
puts " • Creating and managing discussion threads"
|
164
|
+
puts " • Adding comments and reactions"
|
165
|
+
puts " • Creating and completing tasks"
|
166
|
+
puts " • Creating detailed meeting notes"
|
167
|
+
puts " • Listing collaboration items"
|
168
|
+
|
169
|
+
# Clean up (optional - uncomment to delete created records)
|
170
|
+
# puts "\nCleaning up..."
|
171
|
+
# client.records.delete(object: "companies", id: company_id)
|
172
|
+
# client.records.delete(object: "people", id: contact_id)
|
173
|
+
# puts " ✓ Cleanup complete"
|