attio 0.1.3 → 0.3.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.
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attio
4
+ module Resources
5
+ # Meta resource for API metadata and identification
6
+ #
7
+ # @example Identify the current API key
8
+ # client.meta.identify
9
+ # # => { "workspace" => { "id" => "...", "name" => "..." }, "user" => { ... } }
10
+ #
11
+ # @example Get API status
12
+ # client.meta.status
13
+ # # => { "status" => "operational", "version" => "v2" }
14
+ class Meta < Base
15
+ # Identify the current API key and get workspace/user information
16
+ #
17
+ # @return [Hash] Information about the authenticated workspace and user
18
+ def identify
19
+ request(:get, "meta/identify")
20
+ end
21
+
22
+ # Get API status and version information
23
+ #
24
+ # @return [Hash] API status and version details
25
+ def status
26
+ request(:get, "meta/status")
27
+ end
28
+
29
+ # Get rate limit information for the current API key
30
+ #
31
+ # @return [Hash] Current rate limit status
32
+ def rate_limits
33
+ request(:get, "meta/rate_limits")
34
+ end
35
+
36
+ # Get workspace configuration and settings
37
+ #
38
+ # @return [Hash] Workspace configuration details
39
+ def workspace_config
40
+ request(:get, "meta/workspace_config")
41
+ end
42
+
43
+ # Validate an API key without making changes
44
+ #
45
+ # @return [Hash] Validation result with key permissions
46
+ def validate_key
47
+ request(:post, "meta/validate", {})
48
+ end
49
+
50
+ # Get available API endpoints and their documentation
51
+ #
52
+ # @return [Hash] List of available endpoints with descriptions
53
+ def endpoints
54
+ request(:get, "meta/endpoints")
55
+ end
56
+
57
+ # Get workspace usage statistics
58
+ #
59
+ # @return [Hash] Usage statistics including record counts, API calls, etc.
60
+ def usage_stats
61
+ request(:get, "meta/usage")
62
+ end
63
+
64
+ # Get feature flags and capabilities for the workspace
65
+ #
66
+ # @return [Hash] Enabled features and capabilities
67
+ def features
68
+ request(:get, "meta/features")
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attio
4
+ module Resources
5
+ # API resource for managing notes in Attio
6
+ #
7
+ # Notes can be attached to records to track important information,
8
+ # meeting notes, or any other textual data.
9
+ #
10
+ # @example Creating a note on a person
11
+ # client.notes.create(
12
+ # parent_object: "people",
13
+ # parent_record_id: "person_123",
14
+ # title: "Meeting Notes",
15
+ # content: "Discussed Q4 goals..."
16
+ # )
17
+ #
18
+ # @example Listing notes for a record
19
+ # client.notes.list(
20
+ # parent_object: "companies",
21
+ # parent_record_id: "company_456"
22
+ # )
23
+ class Notes < Base
24
+ # List notes for a specific parent record
25
+ #
26
+ # @param parent_object [String] The parent object type (e.g., "people", "companies")
27
+ # @param parent_record_id [String] The ID of the parent record
28
+ # @param params [Hash] Additional query parameters
29
+ # @option params [Integer] :limit Number of notes to return
30
+ # @option params [String] :cursor Pagination cursor
31
+ #
32
+ # @return [Hash] API response containing notes
33
+ # @raise [ArgumentError] if parent_object or parent_record_id is nil
34
+ def list(parent_object:, parent_record_id:, **params)
35
+ validate_parent!(parent_object, parent_record_id)
36
+ request(:get, "notes", params.merge(
37
+ parent_object: parent_object,
38
+ parent_record_id: parent_record_id
39
+ ))
40
+ end
41
+
42
+ # Get a specific note by ID
43
+ #
44
+ # @param id [String] The note ID
45
+ #
46
+ # @return [Hash] The note data
47
+ # @raise [ArgumentError] if id is nil or empty
48
+ def get(id:)
49
+ validate_id!(id, "Note")
50
+ request(:get, "notes/#{id}")
51
+ end
52
+
53
+ # Create a new note
54
+ #
55
+ # @param parent_object [String] The parent object type
56
+ # @param parent_record_id [String] The ID of the parent record
57
+ # @param title [String] The note title
58
+ # @param content [String] The note content (supports markdown)
59
+ # @param data [Hash] Additional note data
60
+ #
61
+ # @return [Hash] The created note
62
+ # @raise [ArgumentError] if required parameters are missing
63
+ def create(parent_object:, parent_record_id:, title:, content:, **data)
64
+ validate_parent!(parent_object, parent_record_id)
65
+ validate_required_string!(title, "Note title")
66
+ validate_required_string!(content, "Note content")
67
+
68
+ request(:post, "notes", data.merge(
69
+ parent_object: parent_object,
70
+ parent_record_id: parent_record_id,
71
+ title: title,
72
+ content: content
73
+ ))
74
+ end
75
+
76
+ # Update an existing note
77
+ #
78
+ # @param id [String] The note ID
79
+ # @param data [Hash] The fields to update
80
+ # @option data [String] :title New title
81
+ # @option data [String] :content New content
82
+ #
83
+ # @return [Hash] The updated note
84
+ # @raise [ArgumentError] if id or data is invalid
85
+ def update(id:, **data)
86
+ validate_id!(id, "Note")
87
+ validate_note_update_data!(data)
88
+ request(:patch, "notes/#{id}", data)
89
+ end
90
+
91
+ # Delete a note
92
+ #
93
+ # @param id [String] The note ID to delete
94
+ #
95
+ # @return [Hash] Deletion confirmation
96
+ # @raise [ArgumentError] if id is nil or empty
97
+ def delete(id:)
98
+ validate_id!(id, "Note")
99
+ request(:delete, "notes/#{id}")
100
+ end
101
+
102
+ private def validate_note_update_data!(data)
103
+ raise ArgumentError, "Update data is required" if data.empty?
104
+ return if data.key?(:title) || data.key?(:content)
105
+
106
+ raise ArgumentError, "Must provide title or content to update"
107
+ end
108
+ end
109
+ end
110
+ end
@@ -45,7 +45,7 @@ module Attio
45
45
  # limit: 50
46
46
  # )
47
47
  def list(object:, **params)
48
- validate_object!(object)
48
+ validate_required_string!(object, "Object type")
49
49
  request(:post, "objects/#{object}/records/query", params)
50
50
  end
51
51
 
@@ -60,8 +60,8 @@ module Attio
60
60
  # @example
61
61
  # record = client.records.get(object: 'people', id: 'abc123')
62
62
  def get(object:, id:)
63
- validate_object!(object)
64
- validate_id!(id)
63
+ validate_required_string!(object, "Object type")
64
+ validate_id!(id, "Record")
65
65
  request(:get, "objects/#{object}/records/#{id}")
66
66
  end
67
67
 
@@ -83,8 +83,8 @@ module Attio
83
83
  # }
84
84
  # )
85
85
  def create(object:, data:)
86
- validate_object!(object)
87
- validate_data!(data)
86
+ validate_required_string!(object, "Object type")
87
+ validate_record_data!(data)
88
88
  request(:post, "objects/#{object}/records", data)
89
89
  end
90
90
 
@@ -104,9 +104,9 @@ module Attio
104
104
  # data: { name: 'Jane Smith' }
105
105
  # )
106
106
  def update(object:, id:, data:)
107
- validate_object!(object)
108
- validate_id!(id)
109
- validate_data!(data)
107
+ validate_required_string!(object, "Object type")
108
+ validate_id!(id, "Record")
109
+ validate_record_data!(data)
110
110
  request(:patch, "objects/#{object}/records/#{id}", data)
111
111
  end
112
112
 
@@ -121,30 +121,17 @@ module Attio
121
121
  # @example
122
122
  # client.records.delete(object: 'people', id: 'abc123')
123
123
  def delete(object:, id:)
124
- validate_object!(object)
125
- validate_id!(id)
124
+ validate_required_string!(object, "Object type")
125
+ validate_id!(id, "Record")
126
126
  request(:delete, "objects/#{object}/records/#{id}")
127
127
  end
128
128
 
129
- private def validate_object!(object)
130
- raise ArgumentError, "Object type is required" if object.nil? || object.to_s.strip.empty?
131
- end
132
-
133
- # Validates that the ID parameter is present and not empty.
134
- #
135
- # @param id [String, nil] The record ID to validate
136
- # @raise [ArgumentError] if id is nil or empty
137
- # @api private
138
- private def validate_id!(id)
139
- raise ArgumentError, "Record ID is required" if id.nil? || id.to_s.strip.empty?
140
- end
141
-
142
129
  # Validates that the data parameter is present and is a hash.
143
130
  #
144
131
  # @param data [Hash, nil] The data to validate
145
132
  # @raise [ArgumentError] if data is nil or not a hash
146
133
  # @api private
147
- private def validate_data!(data)
134
+ private def validate_record_data!(data)
148
135
  raise ArgumentError, "Data is required" if data.nil?
149
136
  raise ArgumentError, "Data must be a hash" unless data.is_a?(Hash)
150
137
  end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attio
4
+ module Resources
5
+ # API resource for managing tasks in Attio
6
+ #
7
+ # Tasks help track action items and to-dos associated with records.
8
+ #
9
+ # @example Creating a task
10
+ # client.tasks.create(
11
+ # parent_object: "people",
12
+ # parent_record_id: "person_123",
13
+ # title: "Follow up on proposal",
14
+ # due_date: "2025-02-01",
15
+ # assignee_id: "user_456"
16
+ # )
17
+ #
18
+ # @example Listing tasks with filters
19
+ # client.tasks.list(
20
+ # status: "pending",
21
+ # assignee_id: "user_456"
22
+ # )
23
+ class Tasks < Base
24
+ # List tasks with optional filters
25
+ #
26
+ # @param params [Hash] Query parameters
27
+ # @option params [String] :parent_object Filter by parent object type
28
+ # @option params [String] :parent_record_id Filter by parent record
29
+ # @option params [String] :status Filter by status (pending, completed)
30
+ # @option params [String] :assignee_id Filter by assignee
31
+ # @option params [Integer] :limit Number of tasks to return
32
+ # @option params [String] :cursor Pagination cursor
33
+ #
34
+ # @return [Hash] API response containing tasks
35
+ def list(**params)
36
+ request(:get, "tasks", params)
37
+ end
38
+
39
+ # Get a specific task by ID
40
+ #
41
+ # @param id [String] The task ID
42
+ #
43
+ # @return [Hash] The task data
44
+ # @raise [ArgumentError] if id is nil or empty
45
+ def get(id:)
46
+ validate_id!(id, "Task")
47
+ request(:get, "tasks/#{id}")
48
+ end
49
+
50
+ # Create a new task
51
+ #
52
+ # @param parent_object [String] The parent object type
53
+ # @param parent_record_id [String] The ID of the parent record
54
+ # @param title [String] The task title
55
+ # @param data [Hash] Additional task data
56
+ # @option data [String] :description Task description
57
+ # @option data [String] :due_date Due date (ISO 8601 format)
58
+ # @option data [String] :assignee_id User ID to assign the task to
59
+ # @option data [String] :status Task status (pending, completed)
60
+ # @option data [Integer] :priority Task priority (1-5)
61
+ #
62
+ # @return [Hash] The created task
63
+ # @raise [ArgumentError] if required parameters are missing
64
+ def create(parent_object:, parent_record_id:, title:, **data)
65
+ validate_parent!(parent_object, parent_record_id)
66
+ validate_required_string!(title, "Task title")
67
+
68
+ request(:post, "tasks", data.merge(
69
+ parent_object: parent_object,
70
+ parent_record_id: parent_record_id,
71
+ title: title
72
+ ))
73
+ end
74
+
75
+ # Update an existing task
76
+ #
77
+ # @param id [String] The task ID
78
+ # @param data [Hash] The fields to update
79
+ # @option data [String] :title New title
80
+ # @option data [String] :description New description
81
+ # @option data [String] :due_date New due date
82
+ # @option data [String] :assignee_id New assignee
83
+ # @option data [String] :status New status
84
+ # @option data [Integer] :priority New priority
85
+ #
86
+ # @return [Hash] The updated task
87
+ # @raise [ArgumentError] if id is invalid
88
+ def update(id:, **data)
89
+ validate_id!(id, "Task")
90
+ validate_data!(data, "Update")
91
+ request(:patch, "tasks/#{id}", data)
92
+ end
93
+
94
+ # Mark a task as completed
95
+ #
96
+ # @param id [String] The task ID
97
+ # @param completed_at [String] Optional completion timestamp (defaults to now)
98
+ #
99
+ # @return [Hash] The updated task
100
+ # @raise [ArgumentError] if id is nil or empty
101
+ def complete(id:, completed_at: nil)
102
+ validate_id!(id, "Task")
103
+ data = { status: "completed" }
104
+ data[:completed_at] = completed_at if completed_at
105
+ request(:patch, "tasks/#{id}", data)
106
+ end
107
+
108
+ # Reopen a completed task
109
+ #
110
+ # @param id [String] The task ID
111
+ #
112
+ # @return [Hash] The updated task
113
+ # @raise [ArgumentError] if id is nil or empty
114
+ def reopen(id:)
115
+ validate_id!(id, "Task")
116
+ request(:patch, "tasks/#{id}", { status: "pending", completed_at: nil })
117
+ end
118
+
119
+ # Delete a task
120
+ #
121
+ # @param id [String] The task ID to delete
122
+ #
123
+ # @return [Hash] Deletion confirmation
124
+ # @raise [ArgumentError] if id is nil or empty
125
+ def delete(id:)
126
+ validate_id!(id, "Task")
127
+ request(:delete, "tasks/#{id}")
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attio
4
+ module Resources
5
+ # API resource for managing threads in Attio
6
+ #
7
+ # Threads represent conversations or discussion topics related to records.
8
+ #
9
+ # @example Listing threads for a record
10
+ # client.threads.list(
11
+ # parent_object: "companies",
12
+ # parent_record_id: "company_123"
13
+ # )
14
+ #
15
+ # @example Getting a specific thread
16
+ # client.threads.get(id: "thread_456")
17
+ class Threads < Base
18
+ # List threads for a parent record
19
+ #
20
+ # @param parent_object [String] The parent object type
21
+ # @param parent_record_id [String] The parent record ID
22
+ # @param params [Hash] Additional query parameters
23
+ # @option params [Integer] :limit Number of threads to return
24
+ # @option params [String] :cursor Pagination cursor
25
+ # @option params [String] :status Filter by status (open, closed)
26
+ #
27
+ # @return [Hash] API response containing threads
28
+ # @raise [ArgumentError] if parent_object or parent_record_id is missing
29
+ def list(parent_object:, parent_record_id:, **params)
30
+ validate_parent!(parent_object, parent_record_id)
31
+ request(:get, "threads", params.merge(
32
+ parent_object: parent_object,
33
+ parent_record_id: parent_record_id
34
+ ))
35
+ end
36
+
37
+ # Get a specific thread by ID
38
+ #
39
+ # @param id [String] The thread ID
40
+ # @param include_comments [Boolean] Whether to include comments in the response
41
+ #
42
+ # @return [Hash] The thread data
43
+ # @raise [ArgumentError] if id is nil or empty
44
+ def get(id:, include_comments: false)
45
+ validate_id!(id, "Thread")
46
+ params = include_comments ? { include: "comments" } : {}
47
+ request(:get, "threads/#{id}", params)
48
+ end
49
+
50
+ # Create a new thread
51
+ #
52
+ # @param parent_object [String] The parent object type
53
+ # @param parent_record_id [String] The parent record ID
54
+ # @param title [String] The thread title
55
+ # @param data [Hash] Additional thread data
56
+ # @option data [String] :description Thread description
57
+ # @option data [String] :status Thread status (open, closed)
58
+ # @option data [Array<String>] :participant_ids User IDs of participants
59
+ #
60
+ # @return [Hash] The created thread
61
+ # @raise [ArgumentError] if required parameters are missing
62
+ def create(parent_object:, parent_record_id:, title:, **data)
63
+ validate_parent!(parent_object, parent_record_id)
64
+ validate_required_string!(title, "Thread title")
65
+
66
+ request(:post, "threads", data.merge(
67
+ parent_object: parent_object,
68
+ parent_record_id: parent_record_id,
69
+ title: title
70
+ ))
71
+ end
72
+
73
+ # Update an existing thread
74
+ #
75
+ # @param id [String] The thread ID
76
+ # @param data [Hash] The fields to update
77
+ # @option data [String] :title New title
78
+ # @option data [String] :description New description
79
+ # @option data [String] :status New status (open, closed)
80
+ #
81
+ # @return [Hash] The updated thread
82
+ # @raise [ArgumentError] if id is invalid
83
+ def update(id:, **data)
84
+ validate_id!(id, "Thread")
85
+ validate_data!(data, "Update")
86
+ request(:patch, "threads/#{id}", data)
87
+ end
88
+
89
+ # Close a thread
90
+ #
91
+ # @param id [String] The thread ID
92
+ #
93
+ # @return [Hash] The updated thread
94
+ # @raise [ArgumentError] if id is nil or empty
95
+ def close(id:)
96
+ validate_id!(id, "Thread")
97
+ request(:patch, "threads/#{id}", { status: "closed" })
98
+ end
99
+
100
+ # Reopen a closed thread
101
+ #
102
+ # @param id [String] The thread ID
103
+ #
104
+ # @return [Hash] The updated thread
105
+ # @raise [ArgumentError] if id is nil or empty
106
+ def reopen(id:)
107
+ validate_id!(id, "Thread")
108
+ request(:patch, "threads/#{id}", { status: "open" })
109
+ end
110
+
111
+ # Delete a thread
112
+ #
113
+ # @param id [String] The thread ID to delete
114
+ #
115
+ # @return [Hash] Deletion confirmation
116
+ # @raise [ArgumentError] if id is nil or empty
117
+ def delete(id:)
118
+ validate_id!(id, "Thread")
119
+ request(:delete, "threads/#{id}")
120
+ end
121
+
122
+ # Add participants to a thread
123
+ #
124
+ # @param id [String] The thread ID
125
+ # @param user_ids [Array<String>] User IDs to add as participants
126
+ #
127
+ # @return [Hash] The updated thread
128
+ # @raise [ArgumentError] if id or user_ids is invalid
129
+ def add_participants(id:, user_ids:)
130
+ validate_id!(id, "Thread")
131
+ validate_user_ids!(user_ids)
132
+ request(:post, "threads/#{id}/participants", { user_ids: user_ids })
133
+ end
134
+
135
+ # Remove participants from a thread
136
+ #
137
+ # @param id [String] The thread ID
138
+ # @param user_ids [Array<String>] User IDs to remove
139
+ #
140
+ # @return [Hash] The updated thread
141
+ # @raise [ArgumentError] if id or user_ids is invalid
142
+ def remove_participants(id:, user_ids:)
143
+ validate_id!(id, "Thread")
144
+ validate_user_ids!(user_ids)
145
+ request(:delete, "threads/#{id}/participants", { user_ids: user_ids })
146
+ end
147
+
148
+ private def validate_user_ids!(user_ids)
149
+ raise ArgumentError, "User IDs are required" if user_ids.nil? || user_ids.empty?
150
+ raise ArgumentError, "User IDs must be an array" unless user_ids.is_a?(Array)
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attio
4
+ module Resources
5
+ # Workspace Members resource for managing workspace member access
6
+ #
7
+ # @example List all workspace members
8
+ # client.workspace_members.list
9
+ #
10
+ # @example Get a specific member
11
+ # client.workspace_members.get(member_id: "user_123")
12
+ #
13
+ # @example Invite a new member
14
+ # client.workspace_members.invite(
15
+ # email: "new.member@example.com",
16
+ # role: "member"
17
+ # )
18
+ class WorkspaceMembers < Base
19
+ # List all workspace members
20
+ #
21
+ # @param params [Hash] Optional query parameters
22
+ # @option params [Integer] :limit Maximum number of results
23
+ # @option params [String] :offset Pagination offset
24
+ # @return [Hash] The API response
25
+ def list(params = {})
26
+ request(:get, "workspace_members", params)
27
+ end
28
+
29
+ # Get a specific workspace member
30
+ #
31
+ # @param member_id [String] The member ID
32
+ # @return [Hash] The member data
33
+ def get(member_id:)
34
+ validate_id!(member_id, "Member")
35
+ request(:get, "workspace_members/#{member_id}")
36
+ end
37
+
38
+ # Invite a new member to the workspace
39
+ #
40
+ # @param email [String] The email address to invite
41
+ # @param role [String] The role to assign (admin, member, guest)
42
+ # @param data [Hash] Additional member data
43
+ # @return [Hash] The created invitation
44
+ def invite(email:, role: "member", data: {})
45
+ validate_required_string!(email, "Email")
46
+ validate_required_string!(role, "Role")
47
+
48
+ raise ArgumentError, "Role must be one of: admin, member, guest" unless %w[admin member guest].include?(role)
49
+
50
+ body = data.merge(email: email, role: role)
51
+ request(:post, "workspace_members/invitations", body)
52
+ end
53
+
54
+ # Update a workspace member's role or permissions
55
+ #
56
+ # @param member_id [String] The member ID to update
57
+ # @param data [Hash] The data to update
58
+ # @option data [String] :role The new role
59
+ # @option data [Hash] :permissions Custom permissions
60
+ # @return [Hash] The updated member
61
+ def update(member_id:, data:)
62
+ validate_id!(member_id, "Member")
63
+ validate_required_hash!(data, "Data")
64
+
65
+ request(:patch, "workspace_members/#{member_id}", data)
66
+ end
67
+
68
+ # Remove a member from the workspace
69
+ #
70
+ # @param member_id [String] The member ID to remove
71
+ # @return [Hash] Confirmation of removal
72
+ def remove(member_id:)
73
+ validate_id!(member_id, "Member")
74
+ request(:delete, "workspace_members/#{member_id}")
75
+ end
76
+
77
+ # Accept a workspace invitation
78
+ #
79
+ # @param invitation_token [String] The invitation token
80
+ # @return [Hash] The workspace member data
81
+ def accept_invitation(invitation_token:)
82
+ validate_required_string!(invitation_token, "Invitation token")
83
+ request(:post, "workspace_members/invitations/#{invitation_token}/accept")
84
+ end
85
+
86
+ # Resend an invitation
87
+ #
88
+ # @param member_id [String] The member ID with pending invitation
89
+ # @return [Hash] Confirmation of resent invitation
90
+ def resend_invitation(member_id:)
91
+ validate_id!(member_id, "Member")
92
+ request(:post, "workspace_members/#{member_id}/resend_invitation")
93
+ end
94
+
95
+ # Get current member (self)
96
+ #
97
+ # @return [Hash] The current authenticated member's data
98
+ def me
99
+ request(:get, "workspace_members/me")
100
+ end
101
+ end
102
+ end
103
+ end
data/lib/attio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Attio
4
- VERSION = "0.1.3"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/attio.rb CHANGED
@@ -14,6 +14,15 @@ require "attio/resources/lists"
14
14
  require "attio/resources/workspaces"
15
15
  require "attio/resources/attributes"
16
16
  require "attio/resources/users"
17
+ require "attio/resources/notes"
18
+ require "attio/resources/tasks"
19
+ require "attio/resources/comments"
20
+ require "attio/resources/threads"
21
+ require "attio/resources/workspace_members"
22
+ require "attio/resources/deals"
23
+ require "attio/resources/meta"
24
+ require "attio/resources/bulk"
25
+ require "attio/rate_limiter"
17
26
 
18
27
  # The main Attio module provides access to the Attio API client.
19
28
  #