bloomy 0.10.0 → 0.11.4

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.
@@ -3,112 +3,96 @@
3
3
  require "json"
4
4
  require "bloomy/utils/get_user_id"
5
5
 
6
- # Class to handle all the operations related to issues
7
- class Issue
8
- include Bloomy::Utilities::UserIdUtility
9
- # Initializes a new Issue instance
10
- #
11
- # @param conn [Object] the connection object to interact with the API
12
- def initialize(conn)
13
- @conn = conn
14
- end
15
-
16
- # Retrieves details of a specific issue
17
- #
18
- # @param issue_id [Integer] the ID of the issue
19
- # @return [Hash] a hash containing issue details
20
- # @example
21
- # issue.details(123)
22
- # #=> { id: 123, title: "Issue Title", notes_url: "http://details.url", ... }
23
- def details(issue_id)
24
- response = @conn.get("issues/#{issue_id}").body
25
- {
26
- id: response["Id"],
27
- title: response["Name"],
28
- notes_url: response["DetailsUrl"],
29
- created_at: response["CreateTime"],
30
- completed_at: response["CloseTime"],
31
- meeting_details: {
32
- id: response["OriginId"],
33
- title: response["Origin"]
34
- },
35
- owner_details: {
36
- id: response["Owner"]["Id"],
37
- name: response["Owner"]["Name"]
38
- }
39
- }
40
- end
6
+ module Bloomy
7
+ # Handles CRUD operations for issues in the system.
8
+ # Provides functionality to create, retrieve, list, and solve issues
9
+ # associated with meetings and users.
10
+ class Issue
11
+ include Bloomy::Utilities::UserIdUtility
41
12
 
42
- # Lists issues for a specific user or meeting
43
- #
44
- # @param user_id [Integer, nil] the ID of the user (defaults to initialized user_id)
45
- # @param meeting_id [Integer, nil] the ID of the meeting
46
- # @raise [ArgumentError] if both `user_id` and `meeting_id` are provided
47
- # @return [Array<Hash>] an array of hashes containing issues details
48
- # @example
49
- # # Fetch issues for the current user
50
- # issue.list
51
- #
52
- # # Fetch issues for a specific user
53
- # issue.list(user_id: 42)
54
- #
55
- # # Fetch issues for a specific meeting
56
- # issue.list(meeting_id: 99)
57
- def list(user_id: nil, meeting_id: nil)
58
- if user_id && meeting_id
59
- raise ArgumentError, "Please provide either `user_id` or `meeting_id`, not both."
13
+ # Initializes a new Issue instance
14
+ #
15
+ # @param conn [Faraday::Connection] Connection object for making API requests
16
+ # @return [Issue] New instance of Issue
17
+ def initialize(conn)
18
+ @conn = conn
60
19
  end
61
20
 
62
- if meeting_id
63
- response = @conn.get("l10/#{meeting_id}/issues").body
64
- else
65
- user_id ||= self.user_id
66
- response = @conn.get("issues/users/#{user_id}").body
21
+ # Retrieves detailed information about a specific issue
22
+ #
23
+ # @param issue_id [Integer] Unique identifier of the issue
24
+ # @return [Types::IssueDetails] Detailed information about the issue
25
+ # @raise [ApiError] When the API request fails or returns invalid data
26
+ def details(issue_id)
27
+ response = @conn.get("issues/#{issue_id}").body
28
+ Types::IssueItem.new(
29
+ id: response["Id"],
30
+ title: response["Name"],
31
+ notes_url: response["DetailsUrl"],
32
+ created_at: response["CreateTime"],
33
+ completed_at: response["CloseTime"],
34
+ meeting_id: response["OriginId"],
35
+ meeting_title: response["Origin"],
36
+ user_id: response["Owner"]["Id"],
37
+ user_name: response["Owner"]["Name"]
38
+ )
67
39
  end
68
40
 
69
- response.map do |issue|
70
- {
71
- id: issue["Id"],
72
- title: issue["Name"],
73
- notes_url: issue["DetailsUrl"],
74
- created_at: issue["CreateTime"],
75
- meeting_id: issue["OriginId"],
76
- meeting_title: issue["Origin"]
77
- }
41
+ # Lists issues filtered by user or meeting
42
+ #
43
+ # @param user_id [Integer, nil] Unique identifier of the user (optional)
44
+ # @param meeting_id [Integer, nil] Unique identifier of the meeting (optional)
45
+ # @return [Array<Types::IssueItem>] List of issues matching the filter criteria
46
+ # @raise [ArgumentError] When both user_id and meeting_id are provided
47
+ # @raise [ApiError] When the API request fails or returns invalid data
48
+ def list(user_id: nil, meeting_id: nil)
49
+ if user_id && meeting_id
50
+ raise ArgumentError, "Please provide either `user_id` or `meeting_id`, not both."
51
+ end
52
+
53
+ response = meeting_id ? @conn.get("l10/#{meeting_id}/issues").body : @conn.get("issues/users/#{user_id || self.user_id}").body
54
+
55
+ response.map do |issue|
56
+ Types::IssueItem.new(
57
+ id: issue["Id"],
58
+ title: issue["Name"],
59
+ notes_url: issue["DetailsUrl"],
60
+ created_at: issue["CreateTime"],
61
+ meeting_id: issue["OriginId"],
62
+ meeting_title: issue["Origin"]
63
+ )
64
+ end
78
65
  end
79
- end
80
66
 
81
- # Marks an issue as solved
82
- #
83
- # @param issue_id [Integer] the ID of the issue
84
- # @return [Boolean] true if the operation was successful, false otherwise
85
- # @example
86
- # issue.solve(123)
87
- # #=> true
88
- def solve(issue_id)
89
- response = @conn.post("issues/#{issue_id}/complete", {complete: true}.to_json)
90
- response.success?
91
- end
67
+ # Marks an issue as completed/solved
68
+ #
69
+ # @param issue_id [Integer] Unique identifier of the issue to be solved
70
+ # @return [Boolean] true if issue was successfully solved, false otherwise
71
+ # @raise [ApiError] When the API request fails
72
+ def solve(issue_id)
73
+ response = @conn.post("issues/#{issue_id}/complete", {complete: true}.to_json)
74
+ response.success?
75
+ end
92
76
 
93
- # Creates a new issue
94
- #
95
- # @param meeting_id [Integer] the ID of the meeting associated with the issue
96
- # @param title [String] the title of the new issue
97
- # @param user_id [Integer] the ID of the user responsible for the issue (default: initialized user ID)
98
- # @param notes [String, nil] the notes for the issue (optional)
99
- # @return [Hash] a hash containing the new issue's ID and title
100
- # @example
101
- # issue.create(meeting_id: 123, title: "New Issue")
102
- # #=> { id: 789, title: "New Issue" }
103
- def create(meeting_id:, title:, user_id: self.user_id, notes: nil)
104
- response = @conn.post("issues/create", {title: title, meetingid: meeting_id, ownerid: user_id, notes: notes}.to_json)
105
- {
106
- id: response.body["Id"],
107
- meeting_id: response.body["OriginId"],
108
- meeting_title: response.body["Origin"],
109
- title: response.body["Name"],
110
- user_id: response.body["Owner"]["Id"],
111
- notes_url: response.body["DetailsUrl"]
112
- }
77
+ # Creates a new issue in the system
78
+ #
79
+ # @param meeting_id [Integer] Unique identifier of the associated meeting
80
+ # @param title [String] Title/name of the issue
81
+ # @param user_id [Integer] Unique identifier of the issue owner (defaults to current user)
82
+ # @param notes [String, nil] Additional notes or description for the issue (optional)
83
+ # @return [Types::IssueItem] Newly created issue details
84
+ # @raise [ApiError] When the API request fails or returns invalid data
85
+ # @raise [ArgumentError] When required parameters are missing or invalid
86
+ def create(meeting_id:, title:, user_id: self.user_id, notes: nil)
87
+ response = @conn.post("issues/create", {title: title, meetingid: meeting_id, ownerid: user_id, notes: notes}.to_json)
88
+ Types::IssueItem.new(
89
+ id: response.body["Id"],
90
+ meeting_id: response.body["OriginId"],
91
+ meeting_title: response.body["Origin"],
92
+ title: response.body["Name"],
93
+ user_id: response.body["Owner"]["Id"],
94
+ notes_url: response.body["DetailsUrl"]
95
+ )
96
+ end
113
97
  end
114
98
  end
@@ -2,171 +2,173 @@
2
2
 
3
3
  require "bloomy/utils/get_user_id"
4
4
 
5
- # Class to handle all the operations related to meeting
6
- # @note
7
- # This class is already initialized via the client and usable as `client.measurable.method`
8
- class Meeting
9
- include Bloomy::Utilities::UserIdUtility
10
- # Initializes a new Meeting instance
11
- #
12
- # @param conn [Object] the connection object to interact with the API
13
- def initialize(conn)
14
- @conn = conn
15
- end
5
+ module Bloomy
6
+ # Class to handle all the operations related to meeting
7
+ # @note
8
+ # This class is already initialized via the client and usable as `client.meeting.method`
9
+ class Meeting
10
+ include Bloomy::Utilities::UserIdUtility
11
+ # Initializes a new Meeting instance
12
+ #
13
+ # @param conn [Object] the connection object to interact with the API
14
+ def initialize(conn)
15
+ @conn = conn
16
+ end
16
17
 
17
- # Lists all meetings for a specific user
18
- #
19
- # @param user_id [Integer] the ID of the user (default is the initialized user ID)
20
- # @return [Array<Hash>] an array of hashes containing meeting details
21
- # @example
22
- # client.meeting.list
23
- # #=> [{ id: 123, name: "Team Meeting" }, ...]
24
- def list(user_id = self.user_id)
25
- response = @conn.get("L10/#{user_id}/list").body
26
- response.map { |meeting| {id: meeting["Id"], name: meeting["Name"]} }
27
- end
18
+ # Lists all meetings for a specific user
19
+ #
20
+ # @param user_id [Integer] the ID of the user (default is the initialized user ID)
21
+ # @return [Array<Hash>] an array of hashes containing meeting details
22
+ # @example
23
+ # client.meeting.list
24
+ # #=> [{ id: 123, name: "Team Meeting" }, ...]
25
+ def list(user_id = self.user_id)
26
+ response = @conn.get("L10/#{user_id}/list").body
27
+ response.map { |meeting| Types::MeetingItem.new(id: meeting["Id"], title: meeting["Name"]) }
28
+ end
28
29
 
29
- # Lists all attendees for a specific meeting
30
- #
31
- # @param meeting_id [Integer] the ID of the meeting
32
- # @return [Array<Hash>] an array of hashes containing attendee details
33
- # @example
34
- # client.meeting.attendees(1)
35
- # #=> [{ name: "John Doe", id: 1 }, ...]
36
- def attendees(meeting_id)
37
- response = @conn.get("L10/#{meeting_id}/attendees").body
38
- response.map { |attendee| {name: attendee["Name"], id: attendee["Id"]} }
39
- end
30
+ # Lists all attendees for a specific meeting
31
+ #
32
+ # @param meeting_id [Integer] the ID of the meeting
33
+ # @return [Array<Hash>] an array of hashes containing attendee details
34
+ # @example
35
+ # client.meeting.attendees(1)
36
+ # #=> [{ name: "John Doe", id: 1 }, ...]
37
+ def attendees(meeting_id)
38
+ response = @conn.get("L10/#{meeting_id}/attendees").body
39
+ response.map { |attendee| Types::UserItem.new(id: attendee["Id"], name: attendee["Name"]) }
40
+ end
40
41
 
41
- # Lists all issues for a specific meeting
42
- #
43
- # @param meeting_id [Integer] the ID of the meeting
44
- # @param include_closed [Boolean] whether to include closed issues (default: false)
45
- # @return [Array<Hash>] an array of hashes containing issue details
46
- # @example
47
- # client.meeting.issues(1)
48
- # #=> [{ id: 1, title: "Issue Title", created_at: "2024-06-10", ... }, ...]
49
- def issues(meeting_id, include_closed: false)
50
- response = @conn.get("L10/#{meeting_id}/issues?include_resolved=#{include_closed}").body
51
- response.map do |issue|
52
- {
53
- id: issue["Id"],
54
- title: issue["Name"],
55
- created_at: issue["CreateTime"],
56
- closed_at: issue["CloseTime"],
57
- details_url: issue["DetailsUrl"],
58
- owner: {
59
- id: issue["Owner"]["Id"],
60
- name: issue["Owner"]["Name"]
61
- }
62
- }
42
+ # Lists all issues for a specific meeting
43
+ #
44
+ # @param meeting_id [Integer] the ID of the meeting
45
+ # @param include_closed [Boolean] whether to include closed issues (default: false)
46
+ # @return [Array<Hash>] an array of hashes containing issue details
47
+ # @example
48
+ # client.meeting.issues(1)
49
+ # #=> [{ id: 1, title: "Issue Title", created_at: "2024-06-10", ... }, ...]
50
+ def issues(meeting_id, include_closed: false)
51
+ response = @conn.get("L10/#{meeting_id}/issues?include_resolved=#{include_closed}").body
52
+ response.map do |issue|
53
+ Types::IssueItem.new(
54
+ id: issue["Id"],
55
+ title: issue["Name"],
56
+ notes_url: issue["DetailsUrl"],
57
+ created_at: issue["CreateTime"],
58
+ completed_at: issue["CloseTime"],
59
+ user_id: issue.dig("Owner", "Id"),
60
+ user_name: issue.dig("Owner", "Name"),
61
+ meeting_id: meeting_id,
62
+ meeting_title: issue["Origin"]
63
+ )
64
+ end
63
65
  end
64
- end
65
66
 
66
- # Lists all todos for a specific meeting
67
- #
68
- # @param meeting_id [Integer] the ID of the meeting
69
- # @param include_closed [Boolean] whether to include closed todos (default: false)
70
- # @return [Array<Hash>] an array of hashes containing todo details
71
- # @example
72
- # client.meeting.todos(1)
73
- # #=> [{ id: 1, title: "Todo Title", due_date: "2024-06-12", ... }, ...]
74
- def todos(meeting_id, include_closed: false)
75
- response = @conn.get("L10/#{meeting_id}/todos?INCLUDE_CLOSED=#{include_closed}").body
76
- response.map do |todo|
77
- {
78
- id: todo["Id"],
79
- title: todo["Name"],
80
- due_date: todo["DueDate"],
81
- details_url: todo["DetailsUrl"],
82
- completed_at: todo["CompleteTime"],
83
- owner: {
84
- id: todo["Owner"]["Id"],
85
- name: todo["Owner"]["Name"]
86
- }
87
- }
67
+ # Lists all todos for a specific meeting
68
+ #
69
+ # @param meeting_id [Integer] the ID of the meeting
70
+ # @param include_closed [Boolean] whether to include closed todos (default: false)
71
+ # @return [Array<Hash>] an array of hashes containing todo details
72
+ # @example
73
+ # client.meeting.todos(1)
74
+ # #=> [{ id: 1, title: "Todo Title", due_date: "2024-06-12", ... }, ...]
75
+ def todos(meeting_id, include_closed: false)
76
+ response = @conn.get("L10/#{meeting_id}/todos?INCLUDE_CLOSED=#{include_closed}").body
77
+ response.map do |todo|
78
+ Types::TodoItem.new(
79
+ {
80
+ id: todo["Id"],
81
+ title: todo["Name"],
82
+ due_date: todo["DueDate"],
83
+ notes_url: todo["DetailsUrl"],
84
+ status: todo["Complete"] ? "Complete" : "Incomplete",
85
+ created_at: todo["CreateTime"],
86
+ completed_at: todo["CompleteTime"],
87
+ user_id: todo.dig("Owner", "Id"),
88
+ user_name: todo.dig("Owner", "Name")
89
+ }
90
+ )
91
+ end
88
92
  end
89
- end
90
93
 
91
- # Lists all metrics for a specific meeting
92
- #
93
- # @param meeting_id [Integer] the ID of the meeting
94
- # @return [Array<Hash>] an array of hashes containing metric details
95
- # @example
96
- # client.meeting.metrics(1)
97
- # #=> [{ id: 1, name: "Sales", target: 100, operator: ">", format: "currency", ... }, ...]
98
- def metrics(meeting_id)
99
- response = @conn.get("L10/#{meeting_id}/measurables").body
100
- response.map do |measurable|
101
- {
102
- id: measurable["Id"],
103
- title: measurable["Name"].strip,
104
- target: measurable["Target"],
105
- operator: measurable["Direction"],
106
- format: measurable["Modifiers"],
107
- owner: {
108
- id: measurable["Owner"]["Id"],
109
- name: measurable["Owner"]["Name"]
110
- },
111
- admin: {
112
- id: measurable["Admin"]["Id"],
113
- name: measurable["Admin"]["Name"]
114
- }
115
- }
94
+ # Lists all metrics for a specific meeting
95
+ #
96
+ # @param meeting_id [Integer] the ID of the meeting
97
+ # @return [Array<Hash>] an array of hashes containing metric details
98
+ # @example
99
+ # client.meeting.metrics(1)
100
+ # #=> [{ id: 1, name: "Sales", target: 100, operator: ">", format: "currency", ... }, ...]
101
+ def metrics(meeting_id)
102
+ response = @conn.get("L10/#{meeting_id}/measurables").body
103
+ return [] if response.nil? || !response.is_a?(Array)
104
+
105
+ response.map do |measurable|
106
+ next unless measurable["Id"] && measurable["Name"]
107
+
108
+ Types::MetricItem.new(
109
+ id: measurable["Id"],
110
+ title: measurable["Name"].to_s.strip,
111
+ target: measurable["Target"].to_f,
112
+ operator: measurable["Direction"].to_s,
113
+ format: measurable["Modifiers"].to_s,
114
+ user_id: measurable.dig("Owner", "Id"),
115
+ user_name: measurable.dig("Owner", "Name"),
116
+ admin_id: measurable.dig("Admin", "Id"),
117
+ admin_name: measurable.dig("Admin", "Name")
118
+ )
119
+ end.compact
116
120
  end
117
- end
118
121
 
119
- # Retrieves details of a specific meeting
120
- #
121
- # @param meeting_id [Integer] the ID of the meeting
122
- # @param include_closed [Boolean] whether to include closed issues and todos (default: false)
123
- # @return [Hash] a hash containing detailed information about the meeting
124
- # @example
125
- # client.meeting.details(1)
126
- # #=> { id: 1, name: "Team Meeting", attendees: [...], issues: [...], todos: [...], metrics: [...] }
127
- def details(meeting_id, include_closed: false)
128
- meeting = list.find { |m| m[:id] == meeting_id }
129
- attendees = attendees(meeting_id)
130
- issues = issues(meeting_id, include_closed: include_closed)
131
- todos = todos(meeting_id, include_closed: include_closed)
132
- measurables = metrics(meeting_id)
133
- {
134
- id: meeting[:id],
135
- title: meeting[:name],
136
- attendees: attendees,
137
- issues: issues,
138
- todos: todos,
139
- metrics: measurables
140
- }
141
- end
122
+ # Retrieves details of a specific meeting
123
+ #
124
+ # @param meeting_id [Integer] the ID of the meeting
125
+ # @param include_closed [Boolean] whether to include closed issues and todos (default: false)
126
+ # @return [Hash] a hash containing detailed information about the meeting
127
+ # @example
128
+ # client.meeting.details(1)
129
+ # #=> { id: 1, name: "Team Meeting", attendees: [...], issues: [...], todos: [...], metrics: [...] }
130
+ def details(meeting_id, include_closed: false)
131
+ meeting = list.find { |m| m.id == meeting_id }
132
+ Types::MeetingDetails.new(
133
+ id: meeting.id,
134
+ title: meeting.title,
135
+ attendees: attendees(meeting_id),
136
+ issues: issues(meeting_id, include_closed: include_closed),
137
+ todos: todos(meeting_id, include_closed: include_closed),
138
+ metrics: metrics(meeting_id)
139
+ )
140
+ end
142
141
 
143
- # Creates a new meeting
144
- #
145
- # @param title [String] the title of the new meeting
146
- # @param add_self [Boolean] whether to add the current user as an attendee (default: true)
147
- # @param attendees [Array<Integer>] a list of user IDs to add as attendees
148
- # @return [Hash] a hash containing the new meeting's ID and title, and the list of attendees
149
- # @example
150
- # client.meeting.create(title: "New Meeting", attendees: [2, 3])
151
- # #=> { meeting_id: 1, title: "New Meeting", attendees: [2, 3] }
152
- def create(title, add_self: true, attendees: [])
153
- payload = {title: title, addSelf: add_self}.to_json
154
- response = @conn.post("L10/create", payload).body
155
- meeting_id = response["meetingId"]
156
- meeting_details = {meeting_id: meeting_id, title: title}
157
- attendees.each do |attendee|
158
- @conn.post("L10/#{meeting_id}/attendees/#{attendee}")
142
+ # Creates a new meeting
143
+ #
144
+ # @param title [String] the title of the new meeting
145
+ # @param add_self [Boolean] whether to add the current user as an attendee (default: true)
146
+ # @param attendees [Array<Integer>] a list of user IDs to add as attendees
147
+ # @return [Hash] a hash containing meeting_id, title and attendees array
148
+ # @example
149
+ # client.meeting.create("New Meeting", attendees: [2, 3])
150
+ # #=> { meeting_id: 1, title: "New Meeting", attendees: [2, 3] }
151
+ def create(title, add_self: true, attendees: [])
152
+ payload = {title: title, addSelf: add_self}.to_json
153
+ response = @conn.post("L10/create", payload).body
154
+ meeting_id = response["meetingId"]
155
+ meeting_details = {meeting_id: meeting_id, title: title}
156
+ attendees.each do |attendee|
157
+ @conn.post("L10/#{meeting_id}/attendees/#{attendee}")
158
+ end
159
+ meeting_details.merge(attendees: attendees)
159
160
  end
160
- meeting_details.merge(attendees: attendees)
161
- end
162
161
 
163
- # Deletes a meeting
164
- #
165
- # @param meeting_id [Integer] the ID of the meeting to delete
166
- # @example
167
- # client.meeting.delete(1)
168
- def delete(meeting_id)
169
- response = @conn.delete("L10/#{meeting_id}")
170
- response.success?
162
+ # Deletes a meeting
163
+ #
164
+ # @param meeting_id [Integer] the ID of the meeting to delete
165
+ # @return [Boolean] true if deletion was successful
166
+ # @example
167
+ # client.meeting.delete(1)
168
+ # #=> true
169
+ def delete(meeting_id)
170
+ response = @conn.delete("L10/#{meeting_id}")
171
+ response.success?
172
+ end
171
173
  end
172
174
  end