basecamp-sdk 0.2.1

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.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +14 -0
  3. data/.yardopts +6 -0
  4. data/README.md +293 -0
  5. data/Rakefile +26 -0
  6. data/basecamp-sdk.gemspec +46 -0
  7. data/lib/basecamp/auth_strategy.rb +38 -0
  8. data/lib/basecamp/chain_hooks.rb +45 -0
  9. data/lib/basecamp/client.rb +428 -0
  10. data/lib/basecamp/config.rb +143 -0
  11. data/lib/basecamp/errors.rb +289 -0
  12. data/lib/basecamp/generated/metadata.json +2281 -0
  13. data/lib/basecamp/generated/services/attachments_service.rb +24 -0
  14. data/lib/basecamp/generated/services/boosts_service.rb +70 -0
  15. data/lib/basecamp/generated/services/campfires_service.rb +122 -0
  16. data/lib/basecamp/generated/services/card_columns_service.rb +103 -0
  17. data/lib/basecamp/generated/services/card_steps_service.rb +57 -0
  18. data/lib/basecamp/generated/services/card_tables_service.rb +20 -0
  19. data/lib/basecamp/generated/services/cards_service.rb +66 -0
  20. data/lib/basecamp/generated/services/checkins_service.rb +157 -0
  21. data/lib/basecamp/generated/services/client_approvals_service.rb +28 -0
  22. data/lib/basecamp/generated/services/client_correspondences_service.rb +28 -0
  23. data/lib/basecamp/generated/services/client_replies_service.rb +30 -0
  24. data/lib/basecamp/generated/services/client_visibility_service.rb +21 -0
  25. data/lib/basecamp/generated/services/comments_service.rb +49 -0
  26. data/lib/basecamp/generated/services/documents_service.rb +52 -0
  27. data/lib/basecamp/generated/services/events_service.rb +20 -0
  28. data/lib/basecamp/generated/services/forwards_service.rb +67 -0
  29. data/lib/basecamp/generated/services/lineup_service.rb +44 -0
  30. data/lib/basecamp/generated/services/message_boards_service.rb +20 -0
  31. data/lib/basecamp/generated/services/message_types_service.rb +59 -0
  32. data/lib/basecamp/generated/services/messages_service.rb +75 -0
  33. data/lib/basecamp/generated/services/people_service.rb +73 -0
  34. data/lib/basecamp/generated/services/projects_service.rb +63 -0
  35. data/lib/basecamp/generated/services/recordings_service.rb +64 -0
  36. data/lib/basecamp/generated/services/reports_service.rb +56 -0
  37. data/lib/basecamp/generated/services/schedules_service.rb +92 -0
  38. data/lib/basecamp/generated/services/search_service.rb +31 -0
  39. data/lib/basecamp/generated/services/subscriptions_service.rb +50 -0
  40. data/lib/basecamp/generated/services/templates_service.rb +82 -0
  41. data/lib/basecamp/generated/services/timeline_service.rb +20 -0
  42. data/lib/basecamp/generated/services/timesheets_service.rb +81 -0
  43. data/lib/basecamp/generated/services/todolist_groups_service.rb +41 -0
  44. data/lib/basecamp/generated/services/todolists_service.rb +53 -0
  45. data/lib/basecamp/generated/services/todos_service.rb +106 -0
  46. data/lib/basecamp/generated/services/todosets_service.rb +20 -0
  47. data/lib/basecamp/generated/services/tools_service.rb +80 -0
  48. data/lib/basecamp/generated/services/uploads_service.rb +61 -0
  49. data/lib/basecamp/generated/services/vaults_service.rb +49 -0
  50. data/lib/basecamp/generated/services/webhooks_service.rb +63 -0
  51. data/lib/basecamp/generated/types.rb +3196 -0
  52. data/lib/basecamp/hooks.rb +70 -0
  53. data/lib/basecamp/http.rb +440 -0
  54. data/lib/basecamp/logger_hooks.rb +46 -0
  55. data/lib/basecamp/noop_hooks.rb +9 -0
  56. data/lib/basecamp/oauth/discovery.rb +123 -0
  57. data/lib/basecamp/oauth/errors.rb +35 -0
  58. data/lib/basecamp/oauth/exchange.rb +291 -0
  59. data/lib/basecamp/oauth/pkce.rb +68 -0
  60. data/lib/basecamp/oauth/types.rb +133 -0
  61. data/lib/basecamp/oauth.rb +56 -0
  62. data/lib/basecamp/oauth_token_provider.rb +108 -0
  63. data/lib/basecamp/operation_info.rb +17 -0
  64. data/lib/basecamp/request_info.rb +10 -0
  65. data/lib/basecamp/request_result.rb +14 -0
  66. data/lib/basecamp/security.rb +112 -0
  67. data/lib/basecamp/services/attachments_service.rb +33 -0
  68. data/lib/basecamp/services/authorization_service.rb +47 -0
  69. data/lib/basecamp/services/base_service.rb +146 -0
  70. data/lib/basecamp/services/campfires_service.rb +141 -0
  71. data/lib/basecamp/services/card_columns_service.rb +106 -0
  72. data/lib/basecamp/services/card_steps_service.rb +86 -0
  73. data/lib/basecamp/services/card_tables_service.rb +23 -0
  74. data/lib/basecamp/services/cards_service.rb +93 -0
  75. data/lib/basecamp/services/checkins_service.rb +127 -0
  76. data/lib/basecamp/services/client_approvals_service.rb +33 -0
  77. data/lib/basecamp/services/client_correspondences_service.rb +33 -0
  78. data/lib/basecamp/services/client_replies_service.rb +35 -0
  79. data/lib/basecamp/services/comments_service.rb +63 -0
  80. data/lib/basecamp/services/documents_service.rb +74 -0
  81. data/lib/basecamp/services/events_service.rb +27 -0
  82. data/lib/basecamp/services/forwards_service.rb +80 -0
  83. data/lib/basecamp/services/lineup_service.rb +67 -0
  84. data/lib/basecamp/services/message_boards_service.rb +24 -0
  85. data/lib/basecamp/services/message_types_service.rb +79 -0
  86. data/lib/basecamp/services/messages_service.rb +133 -0
  87. data/lib/basecamp/services/people_service.rb +73 -0
  88. data/lib/basecamp/services/projects_service.rb +67 -0
  89. data/lib/basecamp/services/recordings_service.rb +127 -0
  90. data/lib/basecamp/services/reports_service.rb +80 -0
  91. data/lib/basecamp/services/schedules_service.rb +156 -0
  92. data/lib/basecamp/services/search_service.rb +36 -0
  93. data/lib/basecamp/services/subscriptions_service.rb +67 -0
  94. data/lib/basecamp/services/templates_service.rb +96 -0
  95. data/lib/basecamp/services/timeline_service.rb +62 -0
  96. data/lib/basecamp/services/timesheet_service.rb +68 -0
  97. data/lib/basecamp/services/todolist_groups_service.rb +100 -0
  98. data/lib/basecamp/services/todolists_service.rb +104 -0
  99. data/lib/basecamp/services/todos_service.rb +156 -0
  100. data/lib/basecamp/services/todosets_service.rb +23 -0
  101. data/lib/basecamp/services/tools_service.rb +89 -0
  102. data/lib/basecamp/services/uploads_service.rb +84 -0
  103. data/lib/basecamp/services/vaults_service.rb +84 -0
  104. data/lib/basecamp/services/webhooks_service.rb +88 -0
  105. data/lib/basecamp/static_token_provider.rb +24 -0
  106. data/lib/basecamp/token_provider.rb +42 -0
  107. data/lib/basecamp/version.rb +6 -0
  108. data/lib/basecamp/webhooks/event.rb +52 -0
  109. data/lib/basecamp/webhooks/rack_middleware.rb +49 -0
  110. data/lib/basecamp/webhooks/receiver.rb +161 -0
  111. data/lib/basecamp/webhooks/verify.rb +36 -0
  112. data/lib/basecamp.rb +107 -0
  113. data/scripts/generate-metadata.rb +106 -0
  114. data/scripts/generate-services.rb +778 -0
  115. data/scripts/generate-types.rb +191 -0
  116. metadata +316 -0
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for lineup operations.
6
+ #
7
+ # The Lineup is Basecamp's visual timeline tool for tracking
8
+ # project schedules and milestones.
9
+ #
10
+ # @example Create a marker
11
+ # marker = account.lineup.create_marker(
12
+ # title: "Launch Day",
13
+ # starts_on: "2024-03-01",
14
+ # ends_on: "2024-03-01",
15
+ # color: "green"
16
+ # )
17
+ class LineupService < BaseService
18
+ # Creates a new marker on the lineup.
19
+ #
20
+ # @param title [String] marker title
21
+ # @param starts_on [String] start date (YYYY-MM-DD)
22
+ # @param ends_on [String] end date (YYYY-MM-DD)
23
+ # @param color [String, nil] marker color (white, red, orange, yellow, green, blue, aqua, purple, gray, pink, brown)
24
+ # @param description [String, nil] description in HTML
25
+ # @return [Hash] created marker
26
+ def create_marker(title:, starts_on:, ends_on:, color: nil, description: nil)
27
+ body = compact_params(
28
+ title: title,
29
+ starts_on: starts_on,
30
+ ends_on: ends_on,
31
+ color: color,
32
+ description: description
33
+ )
34
+ http_post("/lineup/markers.json", body: body).json
35
+ end
36
+
37
+ # Updates an existing marker.
38
+ #
39
+ # @param marker_id [Integer, String] marker ID
40
+ # @param title [String, nil] new title
41
+ # @param starts_on [String, nil] new start date (YYYY-MM-DD)
42
+ # @param ends_on [String, nil] new end date (YYYY-MM-DD)
43
+ # @param color [String, nil] new color
44
+ # @param description [String, nil] new description
45
+ # @return [Hash] updated marker
46
+ def update_marker(marker_id:, title: nil, starts_on: nil, ends_on: nil, color: nil, description: nil)
47
+ body = compact_params(
48
+ title: title,
49
+ starts_on: starts_on,
50
+ ends_on: ends_on,
51
+ color: color,
52
+ description: description
53
+ )
54
+ http_put("/lineup/markers/#{marker_id}", body: body).json
55
+ end
56
+
57
+ # Deletes a marker.
58
+ #
59
+ # @param marker_id [Integer, String] marker ID
60
+ # @return [void]
61
+ def delete_marker(marker_id:)
62
+ http_delete("/lineup/markers/#{marker_id}")
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for message board operations.
6
+ #
7
+ # Each project has a message board where team members can post messages
8
+ # (announcements, updates, etc.).
9
+ #
10
+ # @example Get a message board
11
+ # board = account.message_boards.get(project_id: 123, board_id: 456)
12
+ # puts "#{board["title"]} - #{board["messages_count"]} messages"
13
+ class MessageBoardsService < BaseService
14
+ # Gets a specific message board.
15
+ #
16
+ # @param project_id [Integer, String] project (bucket) ID
17
+ # @param board_id [Integer, String] message board ID
18
+ # @return [Hash] message board data
19
+ def get(project_id:, board_id:)
20
+ http_get(bucket_path(project_id, "/message_boards/#{board_id}")).json
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for message type (category) operations.
6
+ #
7
+ # Message types (also called categories) are used to categorize messages
8
+ # on a message board. Each message type has a name and icon.
9
+ #
10
+ # @example List message types
11
+ # account.message_types.list(project_id: 123).each do |type|
12
+ # puts "#{type["icon"]} #{type["name"]}"
13
+ # end
14
+ #
15
+ # @example Create a message type
16
+ # type = account.message_types.create(
17
+ # project_id: 123,
18
+ # name: "Announcement",
19
+ # icon: "📢"
20
+ # )
21
+ class MessageTypesService < BaseService
22
+ # Lists all message types in a project.
23
+ #
24
+ # @param project_id [Integer, String] project (bucket) ID
25
+ # @return [Enumerator<Hash>] message types
26
+ def list(project_id:)
27
+ paginate(bucket_path(project_id, "/categories.json"))
28
+ end
29
+
30
+ # Gets a message type by ID.
31
+ #
32
+ # @param project_id [Integer, String] project (bucket) ID
33
+ # @param type_id [Integer, String] message type ID
34
+ # @return [Hash] message type data
35
+ def get(project_id:, type_id:)
36
+ http_get(bucket_path(project_id, "/categories/#{type_id}")).json
37
+ end
38
+
39
+ # Creates a new message type in a project.
40
+ #
41
+ # @param project_id [Integer, String] project (bucket) ID
42
+ # @param name [String] message type name
43
+ # @param icon [String] message type icon
44
+ # @return [Hash] created message type
45
+ def create(project_id:, name:, icon:)
46
+ body = {
47
+ name: name,
48
+ icon: icon
49
+ }
50
+ http_post(bucket_path(project_id, "/categories.json"), body: body).json
51
+ end
52
+
53
+ # Updates an existing message type.
54
+ #
55
+ # @param project_id [Integer, String] project (bucket) ID
56
+ # @param type_id [Integer, String] message type ID
57
+ # @param name [String, nil] new name
58
+ # @param icon [String, nil] new icon
59
+ # @return [Hash] updated message type
60
+ def update(project_id:, type_id:, name: nil, icon: nil)
61
+ body = compact_params(
62
+ name: name,
63
+ icon: icon
64
+ )
65
+ http_put(bucket_path(project_id, "/categories/#{type_id}"), body: body).json
66
+ end
67
+
68
+ # Deletes a message type from a project.
69
+ #
70
+ # @param project_id [Integer, String] project (bucket) ID
71
+ # @param type_id [Integer, String] message type ID
72
+ # @return [void]
73
+ def delete(project_id:, type_id:)
74
+ http_delete(bucket_path(project_id, "/categories/#{type_id}"))
75
+ nil
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for message operations.
6
+ #
7
+ # Messages are posts on a project's message board. They have a subject,
8
+ # content, and can be categorized with message types.
9
+ #
10
+ # @example List messages
11
+ # account.messages.list(project_id: 123, board_id: 456).each do |message|
12
+ # puts "#{message["subject"]} by #{message["creator"]["name"]}"
13
+ # end
14
+ #
15
+ # @example Create a message
16
+ # message = account.messages.create(
17
+ # project_id: 123,
18
+ # board_id: 456,
19
+ # subject: "Project Update",
20
+ # content: "<p>Here's what happened this week...</p>"
21
+ # )
22
+ #
23
+ # @example Pin a message
24
+ # account.messages.pin(project_id: 123, message_id: 789)
25
+ class MessagesService < BaseService
26
+ # Lists all messages on a message board.
27
+ #
28
+ # @param project_id [Integer, String] project (bucket) ID
29
+ # @param board_id [Integer, String] message board ID
30
+ # @return [Enumerator<Hash>] messages
31
+ def list(project_id:, board_id:)
32
+ paginate(bucket_path(project_id, "/message_boards/#{board_id}/messages.json"))
33
+ end
34
+
35
+ # Gets a specific message.
36
+ #
37
+ # @param project_id [Integer, String] project (bucket) ID
38
+ # @param message_id [Integer, String] message ID
39
+ # @return [Hash] message data
40
+ def get(project_id:, message_id:)
41
+ http_get(bucket_path(project_id, "/messages/#{message_id}")).json
42
+ end
43
+
44
+ # Creates a new message on a message board.
45
+ #
46
+ # @param project_id [Integer, String] project (bucket) ID
47
+ # @param board_id [Integer, String] message board ID
48
+ # @param subject [String] message title
49
+ # @param content [String, nil] message body in HTML
50
+ # @param status [String, nil] "drafted" or "active" (defaults to active)
51
+ # @param category_id [Integer, nil] message type ID
52
+ # @return [Hash] created message
53
+ def create(project_id:, board_id:, subject:, content: nil, status: nil, category_id: nil)
54
+ body = compact_params(
55
+ subject: subject,
56
+ content: content,
57
+ status: status,
58
+ category_id: category_id
59
+ )
60
+ http_post(bucket_path(project_id, "/message_boards/#{board_id}/messages.json"), body: body).json
61
+ end
62
+
63
+ # Updates an existing message.
64
+ #
65
+ # @param project_id [Integer, String] project (bucket) ID
66
+ # @param message_id [Integer, String] message ID
67
+ # @param subject [String, nil] new title
68
+ # @param content [String, nil] new content in HTML
69
+ # @param status [String, nil] "drafted" or "active"
70
+ # @param category_id [Integer, nil] message type ID
71
+ # @return [Hash] updated message
72
+ def update(project_id:, message_id:, subject: nil, content: nil, status: nil, category_id: nil)
73
+ body = compact_params(
74
+ subject: subject,
75
+ content: content,
76
+ status: status,
77
+ category_id: category_id
78
+ )
79
+ http_put(bucket_path(project_id, "/messages/#{message_id}"), body: body).json
80
+ end
81
+
82
+ # Pins a message to the top of the message board.
83
+ #
84
+ # @param project_id [Integer, String] project (bucket) ID
85
+ # @param message_id [Integer, String] message ID
86
+ # @return [void]
87
+ def pin(project_id:, message_id:)
88
+ http_post(bucket_path(project_id, "/recordings/#{message_id}/pin.json"))
89
+ nil
90
+ end
91
+
92
+ # Unpins a message from the top of the message board.
93
+ #
94
+ # @param project_id [Integer, String] project (bucket) ID
95
+ # @param message_id [Integer, String] message ID
96
+ # @return [void]
97
+ def unpin(project_id:, message_id:)
98
+ http_delete(bucket_path(project_id, "/recordings/#{message_id}/pin.json"))
99
+ nil
100
+ end
101
+
102
+ # Archives a message.
103
+ #
104
+ # @param project_id [Integer, String] project (bucket) ID
105
+ # @param message_id [Integer, String] message ID
106
+ # @return [void]
107
+ def archive(project_id:, message_id:)
108
+ http_put(bucket_path(project_id, "/recordings/#{message_id}/status/archived.json"))
109
+ nil
110
+ end
111
+
112
+ # Restores an archived message to active status.
113
+ #
114
+ # @param project_id [Integer, String] project (bucket) ID
115
+ # @param message_id [Integer, String] message ID
116
+ # @return [void]
117
+ def unarchive(project_id:, message_id:)
118
+ http_put(bucket_path(project_id, "/recordings/#{message_id}/status/active.json"))
119
+ nil
120
+ end
121
+
122
+ # Moves a message to the trash.
123
+ #
124
+ # @param project_id [Integer, String] project (bucket) ID
125
+ # @param message_id [Integer, String] message ID
126
+ # @return [void]
127
+ def trash(project_id:, message_id:)
128
+ http_put(bucket_path(project_id, "/recordings/#{message_id}/status/trashed.json"))
129
+ nil
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for people operations.
6
+ #
7
+ # People are the users in your Basecamp account.
8
+ #
9
+ # @example List all people
10
+ # account.people.list.each do |person|
11
+ # puts "#{person["name"]} <#{person["email_address"]}>"
12
+ # end
13
+ #
14
+ # @example Get current user's profile
15
+ # me = account.people.me
16
+ # puts "Logged in as #{me["name"]}"
17
+ class PeopleService < BaseService
18
+ # Lists all people in the account.
19
+ #
20
+ # @return [Enumerator<Hash>] people
21
+ def list
22
+ paginate("/people.json")
23
+ end
24
+
25
+ # Gets a specific person.
26
+ #
27
+ # @param person_id [Integer, String] person ID
28
+ # @return [Hash] person data
29
+ def get(person_id:)
30
+ http_get("/people/#{person_id}").json
31
+ end
32
+
33
+ # Gets the current user's profile.
34
+ #
35
+ # @return [Hash] current user's profile
36
+ def me
37
+ http_get("/my/profile.json").json
38
+ end
39
+
40
+ # Lists all people who can be pinged (mentioned).
41
+ #
42
+ # @return [Enumerator<Hash>] pingable people
43
+ def list_pingable
44
+ paginate("/circles/people.json")
45
+ end
46
+
47
+ # Lists all people in a project.
48
+ #
49
+ # @param project_id [Integer, String] project ID
50
+ # @return [Enumerator<Hash>] project members
51
+ def list_project_people(project_id:)
52
+ paginate("/projects/#{project_id}/people.json")
53
+ end
54
+
55
+ # Updates project access for users.
56
+ #
57
+ # @param project_id [Integer, String] project ID
58
+ # @param grant [Array<Integer>, nil] user IDs to grant access
59
+ # @param revoke [Array<Integer>, nil] user IDs to revoke access
60
+ # @param create [Array<Hash>, nil] new users to create and grant access
61
+ # @return [void]
62
+ def update_project_access(project_id:, grant: nil, revoke: nil, create: nil)
63
+ body = compact_params(
64
+ grant: grant,
65
+ revoke: revoke,
66
+ create: create
67
+ )
68
+ http_put("/projects/#{project_id}/people/users.json", body: body)
69
+ nil
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for project (Basecamp) operations.
6
+ #
7
+ # @example List all projects
8
+ # account.projects.list.each do |project|
9
+ # puts "#{project["name"]} (#{project["id"]})"
10
+ # end
11
+ #
12
+ # @example Get a specific project
13
+ # project = account.projects.get(123)
14
+ # puts project["name"]
15
+ #
16
+ # @example Create a project
17
+ # project = account.projects.create(name: "My Project", description: "A new project")
18
+ class ProjectsService < BaseService
19
+ # Lists all projects in the account.
20
+ #
21
+ # @param status [String, nil] filter by status ("active", "archived", "trashed")
22
+ # @return [Enumerator<Hash>] projects
23
+ def list(status: nil)
24
+ params = compact_params(status: status)
25
+ paginate("/projects.json", params: params)
26
+ end
27
+
28
+ # Gets a specific project.
29
+ #
30
+ # @param project_id [Integer, String] project ID
31
+ # @return [Hash] project data
32
+ def get(project_id)
33
+ http_get("/projects/#{project_id}.json").json
34
+ end
35
+
36
+ # Creates a new project.
37
+ #
38
+ # @param name [String] project name
39
+ # @param description [String, nil] project description
40
+ # @return [Hash] created project
41
+ def create(name:, description: nil)
42
+ body = compact_params(name: name, description: description)
43
+ http_post("/projects.json", body: body).json
44
+ end
45
+
46
+ # Updates a project.
47
+ #
48
+ # @param project_id [Integer, String] project ID
49
+ # @param name [String, nil] new name
50
+ # @param description [String, nil] new description
51
+ # @return [Hash] updated project
52
+ def update(project_id, name: nil, description: nil)
53
+ body = compact_params(name: name, description: description)
54
+ http_put("/projects/#{project_id}.json", body: body).json
55
+ end
56
+
57
+ # Trashes a project.
58
+ #
59
+ # @param project_id [Integer, String] project ID
60
+ # @return [void]
61
+ def trash(project_id)
62
+ http_delete("/projects/#{project_id}.json")
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for recording operations.
6
+ #
7
+ # Recordings are the base type for most content in Basecamp, including
8
+ # todos, messages, comments, documents, uploads, etc. This service
9
+ # provides common operations that work across all recording types.
10
+ #
11
+ # @example List all recordings in a project
12
+ # account.recordings.list(type: "Todo", bucket: 123).each do |recording|
13
+ # puts "#{recording["title"]} - #{recording["status"]}"
14
+ # end
15
+ #
16
+ # @example Archive a recording
17
+ # account.recordings.archive(project_id: 123, recording_id: 456)
18
+ class RecordingsService < BaseService
19
+ # Lists recordings across projects.
20
+ #
21
+ # @param type [String] recording type (e.g., "Todo", "Message", "Comment")
22
+ # @param bucket [Integer, nil] filter by project ID
23
+ # @param status [String, nil] filter by status ("active", "archived", "trashed")
24
+ # @param sort [String, nil] sort field ("created_at", "updated_at")
25
+ # @param direction [String, nil] sort direction ("asc", "desc")
26
+ # @return [Enumerator<Hash>] recordings
27
+ def list(type:, bucket: nil, status: nil, sort: nil, direction: nil)
28
+ params = compact_params(
29
+ type: type,
30
+ bucket: bucket,
31
+ status: status,
32
+ sort: sort,
33
+ direction: direction
34
+ )
35
+ paginate("/projects/recordings.json", params: params)
36
+ end
37
+
38
+ # Gets a specific recording.
39
+ #
40
+ # @param project_id [Integer, String] project (bucket) ID
41
+ # @param recording_id [Integer, String] recording ID
42
+ # @return [Hash] recording data
43
+ def get(project_id:, recording_id:)
44
+ http_get(bucket_path(project_id, "/recordings/#{recording_id}")).json
45
+ end
46
+
47
+ # Archives a recording.
48
+ #
49
+ # @param project_id [Integer, String] project (bucket) ID
50
+ # @param recording_id [Integer, String] recording ID
51
+ # @return [void]
52
+ def archive(project_id:, recording_id:)
53
+ http_put(bucket_path(project_id, "/recordings/#{recording_id}/status/archived.json"))
54
+ nil
55
+ end
56
+
57
+ # Unarchives a recording (restores to active).
58
+ #
59
+ # @param project_id [Integer, String] project (bucket) ID
60
+ # @param recording_id [Integer, String] recording ID
61
+ # @return [void]
62
+ def unarchive(project_id:, recording_id:)
63
+ http_put(bucket_path(project_id, "/recordings/#{recording_id}/status/active.json"))
64
+ nil
65
+ end
66
+
67
+ # Moves a recording to the trash.
68
+ #
69
+ # @param project_id [Integer, String] project (bucket) ID
70
+ # @param recording_id [Integer, String] recording ID
71
+ # @return [void]
72
+ def trash(project_id:, recording_id:)
73
+ http_put(bucket_path(project_id, "/recordings/#{recording_id}/status/trashed.json"))
74
+ nil
75
+ end
76
+
77
+ # Lists events (change history) for a recording.
78
+ #
79
+ # @param project_id [Integer, String] project (bucket) ID
80
+ # @param recording_id [Integer, String] recording ID
81
+ # @return [Enumerator<Hash>] events
82
+ def list_events(project_id:, recording_id:)
83
+ paginate(bucket_path(project_id, "/recordings/#{recording_id}/events.json"))
84
+ end
85
+
86
+ # Gets the subscription status for a recording.
87
+ #
88
+ # @param project_id [Integer, String] project (bucket) ID
89
+ # @param recording_id [Integer, String] recording ID
90
+ # @return [Hash] subscription data
91
+ def get_subscription(project_id:, recording_id:)
92
+ http_get(bucket_path(project_id, "/recordings/#{recording_id}/subscription.json")).json
93
+ end
94
+
95
+ # Subscribes to a recording.
96
+ #
97
+ # @param project_id [Integer, String] project (bucket) ID
98
+ # @param recording_id [Integer, String] recording ID
99
+ # @return [Hash] subscription data
100
+ def subscribe(project_id:, recording_id:)
101
+ http_post(bucket_path(project_id, "/recordings/#{recording_id}/subscription.json")).json
102
+ end
103
+
104
+ # Unsubscribes from a recording.
105
+ #
106
+ # @param project_id [Integer, String] project (bucket) ID
107
+ # @param recording_id [Integer, String] recording ID
108
+ # @return [void]
109
+ def unsubscribe(project_id:, recording_id:)
110
+ http_delete(bucket_path(project_id, "/recordings/#{recording_id}/subscription.json"))
111
+ nil
112
+ end
113
+
114
+ # Sets client visibility for a recording.
115
+ #
116
+ # @param project_id [Integer, String] project (bucket) ID
117
+ # @param recording_id [Integer, String] recording ID
118
+ # @param visible [Boolean] whether clients can see this recording
119
+ # @return [void]
120
+ def set_client_visibility(project_id:, recording_id:, visible:)
121
+ http_put(bucket_path(project_id, "/recordings/#{recording_id}/client_visibility.json"),
122
+ body: { visible: visible })
123
+ nil
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for report operations.
6
+ #
7
+ # Provides access to various report types including timesheet and assigned todos reports.
8
+ #
9
+ # @example Get account-wide timesheet
10
+ # result = account.reports.timesheet
11
+ # result["entries"].each { |e| puts e["hours"] }
12
+ #
13
+ # @example Get list of people with assigned todos
14
+ # people = account.reports.assignable_people.to_a
15
+ #
16
+ # @example Get todos assigned to a specific person
17
+ # result = account.reports.assigned_todos(person_id: 123)
18
+ # result["todos"].each { |todo| puts todo["content"] }
19
+ class ReportsService < BaseService
20
+ # Returns the account-wide timesheet report.
21
+ # This includes time entries across all projects in the account.
22
+ #
23
+ # @param from [String, nil] filter entries on or after this date (ISO 8601)
24
+ # @param to [String, nil] filter entries on or before this date (ISO 8601)
25
+ # @param person_id [Integer, nil] filter entries by a specific person
26
+ # @return [Hash] timesheet report object with "entries" array
27
+ def timesheet(from: nil, to: nil, person_id: nil)
28
+ params = compact_params(from: from, to: to, person_id: person_id)
29
+ response = http_get("/reports/timesheet.json", params: params)
30
+ response.json
31
+ end
32
+
33
+ # Returns the timesheet report for a specific project.
34
+ #
35
+ # @param project_id [Integer, String] project (bucket) ID
36
+ # @param from [String, nil] filter entries on or after this date (ISO 8601)
37
+ # @param to [String, nil] filter entries on or before this date (ISO 8601)
38
+ # @param person_id [Integer, nil] filter entries by a specific person
39
+ # @return [Hash] timesheet report object with "entries" array
40
+ def project_timesheet(project_id:, from: nil, to: nil, person_id: nil)
41
+ params = compact_params(from: from, to: to, person_id: person_id)
42
+ response = http_get(bucket_path(project_id, "/timesheet.json"), params: params)
43
+ response.json
44
+ end
45
+
46
+ # Returns the timesheet report for a specific recording within a project.
47
+ #
48
+ # @param project_id [Integer, String] project (bucket) ID
49
+ # @param recording_id [Integer, String] recording ID (e.g., a todo or message)
50
+ # @param from [String, nil] filter entries on or after this date (ISO 8601)
51
+ # @param to [String, nil] filter entries on or before this date (ISO 8601)
52
+ # @param person_id [Integer, nil] filter entries by a specific person
53
+ # @return [Hash] timesheet report object with "entries" array
54
+ def recording_timesheet(project_id:, recording_id:, from: nil, to: nil, person_id: nil)
55
+ params = compact_params(from: from, to: to, person_id: person_id)
56
+ response = http_get(bucket_path(project_id, "/recordings/#{recording_id}/timesheet.json"), params: params)
57
+ response.json
58
+ end
59
+
60
+ # Returns the list of people who have assigned todos.
61
+ # Use assigned_todos(person_id:) to get the actual todos for a specific person.
62
+ #
63
+ # @return [Enumerator<Hash>] list of Person objects
64
+ def assignable_people
65
+ paginate("/reports/todos/assigned.json")
66
+ end
67
+
68
+ # Returns all todos assigned to a specific person.
69
+ #
70
+ # @param person_id [Integer, String] person ID
71
+ # @param group_by [String, nil] grouping method: "bucket" or "date"
72
+ # @return [Hash] object with "person", "grouped_by", and "todos" keys
73
+ def assigned_todos(person_id:, group_by: nil)
74
+ params = compact_params(group_by: group_by)
75
+ response = http_get("/reports/todos/assigned/#{person_id}", params: params)
76
+ response.json
77
+ end
78
+ end
79
+ end
80
+ end