bloomy 0.11.4 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bloomy/client.rb +0 -5
- data/lib/bloomy/configuration.rb +13 -2
- data/lib/bloomy/operations/goals.rb +14 -14
- data/lib/bloomy/operations/headlines.rb +23 -23
- data/lib/bloomy/operations/issues.rb +9 -9
- data/lib/bloomy/operations/meetings.rb +22 -24
- data/lib/bloomy/operations/scorecard.rb +10 -10
- data/lib/bloomy/operations/todos.rb +16 -16
- data/lib/bloomy/operations/users.rb +17 -32
- data/lib/bloomy/version.rb +1 -1
- metadata +3 -9
- data/lib/bloomy/types/items.rb +0 -119
- data/lib/bloomy/utils/plugin_loader.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bdff815414812cb7824504342fb4e3b12a2bf7e34b9780275fc1e598c4101dc
|
4
|
+
data.tar.gz: '0687ba4239c6e4fbcc03d0e6e0405f18488705fd83b95014e7b70f8c1ad06c51'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03a0f56e7947c6615b7ae0819c081b81d98f79e7b41451c62feb30c0c4bb2d17fc20b743317997d68c5b15e7cfaf06f69844e2be26d192da773b35b46ed77f56
|
7
|
+
data.tar.gz: c336b3966589127d7e56170428515910139799d2de6bcb01b214a62c6d5a00663afa750363351dbf91663b7427001c28deb47ca5f5c76efa33a63f035077a8df
|
data/lib/bloomy/client.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "faraday"
|
4
4
|
|
5
|
-
require "bloomy/types/items"
|
6
5
|
require "bloomy/operations/users"
|
7
6
|
require "bloomy/operations/todos"
|
8
7
|
require "bloomy/operations/goals"
|
@@ -10,7 +9,6 @@ require "bloomy/operations/meetings"
|
|
10
9
|
require "bloomy/operations/scorecard"
|
11
10
|
require "bloomy/operations/issues"
|
12
11
|
require "bloomy/operations/headlines"
|
13
|
-
require "bloomy/utils/plugin_loader"
|
14
12
|
|
15
13
|
module Bloomy
|
16
14
|
# The Client class is the main entry point for interacting with the Bloomy API.
|
@@ -46,9 +44,6 @@ module Bloomy
|
|
46
44
|
@scorecard = Scorecard.new(@conn)
|
47
45
|
@issue = Issue.new(@conn)
|
48
46
|
@headline = Headline.new(@conn)
|
49
|
-
|
50
|
-
# Initialize plugins
|
51
|
-
Bloomy::Utilities::Plugin.apply(self)
|
52
47
|
end
|
53
48
|
end
|
54
49
|
end
|
data/lib/bloomy/configuration.rb
CHANGED
@@ -44,8 +44,19 @@ module Bloomy
|
|
44
44
|
# @return [String] the fetched API key
|
45
45
|
def fetch_api_key(username, password)
|
46
46
|
conn = Faraday.new(url: "https://app.bloomgrowth.com")
|
47
|
-
response = conn.post("/Token"
|
48
|
-
|
47
|
+
response = conn.post("/Token") do |req|
|
48
|
+
req.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
49
|
+
req.body = URI.encode_www_form(
|
50
|
+
grant_type: "password",
|
51
|
+
userName: username,
|
52
|
+
password: password
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
unless response.success?
|
57
|
+
raise "Failed to fetch API key: #{response.status} - #{response.body}"
|
58
|
+
end
|
59
|
+
|
49
60
|
JSON.parse(response.body)["access_token"]
|
50
61
|
end
|
51
62
|
|
@@ -19,22 +19,22 @@ module Bloomy
|
|
19
19
|
#
|
20
20
|
# @param user_id [Integer] the ID of the user (default is the initialized user ID)
|
21
21
|
# @param archived [Boolean] whether to include archived goals (default: false)
|
22
|
-
# @return [Array<
|
23
|
-
# - An array of
|
24
|
-
# - A hash with :active and :archived arrays of
|
22
|
+
# @return [Array<Hash>, Hash] Returns either:
|
23
|
+
# - An array of goal hashes if archived is false
|
24
|
+
# - A hash with :active and :archived arrays of goal hashes if archived is true
|
25
25
|
# @example List active goals
|
26
26
|
# client.goal.list
|
27
|
-
# #=> [
|
27
|
+
# #=> [{ id: 1, title: "Complete project", ... }]
|
28
28
|
#
|
29
29
|
# @example List both active and archived goals
|
30
30
|
# client.goal.list(archived: true)
|
31
31
|
# #=> {
|
32
|
-
# active: [
|
33
|
-
# archived: [
|
32
|
+
# active: [{ id: 1, ... }],
|
33
|
+
# archived: [{ id: 2, ... }]
|
34
34
|
# }
|
35
35
|
def list(user_id = self.user_id, archived: false)
|
36
36
|
active_goals = @conn.get("rocks/user/#{user_id}?include_origin=true").body.map do |goal|
|
37
|
-
|
37
|
+
{
|
38
38
|
id: goal["Id"],
|
39
39
|
user_id: goal["Owner"]["Id"],
|
40
40
|
user_name: goal["Owner"]["Name"],
|
@@ -44,7 +44,7 @@ module Bloomy
|
|
44
44
|
status: goal["Complete"] ? "Completed" : "Incomplete",
|
45
45
|
meeting_id: goal["Origins"].empty? ? nil : goal["Origins"][0]["Id"],
|
46
46
|
meeting_title: goal["Origins"].empty? ? nil : goal["Origins"][0]["Name"]
|
47
|
-
|
47
|
+
}
|
48
48
|
end
|
49
49
|
|
50
50
|
archived ? {active: active_goals, archived: get_archived_goals(user_id)} : active_goals
|
@@ -55,7 +55,7 @@ module Bloomy
|
|
55
55
|
# @param title [String] the title of the new goal
|
56
56
|
# @param meeting_id [Integer] the ID of the meeting associated with the goal
|
57
57
|
# @param user_id [Integer] the ID of the user responsible for the goal (default: initialized user ID)
|
58
|
-
# @return [
|
58
|
+
# @return [Hash] the newly created goal
|
59
59
|
# @example
|
60
60
|
# client.goal.create(title: "New Goal", meeting_id: 1)
|
61
61
|
# #=> { goal_id: 1, title: "New Goal", meeting_id: 1, ... }
|
@@ -63,7 +63,7 @@ module Bloomy
|
|
63
63
|
payload = {title: title, accountableUserId: user_id}.to_json
|
64
64
|
response = @conn.post("L10/#{meeting_id}/rocks", payload).body
|
65
65
|
|
66
|
-
|
66
|
+
{
|
67
67
|
id: response["Id"],
|
68
68
|
user_id: user_id,
|
69
69
|
user_name: response["Owner"]["Name"],
|
@@ -72,7 +72,7 @@ module Bloomy
|
|
72
72
|
meeting_title: response["Origins"][0]["Name"],
|
73
73
|
status: {complete: 2, on: 1, off: 0}.key(response["Completion"]).to_s,
|
74
74
|
created_at: response["CreateTime"]
|
75
|
-
|
75
|
+
}
|
76
76
|
end
|
77
77
|
|
78
78
|
# Deletes a goal
|
@@ -139,20 +139,20 @@ module Bloomy
|
|
139
139
|
# Retrieves all archived goals for a specific user (private method)
|
140
140
|
#
|
141
141
|
# @param user_id [Integer] the ID of the user (default is the initialized user ID)
|
142
|
-
# @return [Array<
|
142
|
+
# @return [Array<Hash>] an array of hashes containing archived goal details
|
143
143
|
# @example
|
144
144
|
# goal.send(:get_archived_goals)
|
145
145
|
# #=> [{ id: 1, title: "Archived Goal", created_at: "2024-06-10", ... }, ...]
|
146
146
|
def get_archived_goals(user_id = self.user_id)
|
147
147
|
response = @conn.get("archivedrocks/user/#{user_id}").body
|
148
148
|
response.map do |goal|
|
149
|
-
|
149
|
+
{
|
150
150
|
id: goal["Id"],
|
151
151
|
title: goal["Name"],
|
152
152
|
created_at: goal["CreateTime"],
|
153
153
|
due_date: goal["DueDate"],
|
154
154
|
status: goal["Complete"] ? "Complete" : "Incomplete"
|
155
|
-
|
155
|
+
}
|
156
156
|
end
|
157
157
|
end
|
158
158
|
end
|
@@ -18,18 +18,18 @@ module Bloomy
|
|
18
18
|
# @param title [String] the title of the headline
|
19
19
|
# @param owner_id [Integer] the ID of the headline owner
|
20
20
|
# @param notes [String] additional notes for the headline
|
21
|
-
# @return [
|
21
|
+
# @return [Hash] containing id, title, owner_details, and notes_url
|
22
22
|
def create(meeting_id:, title:, owner_id: user_id, notes: nil)
|
23
23
|
response = @conn.post("/api/v1/L10/#{meeting_id}/headlines",
|
24
24
|
{title: title, ownerId: owner_id, notes: notes}.to_json)
|
25
25
|
raise "Failed to create headline" unless response.status == 200
|
26
26
|
|
27
|
-
|
27
|
+
{
|
28
28
|
id: response.body["Id"],
|
29
29
|
title: response.body["Name"],
|
30
|
-
owner_details:
|
30
|
+
owner_details: {id: response.body["OwnerId"]},
|
31
31
|
notes_url: response.body["DetailsUrl"]
|
32
|
-
|
32
|
+
}
|
33
33
|
end
|
34
34
|
|
35
35
|
# Updates a headline
|
@@ -46,28 +46,28 @@ module Bloomy
|
|
46
46
|
# Get headline details
|
47
47
|
#
|
48
48
|
# @param headline_id [Integer] the ID of the headline
|
49
|
-
# @return [
|
50
|
-
#
|
49
|
+
# @return [Hash] containing id, title, notes_url, meeting_details,
|
50
|
+
# owner_details, archived, created_at, and closed_at
|
51
51
|
def details(headline_id)
|
52
52
|
response = @conn.get("/api/v1/headline/#{headline_id}?Include_Origin=true")
|
53
53
|
raise "Failed to get headline details" unless response.status == 200
|
54
54
|
|
55
|
-
|
55
|
+
{
|
56
56
|
id: response.body["Id"],
|
57
57
|
title: response.body["Name"],
|
58
58
|
notes_url: response.body["DetailsUrl"],
|
59
|
-
meeting_details:
|
59
|
+
meeting_details: {
|
60
60
|
id: response.body["OriginId"],
|
61
61
|
title: response.body["Origin"]
|
62
|
-
|
63
|
-
owner_details:
|
62
|
+
},
|
63
|
+
owner_details: {
|
64
64
|
id: response.body["Owner"]["Id"],
|
65
65
|
name: response.body["Owner"]["Name"]
|
66
|
-
|
66
|
+
},
|
67
67
|
archived: response.body["Archived"],
|
68
68
|
created_at: response.body["CreateTime"],
|
69
69
|
closed_at: response.body["CloseTime"]
|
70
|
-
|
70
|
+
}
|
71
71
|
end
|
72
72
|
|
73
73
|
# Get headlines for a user or a meeting.
|
@@ -75,7 +75,7 @@ module Bloomy
|
|
75
75
|
# @param user_id [Integer, nil] the ID of the user (defaults to initialized user_id)
|
76
76
|
# @param meeting_id [Integer, nil] the ID of the meeting
|
77
77
|
# @raise [ArgumentError] if both `user_id` and `meeting_id` are provided
|
78
|
-
# @return [Array<
|
78
|
+
# @return [Array<Hash>] a list of headlines containing:
|
79
79
|
# - id
|
80
80
|
# - title
|
81
81
|
# - meeting_details
|
@@ -86,15 +86,15 @@ module Bloomy
|
|
86
86
|
# @example
|
87
87
|
# client.headline.list
|
88
88
|
# #=> [
|
89
|
-
#
|
89
|
+
# {
|
90
90
|
# id: 1,
|
91
91
|
# title: "Headline Title",
|
92
|
-
# meeting_details:
|
93
|
-
# owner_details:
|
92
|
+
# meeting_details: { id: 1, title: "Team Meeting" },
|
93
|
+
# owner_details: { id: 1, name: "John Doe" },
|
94
94
|
# archived: false,
|
95
95
|
# created_at: "2023-01-01",
|
96
96
|
# closed_at: nil
|
97
|
-
#
|
97
|
+
# }
|
98
98
|
# ]
|
99
99
|
def list(user_id: nil, meeting_id: nil)
|
100
100
|
raise ArgumentError, "Please provide either `user_id` or `meeting_id`, not both." if user_id && meeting_id
|
@@ -109,21 +109,21 @@ module Bloomy
|
|
109
109
|
raise "Failed to list headlines" unless response.success?
|
110
110
|
|
111
111
|
response.body.map do |headline|
|
112
|
-
|
112
|
+
{
|
113
113
|
id: headline["Id"],
|
114
114
|
title: headline["Name"],
|
115
|
-
meeting_details:
|
115
|
+
meeting_details: {
|
116
116
|
id: headline["OriginId"],
|
117
117
|
title: headline["Origin"]
|
118
|
-
|
119
|
-
owner_details:
|
118
|
+
},
|
119
|
+
owner_details: {
|
120
120
|
id: headline["Owner"]["Id"],
|
121
121
|
name: headline["Owner"]["Name"]
|
122
|
-
|
122
|
+
},
|
123
123
|
archived: headline["Archived"],
|
124
124
|
created_at: headline["CreateTime"],
|
125
125
|
closed_at: headline["CloseTime"]
|
126
|
-
|
126
|
+
}
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -21,11 +21,11 @@ module Bloomy
|
|
21
21
|
# Retrieves detailed information about a specific issue
|
22
22
|
#
|
23
23
|
# @param issue_id [Integer] Unique identifier of the issue
|
24
|
-
# @return [
|
24
|
+
# @return [Hash] Detailed information about the issue
|
25
25
|
# @raise [ApiError] When the API request fails or returns invalid data
|
26
26
|
def details(issue_id)
|
27
27
|
response = @conn.get("issues/#{issue_id}").body
|
28
|
-
|
28
|
+
{
|
29
29
|
id: response["Id"],
|
30
30
|
title: response["Name"],
|
31
31
|
notes_url: response["DetailsUrl"],
|
@@ -35,14 +35,14 @@ module Bloomy
|
|
35
35
|
meeting_title: response["Origin"],
|
36
36
|
user_id: response["Owner"]["Id"],
|
37
37
|
user_name: response["Owner"]["Name"]
|
38
|
-
|
38
|
+
}
|
39
39
|
end
|
40
40
|
|
41
41
|
# Lists issues filtered by user or meeting
|
42
42
|
#
|
43
43
|
# @param user_id [Integer, nil] Unique identifier of the user (optional)
|
44
44
|
# @param meeting_id [Integer, nil] Unique identifier of the meeting (optional)
|
45
|
-
# @return [Array<
|
45
|
+
# @return [Array<Hash>] List of issues matching the filter criteria
|
46
46
|
# @raise [ArgumentError] When both user_id and meeting_id are provided
|
47
47
|
# @raise [ApiError] When the API request fails or returns invalid data
|
48
48
|
def list(user_id: nil, meeting_id: nil)
|
@@ -53,14 +53,14 @@ module Bloomy
|
|
53
53
|
response = meeting_id ? @conn.get("l10/#{meeting_id}/issues").body : @conn.get("issues/users/#{user_id || self.user_id}").body
|
54
54
|
|
55
55
|
response.map do |issue|
|
56
|
-
|
56
|
+
{
|
57
57
|
id: issue["Id"],
|
58
58
|
title: issue["Name"],
|
59
59
|
notes_url: issue["DetailsUrl"],
|
60
60
|
created_at: issue["CreateTime"],
|
61
61
|
meeting_id: issue["OriginId"],
|
62
62
|
meeting_title: issue["Origin"]
|
63
|
-
|
63
|
+
}
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
@@ -80,19 +80,19 @@ module Bloomy
|
|
80
80
|
# @param title [String] Title/name of the issue
|
81
81
|
# @param user_id [Integer] Unique identifier of the issue owner (defaults to current user)
|
82
82
|
# @param notes [String, nil] Additional notes or description for the issue (optional)
|
83
|
-
# @return [
|
83
|
+
# @return [Hash] Newly created issue details
|
84
84
|
# @raise [ApiError] When the API request fails or returns invalid data
|
85
85
|
# @raise [ArgumentError] When required parameters are missing or invalid
|
86
86
|
def create(meeting_id:, title:, user_id: self.user_id, notes: nil)
|
87
87
|
response = @conn.post("issues/create", {title: title, meetingid: meeting_id, ownerid: user_id, notes: notes}.to_json)
|
88
|
-
|
88
|
+
{
|
89
89
|
id: response.body["Id"],
|
90
90
|
meeting_id: response.body["OriginId"],
|
91
91
|
meeting_title: response.body["Origin"],
|
92
92
|
title: response.body["Name"],
|
93
93
|
user_id: response.body["Owner"]["Id"],
|
94
94
|
notes_url: response.body["DetailsUrl"]
|
95
|
-
|
95
|
+
}
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
@@ -24,7 +24,7 @@ module Bloomy
|
|
24
24
|
# #=> [{ id: 123, name: "Team Meeting" }, ...]
|
25
25
|
def list(user_id = self.user_id)
|
26
26
|
response = @conn.get("L10/#{user_id}/list").body
|
27
|
-
response.map { |meeting|
|
27
|
+
response.map { |meeting| {id: meeting["Id"], title: meeting["Name"]} }
|
28
28
|
end
|
29
29
|
|
30
30
|
# Lists all attendees for a specific meeting
|
@@ -36,7 +36,7 @@ module Bloomy
|
|
36
36
|
# #=> [{ name: "John Doe", id: 1 }, ...]
|
37
37
|
def attendees(meeting_id)
|
38
38
|
response = @conn.get("L10/#{meeting_id}/attendees").body
|
39
|
-
response.map { |attendee|
|
39
|
+
response.map { |attendee| {id: attendee["Id"], name: attendee["Name"]} }
|
40
40
|
end
|
41
41
|
|
42
42
|
# Lists all issues for a specific meeting
|
@@ -50,7 +50,7 @@ module Bloomy
|
|
50
50
|
def issues(meeting_id, include_closed: false)
|
51
51
|
response = @conn.get("L10/#{meeting_id}/issues?include_resolved=#{include_closed}").body
|
52
52
|
response.map do |issue|
|
53
|
-
|
53
|
+
{
|
54
54
|
id: issue["Id"],
|
55
55
|
title: issue["Name"],
|
56
56
|
notes_url: issue["DetailsUrl"],
|
@@ -60,7 +60,7 @@ module Bloomy
|
|
60
60
|
user_name: issue.dig("Owner", "Name"),
|
61
61
|
meeting_id: meeting_id,
|
62
62
|
meeting_title: issue["Origin"]
|
63
|
-
|
63
|
+
}
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
@@ -75,19 +75,17 @@ module Bloomy
|
|
75
75
|
def todos(meeting_id, include_closed: false)
|
76
76
|
response = @conn.get("L10/#{meeting_id}/todos?INCLUDE_CLOSED=#{include_closed}").body
|
77
77
|
response.map do |todo|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
}
|
90
|
-
)
|
78
|
+
{
|
79
|
+
id: todo["Id"],
|
80
|
+
title: todo["Name"],
|
81
|
+
due_date: todo["DueDate"],
|
82
|
+
notes_url: todo["DetailsUrl"],
|
83
|
+
status: todo["Complete"] ? "Complete" : "Incomplete",
|
84
|
+
created_at: todo["CreateTime"],
|
85
|
+
completed_at: todo["CompleteTime"],
|
86
|
+
user_id: todo.dig("Owner", "Id"),
|
87
|
+
user_name: todo.dig("Owner", "Name")
|
88
|
+
}
|
91
89
|
end
|
92
90
|
end
|
93
91
|
|
@@ -105,7 +103,7 @@ module Bloomy
|
|
105
103
|
response.map do |measurable|
|
106
104
|
next unless measurable["Id"] && measurable["Name"]
|
107
105
|
|
108
|
-
|
106
|
+
{
|
109
107
|
id: measurable["Id"],
|
110
108
|
title: measurable["Name"].to_s.strip,
|
111
109
|
target: measurable["Target"].to_f,
|
@@ -115,7 +113,7 @@ module Bloomy
|
|
115
113
|
user_name: measurable.dig("Owner", "Name"),
|
116
114
|
admin_id: measurable.dig("Admin", "Id"),
|
117
115
|
admin_name: measurable.dig("Admin", "Name")
|
118
|
-
|
116
|
+
}
|
119
117
|
end.compact
|
120
118
|
end
|
121
119
|
|
@@ -128,15 +126,15 @@ module Bloomy
|
|
128
126
|
# client.meeting.details(1)
|
129
127
|
# #=> { id: 1, name: "Team Meeting", attendees: [...], issues: [...], todos: [...], metrics: [...] }
|
130
128
|
def details(meeting_id, include_closed: false)
|
131
|
-
meeting = list.find { |m| m
|
132
|
-
|
133
|
-
id: meeting
|
134
|
-
title: meeting
|
129
|
+
meeting = list.find { |m| m[:id] == meeting_id }
|
130
|
+
{
|
131
|
+
id: meeting[:id],
|
132
|
+
title: meeting[:title],
|
135
133
|
attendees: attendees(meeting_id),
|
136
134
|
issues: issues(meeting_id, include_closed: include_closed),
|
137
135
|
todos: todos(meeting_id, include_closed: include_closed),
|
138
136
|
metrics: metrics(meeting_id)
|
139
|
-
|
137
|
+
}
|
140
138
|
end
|
141
139
|
|
142
140
|
# Creates a new meeting
|
@@ -19,18 +19,18 @@ module Bloomy
|
|
19
19
|
|
20
20
|
# Retrieves the current week details
|
21
21
|
#
|
22
|
-
# @return [
|
22
|
+
# @return [Hash] a hash containing current week details
|
23
23
|
# @example
|
24
24
|
# client.scorecard.current_week
|
25
|
-
# #=>
|
25
|
+
# #=> { id: 123, week_number: 24, week_start: "2024-06-10", week_end: "2024-06-16" }
|
26
26
|
def current_week
|
27
27
|
response = @conn.get("weeks/current").body
|
28
|
-
|
28
|
+
{
|
29
29
|
id: response["Id"],
|
30
30
|
week_number: response["ForWeekNumber"],
|
31
31
|
week_start: response["LocalDate"]["Date"],
|
32
32
|
week_end: response["ForWeek"]
|
33
|
-
|
33
|
+
}
|
34
34
|
end
|
35
35
|
|
36
36
|
# Retrieves the scorecards for a user or a meeting.
|
@@ -40,7 +40,7 @@ module Bloomy
|
|
40
40
|
# @param show_empty [Boolean] whether to include scores with nil values (default: false)
|
41
41
|
# @param week_offset [Integer, nil] offset for the week number to filter scores
|
42
42
|
# @raise [ArgumentError] if both `user_id` and `meeting_id` are provided
|
43
|
-
# @return [Array<
|
43
|
+
# @return [Array<Hash>] an array of scorecard hashes
|
44
44
|
# @example
|
45
45
|
# # Fetch scorecards for the current user
|
46
46
|
# client.scorecard.list
|
@@ -65,7 +65,7 @@ module Bloomy
|
|
65
65
|
end
|
66
66
|
|
67
67
|
scorecards = response["Scores"].map do |scorecard|
|
68
|
-
|
68
|
+
{
|
69
69
|
id: scorecard["Id"],
|
70
70
|
measurable_id: scorecard["MeasurableId"],
|
71
71
|
accountable_user_id: scorecard["AccountableUserId"],
|
@@ -75,16 +75,16 @@ module Bloomy
|
|
75
75
|
week: scorecard["Week"],
|
76
76
|
week_id: scorecard["ForWeek"],
|
77
77
|
updated_at: scorecard["DateEntered"]
|
78
|
-
|
78
|
+
}
|
79
79
|
end
|
80
80
|
|
81
81
|
if week_offset
|
82
82
|
week_data = current_week
|
83
|
-
week_id = week_data
|
84
|
-
scorecards.select! { |scorecard| scorecard
|
83
|
+
week_id = week_data[:week_number] + week_offset
|
84
|
+
scorecards.select! { |scorecard| scorecard[:week_id] == week_id }
|
85
85
|
end
|
86
86
|
|
87
|
-
scorecards.select! { |scorecard| scorecard
|
87
|
+
scorecards.select! { |scorecard| scorecard[:value] || show_empty } unless show_empty
|
88
88
|
scorecards
|
89
89
|
end
|
90
90
|
|
@@ -19,12 +19,12 @@ module Bloomy
|
|
19
19
|
#
|
20
20
|
# @param user_id [Integer, nil] the ID of the user (default is the initialized user ID)
|
21
21
|
# @param meeting_id [Integer, nil] the ID of the meeting
|
22
|
-
# @return [Array<
|
22
|
+
# @return [Array<Hash>] an array of todo hashes
|
23
23
|
# @raise [ArgumentError] if both `user_id` and `meeting_id` are provided
|
24
24
|
# @example
|
25
25
|
# # Fetch todos for the current user
|
26
26
|
# client.todo.list
|
27
|
-
# #=> [
|
27
|
+
# #=> [{ id: 1, title: "New Todo", due_date: "2024-06-15", ... }]
|
28
28
|
def list(user_id: nil, meeting_id: nil)
|
29
29
|
raise ArgumentError, "Please provide either `user_id` or `meeting_id`, not both." if user_id && meeting_id
|
30
30
|
|
@@ -36,7 +36,7 @@ module Bloomy
|
|
36
36
|
end
|
37
37
|
|
38
38
|
response.map do |todo|
|
39
|
-
|
39
|
+
{
|
40
40
|
id: todo["Id"],
|
41
41
|
title: todo["Name"],
|
42
42
|
notes_url: todo["DetailsUrl"],
|
@@ -44,7 +44,7 @@ module Bloomy
|
|
44
44
|
created_at: todo["CreateTime"],
|
45
45
|
completed_at: todo["CompleteTime"],
|
46
46
|
status: todo["Complete"] ? "Complete" : "Incomplete"
|
47
|
-
|
47
|
+
}
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -55,23 +55,23 @@ module Bloomy
|
|
55
55
|
# @param due_date [String, nil] the due date of the todo (optional)
|
56
56
|
# @param user_id [Integer] the ID of the user responsible for the todo (default: initialized user ID)
|
57
57
|
# @param notes [String, nil] additional notes for the todo (optional)
|
58
|
-
# @return [
|
58
|
+
# @return [Hash] the newly created todo hash
|
59
59
|
# @example
|
60
60
|
# client.todo.create(title: "New Todo", meeting_id: 1, due_date: "2024-06-15")
|
61
|
-
# #=>
|
61
|
+
# #=> { id: 1, title: "New Todo", due_date: "2024-06-15", ... }
|
62
62
|
def create(title:, meeting_id:, due_date: nil, user_id: self.user_id, notes: nil)
|
63
63
|
payload = {title: title, accountableUserId: user_id, notes: notes}
|
64
64
|
payload[:dueDate] = due_date if due_date
|
65
65
|
response = @conn.post("/api/v1/L10/#{meeting_id}/todos", payload.to_json).body
|
66
66
|
|
67
|
-
|
67
|
+
{
|
68
68
|
id: response["Id"],
|
69
69
|
title: response["Name"],
|
70
70
|
notes_url: response["DetailsUrl"],
|
71
71
|
due_date: response["DueDate"],
|
72
72
|
created_at: DateTime.now.to_s,
|
73
73
|
status: "Incomplete"
|
74
|
-
|
74
|
+
}
|
75
75
|
end
|
76
76
|
|
77
77
|
# Marks a todo as complete
|
@@ -91,12 +91,12 @@ module Bloomy
|
|
91
91
|
# @param todo_id [Integer] the ID of the todo to update
|
92
92
|
# @param title [String, nil] the new title of the todo (optional)
|
93
93
|
# @param due_date [String, nil] the new due date of the todo (optional)
|
94
|
-
# @return [
|
94
|
+
# @return [Hash] the updated todo hash
|
95
95
|
# @raise [ArgumentError] if no update fields are provided
|
96
96
|
# @raise [RuntimeError] if the update request fails
|
97
97
|
# @example
|
98
98
|
# todo.update(todo_id: 1, title: "Updated Todo", due_date: "2024-11-01")
|
99
|
-
# #=>
|
99
|
+
# #=> { id: 1, title: "Updated Todo", due_date: "2024-11-01", ... }
|
100
100
|
def update(todo_id:, title: nil, due_date: nil)
|
101
101
|
payload = {}
|
102
102
|
payload[:title] = title if title
|
@@ -107,29 +107,29 @@ module Bloomy
|
|
107
107
|
response = @conn.put("/api/v1/todo/#{todo_id}", payload.to_json)
|
108
108
|
raise "Failed to update todo. Status: #{response.status}" unless response.status == 200
|
109
109
|
|
110
|
-
|
110
|
+
{
|
111
111
|
id: todo_id,
|
112
112
|
title: title,
|
113
113
|
due_date: due_date,
|
114
114
|
created_at: nil,
|
115
115
|
status: "Incomplete"
|
116
|
-
|
116
|
+
}
|
117
117
|
end
|
118
118
|
|
119
119
|
# Retrieves the details of a specific todo item by its ID.
|
120
120
|
#
|
121
121
|
# @param todo_id [Integer] The ID of the todo item to retrieve.
|
122
|
-
# @return [
|
122
|
+
# @return [Hash] The requested todo hash
|
123
123
|
# @raise [RuntimeError] If the request to retrieve the todo details fails.
|
124
124
|
# @example
|
125
125
|
# client.todo.details(1)
|
126
|
-
# #=>
|
126
|
+
# #=> { id: 1, title: "Updated Todo", due_date: "2024-11-01", ... }
|
127
127
|
def details(todo_id)
|
128
128
|
response = @conn.get("/api/v1/todo/#{todo_id}")
|
129
129
|
raise "Failed to get todo details. Status: #{response.status}" unless response.success?
|
130
130
|
|
131
131
|
todo = response.body
|
132
|
-
|
132
|
+
{
|
133
133
|
id: todo["Id"],
|
134
134
|
title: todo["Name"],
|
135
135
|
notes_url: todo["DetailsUrl"],
|
@@ -137,7 +137,7 @@ module Bloomy
|
|
137
137
|
created_at: todo["CreateTime"],
|
138
138
|
completed_at: todo["CompleteTime"],
|
139
139
|
status: todo["Complete"] ? "Complete" : "Incomplete"
|
140
|
-
|
140
|
+
}
|
141
141
|
end
|
142
142
|
end
|
143
143
|
end
|
@@ -20,99 +20,84 @@ module Bloomy
|
|
20
20
|
# @param direct_reports [Boolean] whether to include direct reports (default: false)
|
21
21
|
# @param positions [Boolean] whether to include positions (default: false)
|
22
22
|
# @param all [Boolean] whether to include both direct reports and positions (default: false)
|
23
|
-
# @return [
|
24
|
-
# @example
|
25
|
-
# client.user.details
|
26
|
-
# #=> #<Types::UserItem id: 1, name: "John Doe", image_url: "http://example.com/image.jpg">
|
23
|
+
# @return [Hash] a hash containing user details
|
27
24
|
def details(user_id = self.user_id, direct_reports: false, positions: false, all: false)
|
28
25
|
response = @conn.get("users/#{user_id}").body
|
29
|
-
user_details =
|
26
|
+
user_details = {
|
30
27
|
id: response["Id"],
|
31
28
|
name: response["Name"],
|
32
29
|
image_url: response["ImageUrl"]
|
33
|
-
|
30
|
+
}
|
34
31
|
|
35
|
-
user_details
|
36
|
-
user_details
|
32
|
+
user_details[:direct_reports] = direct_reports(user_id) if direct_reports || all
|
33
|
+
user_details[:positions] = positions(user_id) if positions || all
|
37
34
|
user_details
|
38
35
|
end
|
39
36
|
|
40
37
|
# Retrieves direct reports of a specific user
|
41
38
|
#
|
42
39
|
# @param user_id [Integer] the ID of the user (default: the current user ID)
|
43
|
-
# @return [Array<
|
44
|
-
# @example
|
45
|
-
# client.user.direct_reports
|
46
|
-
# #=> [#<Types::UserItem name: "Jane Smith", id: 2, image_url: "http://example.com/image.jpg">, ...]
|
40
|
+
# @return [Array<Hash>] an array of hashes containing direct report details
|
47
41
|
def direct_reports(user_id = self.user_id)
|
48
42
|
direct_reports_response = @conn.get("users/#{user_id}/directreports").body
|
49
43
|
direct_reports_response.map do |report|
|
50
|
-
|
44
|
+
{
|
51
45
|
name: report["Name"],
|
52
46
|
id: report["Id"],
|
53
47
|
image_url: report["ImageUrl"]
|
54
|
-
|
48
|
+
}
|
55
49
|
end
|
56
50
|
end
|
57
51
|
|
58
52
|
# Retrieves positions of a specific user
|
59
53
|
#
|
60
54
|
# @param user_id [Integer] the ID of the user (default: the current user ID)
|
61
|
-
# @return [Array<
|
62
|
-
# @example
|
63
|
-
# user.positions
|
64
|
-
# #=> [#<Types::UserItem name: "Manager", id: 3>, ...]
|
55
|
+
# @return [Array<Hash>] an array of hashes containing position details
|
65
56
|
def positions(user_id = self.user_id)
|
66
57
|
position_response = @conn.get("users/#{user_id}/seats").body
|
67
58
|
position_response.map do |position|
|
68
|
-
|
59
|
+
{
|
69
60
|
name: position["Group"]["Position"]["Name"],
|
70
61
|
id: position["Group"]["Position"]["Id"]
|
71
|
-
|
62
|
+
}
|
72
63
|
end
|
73
64
|
end
|
74
65
|
|
75
66
|
# Searches for users based on a search term
|
76
67
|
#
|
77
68
|
# @param term [String] the search term
|
78
|
-
# @return [Array<
|
79
|
-
# @example
|
80
|
-
# user.search("John")
|
81
|
-
# #=> [#<Types::UserItem id: 1, name: "John Doe", description: "Developer", ...>, ...]
|
69
|
+
# @return [Array<Hash>] an array of hashes containing search results
|
82
70
|
def search(term)
|
83
71
|
response = @conn.get("search/user", term: term).body
|
84
72
|
response.map do |user|
|
85
|
-
|
73
|
+
{
|
86
74
|
id: user["Id"],
|
87
75
|
name: user["Name"],
|
88
76
|
description: user["Description"],
|
89
77
|
email: user["Email"],
|
90
78
|
organization_id: user["OrganizationId"],
|
91
79
|
image_url: user["ImageUrl"]
|
92
|
-
|
80
|
+
}
|
93
81
|
end
|
94
82
|
end
|
95
83
|
|
96
84
|
# Retrieves all users in the system
|
97
85
|
#
|
98
86
|
# @param include_placeholders [Boolean] whether to include placeholder users (default: false)
|
99
|
-
# @return [Array<
|
100
|
-
# @example
|
101
|
-
# user.all
|
102
|
-
# #=> [#<Types::UserItem id: 1, name: "John Doe", email: "john@example.com", ...>, ...]
|
87
|
+
# @return [Array<Hash>] an array of hashes containing user details
|
103
88
|
def all(include_placeholders: false)
|
104
89
|
users = @conn.get("search/all", term: "%").body
|
105
90
|
users
|
106
91
|
.select { |user| user["ResultType"] == "User" }
|
107
92
|
.reject { |user| !include_placeholders && user["ImageUrl"] == "/i/userplaceholder" }
|
108
93
|
.map do |user|
|
109
|
-
|
94
|
+
{
|
110
95
|
id: user["Id"],
|
111
96
|
name: user["Name"],
|
112
97
|
email: user["Email"],
|
113
98
|
position: user["Description"],
|
114
99
|
image_url: user["ImageUrl"]
|
115
|
-
|
100
|
+
}
|
116
101
|
end
|
117
102
|
end
|
118
103
|
end
|
data/lib/bloomy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bloomy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Franccesco Orozco
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-19 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: faraday
|
@@ -38,7 +37,6 @@ dependencies:
|
|
38
37
|
- - "~>"
|
39
38
|
- !ruby/object:Gem::Version
|
40
39
|
version: '3.13'
|
41
|
-
description:
|
42
40
|
email:
|
43
41
|
- franccesco@thatai.dev
|
44
42
|
executables:
|
@@ -66,9 +64,7 @@ files:
|
|
66
64
|
- lib/bloomy/operations/scorecard.rb
|
67
65
|
- lib/bloomy/operations/todos.rb
|
68
66
|
- lib/bloomy/operations/users.rb
|
69
|
-
- lib/bloomy/types/items.rb
|
70
67
|
- lib/bloomy/utils/get_user_id.rb
|
71
|
-
- lib/bloomy/utils/plugin_loader.rb
|
72
68
|
- lib/bloomy/version.rb
|
73
69
|
- sig/bloomy.rbs
|
74
70
|
homepage: https://github.com/franccesco/bloomy
|
@@ -77,7 +73,6 @@ licenses:
|
|
77
73
|
metadata:
|
78
74
|
homepage_uri: https://github.com/franccesco/bloomy
|
79
75
|
rubygems_mfa_required: 'true'
|
80
|
-
post_install_message:
|
81
76
|
rdoc_options: []
|
82
77
|
require_paths:
|
83
78
|
- lib
|
@@ -92,8 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
87
|
- !ruby/object:Gem::Version
|
93
88
|
version: '0'
|
94
89
|
requirements: []
|
95
|
-
rubygems_version: 3.
|
96
|
-
signing_key:
|
90
|
+
rubygems_version: 3.6.2
|
97
91
|
specification_version: 4
|
98
92
|
summary: Manage your Bloom Growth account from the command line.
|
99
93
|
test_files: []
|
data/lib/bloomy/types/items.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Bloomy
|
4
|
-
module Types
|
5
|
-
TodoItem = Struct.new(
|
6
|
-
:id,
|
7
|
-
:title,
|
8
|
-
:notes_url,
|
9
|
-
:due_date,
|
10
|
-
:created_at,
|
11
|
-
:completed_at,
|
12
|
-
:status,
|
13
|
-
:user_name,
|
14
|
-
:user_id,
|
15
|
-
keyword_init: true
|
16
|
-
)
|
17
|
-
|
18
|
-
HeadlineItem = Struct.new(
|
19
|
-
:id,
|
20
|
-
:title,
|
21
|
-
:notes_url,
|
22
|
-
:meeting_details,
|
23
|
-
:owner_details,
|
24
|
-
:archived,
|
25
|
-
:created_at,
|
26
|
-
:closed_at,
|
27
|
-
keyword_init: true
|
28
|
-
)
|
29
|
-
|
30
|
-
MeetingItem = Struct.new(
|
31
|
-
:id,
|
32
|
-
:title,
|
33
|
-
keyword_init: true
|
34
|
-
)
|
35
|
-
|
36
|
-
MeetingDetails = Struct.new(
|
37
|
-
:id,
|
38
|
-
:title,
|
39
|
-
:attendees,
|
40
|
-
:issues,
|
41
|
-
:todos,
|
42
|
-
:metrics,
|
43
|
-
keyword_init: true
|
44
|
-
)
|
45
|
-
|
46
|
-
MetricItem = Struct.new(
|
47
|
-
:id,
|
48
|
-
:title,
|
49
|
-
:target,
|
50
|
-
:operator,
|
51
|
-
:format,
|
52
|
-
:user_id,
|
53
|
-
:user_name,
|
54
|
-
:admin_id,
|
55
|
-
:admin_name,
|
56
|
-
keyword_init: true
|
57
|
-
)
|
58
|
-
|
59
|
-
UserItem = Struct.new(
|
60
|
-
:id,
|
61
|
-
:name,
|
62
|
-
:image_url,
|
63
|
-
:email,
|
64
|
-
:description,
|
65
|
-
:organization_id,
|
66
|
-
:position,
|
67
|
-
:direct_reports,
|
68
|
-
:positions,
|
69
|
-
keyword_init: true
|
70
|
-
)
|
71
|
-
|
72
|
-
GoalItem = Struct.new(
|
73
|
-
:id,
|
74
|
-
:title,
|
75
|
-
:created_at,
|
76
|
-
:due_date,
|
77
|
-
:status,
|
78
|
-
:meeting_id,
|
79
|
-
:meeting_title,
|
80
|
-
:user_id,
|
81
|
-
:user_name,
|
82
|
-
keyword_init: true
|
83
|
-
)
|
84
|
-
|
85
|
-
IssueItem = Struct.new(
|
86
|
-
:id,
|
87
|
-
:title,
|
88
|
-
:notes_url,
|
89
|
-
:created_at,
|
90
|
-
:completed_at,
|
91
|
-
:meeting_id,
|
92
|
-
:meeting_title,
|
93
|
-
:user_id,
|
94
|
-
:user_name,
|
95
|
-
keyword_init: true
|
96
|
-
)
|
97
|
-
|
98
|
-
WeekItem = Struct.new(
|
99
|
-
:id,
|
100
|
-
:week_number,
|
101
|
-
:week_start,
|
102
|
-
:week_end,
|
103
|
-
keyword_init: true
|
104
|
-
)
|
105
|
-
|
106
|
-
ScorecardItem = Struct.new(
|
107
|
-
:id,
|
108
|
-
:measurable_id,
|
109
|
-
:accountable_user_id,
|
110
|
-
:title,
|
111
|
-
:target,
|
112
|
-
:value,
|
113
|
-
:week,
|
114
|
-
:week_id,
|
115
|
-
:updated_at,
|
116
|
-
keyword_init: true
|
117
|
-
)
|
118
|
-
end
|
119
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Bloomy
|
2
|
-
module Utilities
|
3
|
-
module Plugin
|
4
|
-
# A collection of registered plugins.
|
5
|
-
@plugins = []
|
6
|
-
|
7
|
-
class << self
|
8
|
-
# Registers a plugin module.
|
9
|
-
#
|
10
|
-
# @param plugin_module [Module] The plugin module to register.
|
11
|
-
# @return [void]
|
12
|
-
def register(plugin_module)
|
13
|
-
@plugins << plugin_module
|
14
|
-
end
|
15
|
-
|
16
|
-
# Applies all registered plugins to the given client.
|
17
|
-
#
|
18
|
-
# @param client [Object] The client to which the plugins will be applied.
|
19
|
-
# @return [void]
|
20
|
-
def apply(client)
|
21
|
-
@plugins.each do |plugin|
|
22
|
-
plugin.apply(client)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|