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.
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attio
4
+ module Resources
5
+ # API resource for managing comments in Attio
6
+ #
7
+ # Comments can be added to records and threads for collaboration.
8
+ #
9
+ # @example Creating a comment on a record
10
+ # client.comments.create(
11
+ # parent_object: "people",
12
+ # parent_record_id: "person_123",
13
+ # content: "Just had a great call with this lead!"
14
+ # )
15
+ #
16
+ # @example Creating a comment in a thread
17
+ # client.comments.create(
18
+ # thread_id: "thread_456",
19
+ # content: "Following up on our discussion..."
20
+ # )
21
+ class Comments < Base
22
+ # List comments for a parent record or thread
23
+ #
24
+ # @param params [Hash] Query parameters
25
+ # @option params [String] :parent_object Parent object type
26
+ # @option params [String] :parent_record_id Parent record ID
27
+ # @option params [String] :thread_id Thread ID
28
+ # @option params [Integer] :limit Number of comments to return
29
+ # @option params [String] :cursor Pagination cursor
30
+ #
31
+ # @return [Hash] API response containing comments
32
+ def list(**params)
33
+ validate_list_params!(params)
34
+ request(:get, "comments", params)
35
+ end
36
+
37
+ # Get a specific comment by ID
38
+ #
39
+ # @param id [String] The comment ID
40
+ #
41
+ # @return [Hash] The comment data
42
+ # @raise [ArgumentError] if id is nil or empty
43
+ def get(id:)
44
+ validate_id!(id, "Comment")
45
+ request(:get, "comments/#{id}")
46
+ end
47
+
48
+ # Create a new comment
49
+ #
50
+ # @param content [String] The comment content (supports markdown)
51
+ # @param parent_object [String] Parent object type (required if no thread_id)
52
+ # @param parent_record_id [String] Parent record ID (required if no thread_id)
53
+ # @param thread_id [String] Thread ID (required if no parent_object/parent_record_id)
54
+ # @param data [Hash] Additional comment data
55
+ #
56
+ # @return [Hash] The created comment
57
+ # @raise [ArgumentError] if required parameters are missing
58
+ def create(content:, parent_object: nil, parent_record_id: nil, thread_id: nil, **data)
59
+ validate_required_string!(content, "Comment content")
60
+ validate_create_params!(parent_object, parent_record_id, thread_id)
61
+
62
+ params = data.merge(content: content)
63
+
64
+ if thread_id
65
+ params[:thread_id] = thread_id
66
+ else
67
+ params[:parent_object] = parent_object
68
+ params[:parent_record_id] = parent_record_id
69
+ end
70
+
71
+ request(:post, "comments", params)
72
+ end
73
+
74
+ # Update an existing comment
75
+ #
76
+ # @param id [String] The comment ID
77
+ # @param content [String] The new content
78
+ #
79
+ # @return [Hash] The updated comment
80
+ # @raise [ArgumentError] if id or content is invalid
81
+ def update(id:, content:)
82
+ validate_id!(id, "Comment")
83
+ validate_required_string!(content, "Comment content")
84
+ request(:patch, "comments/#{id}", { content: content })
85
+ end
86
+
87
+ # Delete a comment
88
+ #
89
+ # @param id [String] The comment ID to delete
90
+ #
91
+ # @return [Hash] Deletion confirmation
92
+ # @raise [ArgumentError] if id is nil or empty
93
+ def delete(id:)
94
+ validate_id!(id, "Comment")
95
+ request(:delete, "comments/#{id}")
96
+ end
97
+
98
+ # React to a comment with an emoji
99
+ #
100
+ # @param id [String] The comment ID
101
+ # @param emoji [String] The emoji reaction (e.g., "👍", "❤️")
102
+ #
103
+ # @return [Hash] The updated comment with reaction
104
+ # @raise [ArgumentError] if id or emoji is invalid
105
+ def react(id:, emoji:)
106
+ validate_id!(id, "Comment")
107
+ validate_required_string!(emoji, "Emoji")
108
+ request(:post, "comments/#{id}/reactions", { emoji: emoji })
109
+ end
110
+
111
+ # Remove a reaction from a comment
112
+ #
113
+ # @param id [String] The comment ID
114
+ # @param emoji [String] The emoji reaction to remove
115
+ #
116
+ # @return [Hash] The updated comment
117
+ # @raise [ArgumentError] if id or emoji is invalid
118
+ def unreact(id:, emoji:)
119
+ validate_id!(id, "Comment")
120
+ validate_required_string!(emoji, "Emoji")
121
+ request(:delete, "comments/#{id}/reactions/#{CGI.escape(emoji)}")
122
+ end
123
+
124
+ private def validate_list_params!(params)
125
+ has_parent = params[:parent_object] && params[:parent_record_id]
126
+ has_thread = params[:thread_id]
127
+
128
+ return if has_parent || has_thread
129
+
130
+ raise ArgumentError, "Must provide either parent_object/parent_record_id or thread_id"
131
+ end
132
+
133
+ private def validate_create_params!(parent_object, parent_record_id, thread_id)
134
+ has_parent = parent_object && parent_record_id
135
+ has_thread = thread_id
136
+
137
+ unless has_parent || has_thread
138
+ raise ArgumentError, "Must provide either parent_object/parent_record_id or thread_id"
139
+ end
140
+
141
+ return unless has_parent && has_thread
142
+
143
+ raise ArgumentError, "Cannot provide both parent and thread parameters"
144
+ end
145
+ end
146
+ end
147
+ end
@@ -17,46 +17,34 @@ module Attio
17
17
  end
18
18
 
19
19
  def get(id:)
20
- validate_id!(id)
20
+ validate_id!(id, "List")
21
21
  request(:get, "lists/#{id}")
22
22
  end
23
23
 
24
24
  def entries(id:, **params)
25
- validate_id!(id)
25
+ validate_id!(id, "List")
26
26
  request(:get, "lists/#{id}/entries", params)
27
27
  end
28
28
 
29
29
  def create_entry(id:, data:)
30
- validate_id!(id)
31
- validate_data!(data)
30
+ validate_id!(id, "List")
31
+ validate_list_entry_data!(data)
32
32
  request(:post, "lists/#{id}/entries", data)
33
33
  end
34
34
 
35
35
  def get_entry(list_id:, entry_id:)
36
- validate_list_id!(list_id)
37
- validate_entry_id!(entry_id)
36
+ validate_id!(list_id, "List")
37
+ validate_id!(entry_id, "Entry")
38
38
  request(:get, "lists/#{list_id}/entries/#{entry_id}")
39
39
  end
40
40
 
41
41
  def delete_entry(list_id:, entry_id:)
42
- validate_list_id!(list_id)
43
- validate_entry_id!(entry_id)
42
+ validate_id!(list_id, "List")
43
+ validate_id!(entry_id, "Entry")
44
44
  request(:delete, "lists/#{list_id}/entries/#{entry_id}")
45
45
  end
46
46
 
47
- private def validate_id!(id)
48
- raise ArgumentError, "List ID is required" if id.nil? || id.to_s.strip.empty?
49
- end
50
-
51
- private def validate_list_id!(list_id)
52
- raise ArgumentError, "List ID is required" if list_id.nil? || list_id.to_s.strip.empty?
53
- end
54
-
55
- private def validate_entry_id!(entry_id)
56
- raise ArgumentError, "Entry ID is required" if entry_id.nil? || entry_id.to_s.strip.empty?
57
- end
58
-
59
- private def validate_data!(data)
47
+ private def validate_list_entry_data!(data)
60
48
  raise ArgumentError, "Data is required" if data.nil?
61
49
  raise ArgumentError, "Data must be a hash" unless data.is_a?(Hash)
62
50
  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
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.2.0"
5
5
  end
data/lib/attio.rb CHANGED
@@ -14,6 +14,10 @@ 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"
17
21
 
18
22
  # The main Attio module provides access to the Attio API client.
19
23
  #