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,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for card step (checklist item) operations.
6
+ #
7
+ # Steps are checklist items on cards in card tables.
8
+ #
9
+ # @example Create a step
10
+ # step = account.card_steps.create(
11
+ # project_id: 123,
12
+ # card_id: 456,
13
+ # title: "Review code",
14
+ # due_on: "2024-12-15"
15
+ # )
16
+ #
17
+ # @example Complete a step
18
+ # account.card_steps.complete(project_id: 123, step_id: 789)
19
+ class CardStepsService < BaseService
20
+ # Creates a new step on a card.
21
+ #
22
+ # @param project_id [Integer, String] project (bucket) ID
23
+ # @param card_id [Integer, String] card ID
24
+ # @param title [String] step title
25
+ # @param due_on [String, nil] due date (YYYY-MM-DD)
26
+ # @param assignees [Array<Integer>, nil] person IDs to assign
27
+ # @return [Hash] created step
28
+ def create(project_id:, card_id:, title:, due_on: nil, assignees: nil)
29
+ body = compact_params(
30
+ title: title,
31
+ due_on: due_on,
32
+ assignees: assignees
33
+ )
34
+ http_post(bucket_path(project_id, "/card_tables/cards/#{card_id}/steps.json"), body: body).json
35
+ end
36
+
37
+ # Updates an existing step.
38
+ #
39
+ # @param project_id [Integer, String] project (bucket) ID
40
+ # @param step_id [Integer, String] step ID
41
+ # @param title [String, nil] new title
42
+ # @param due_on [String, nil] new due date (YYYY-MM-DD)
43
+ # @param assignees [Array<Integer>, nil] person IDs to assign
44
+ # @return [Hash] updated step
45
+ def update(project_id:, step_id:, title: nil, due_on: nil, assignees: nil)
46
+ body = compact_params(
47
+ title: title,
48
+ due_on: due_on,
49
+ assignees: assignees
50
+ )
51
+ http_put(bucket_path(project_id, "/card_tables/steps/#{step_id}.json"), body: body).json
52
+ end
53
+
54
+ # Marks a step as completed.
55
+ #
56
+ # @param project_id [Integer, String] project (bucket) ID
57
+ # @param step_id [Integer, String] step ID
58
+ # @return [Hash] updated step
59
+ def complete(project_id:, step_id:)
60
+ http_put(bucket_path(project_id, "/card_tables/steps/#{step_id}/completions.json")).json
61
+ end
62
+
63
+ # Marks a step as incomplete.
64
+ #
65
+ # @param project_id [Integer, String] project (bucket) ID
66
+ # @param step_id [Integer, String] step ID
67
+ # @return [Hash] updated step
68
+ def uncomplete(project_id:, step_id:)
69
+ http_delete(bucket_path(project_id, "/card_tables/steps/#{step_id}/completions.json")).json
70
+ end
71
+
72
+ # Changes the position of a step within a card.
73
+ #
74
+ # @param project_id [Integer, String] project (bucket) ID
75
+ # @param card_id [Integer, String] card ID
76
+ # @param step_id [Integer, String] step ID
77
+ # @param position [Integer] new position (0-indexed)
78
+ # @return [void]
79
+ def reposition(project_id:, card_id:, step_id:, position:)
80
+ http_post(bucket_path(project_id, "/card_tables/cards/#{card_id}/positions.json"),
81
+ body: { source_id: step_id, position: position })
82
+ nil
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for card table (kanban board) operations.
6
+ #
7
+ # Card Tables are kanban-style boards with columns containing cards.
8
+ #
9
+ # @example Get a card table
10
+ # table = account.card_tables.get(project_id: 123, card_table_id: 456)
11
+ # puts "#{table["title"]} - #{table["lists"].length} columns"
12
+ class CardTablesService < BaseService
13
+ # Gets a card table by ID.
14
+ #
15
+ # @param project_id [Integer, String] project (bucket) ID
16
+ # @param card_table_id [Integer, String] card table ID
17
+ # @return [Hash] card table with its columns
18
+ def get(project_id:, card_table_id:)
19
+ http_get(bucket_path(project_id, "/card_tables/#{card_table_id}.json")).json
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for card operations within card tables.
6
+ #
7
+ # Cards are items in card table columns. They can have steps (checklist items),
8
+ # assignees, and due dates.
9
+ #
10
+ # @example List cards in a column
11
+ # account.cards.list(project_id: 123, column_id: 456).each do |card|
12
+ # puts "#{card["title"]} - #{card["completed"] ? "done" : "pending"}"
13
+ # end
14
+ #
15
+ # @example Create a card
16
+ # card = account.cards.create(
17
+ # project_id: 123,
18
+ # column_id: 456,
19
+ # title: "New Feature",
20
+ # content: "<p>Feature description</p>",
21
+ # due_on: "2024-12-31"
22
+ # )
23
+ class CardsService < BaseService
24
+ # Lists all cards in a column.
25
+ #
26
+ # @param project_id [Integer, String] project (bucket) ID
27
+ # @param column_id [Integer, String] column ID
28
+ # @return [Enumerator<Hash>] cards
29
+ def list(project_id:, column_id:)
30
+ paginate(bucket_path(project_id, "/card_tables/lists/#{column_id}/cards.json"))
31
+ end
32
+
33
+ # Gets a card by ID.
34
+ #
35
+ # @param project_id [Integer, String] project (bucket) ID
36
+ # @param card_id [Integer, String] card ID
37
+ # @return [Hash] card data
38
+ def get(project_id:, card_id:)
39
+ http_get(bucket_path(project_id, "/card_tables/cards/#{card_id}.json")).json
40
+ end
41
+
42
+ # Creates a new card in a column.
43
+ #
44
+ # @param project_id [Integer, String] project (bucket) ID
45
+ # @param column_id [Integer, String] column ID
46
+ # @param title [String] card title
47
+ # @param content [String, nil] card body in HTML
48
+ # @param due_on [String, nil] due date (YYYY-MM-DD)
49
+ # @param notify [Boolean, nil] notify assignees
50
+ # @return [Hash] created card
51
+ def create(project_id:, column_id:, title:, content: nil, due_on: nil, notify: nil)
52
+ body = compact_params(
53
+ title: title,
54
+ content: content,
55
+ due_on: due_on,
56
+ notify: notify
57
+ )
58
+ http_post(bucket_path(project_id, "/card_tables/lists/#{column_id}/cards.json"), body: body).json
59
+ end
60
+
61
+ # Updates an existing card.
62
+ #
63
+ # @param project_id [Integer, String] project (bucket) ID
64
+ # @param card_id [Integer, String] card ID
65
+ # @param title [String, nil] new title
66
+ # @param content [String, nil] new content
67
+ # @param due_on [String, nil] new due date (YYYY-MM-DD)
68
+ # @param assignee_ids [Array<Integer>, nil] person IDs to assign
69
+ # @return [Hash] updated card
70
+ def update(project_id:, card_id:, title: nil, content: nil, due_on: nil, assignee_ids: nil)
71
+ body = compact_params(
72
+ title: title,
73
+ content: content,
74
+ due_on: due_on,
75
+ assignee_ids: assignee_ids
76
+ )
77
+ http_put(bucket_path(project_id, "/card_tables/cards/#{card_id}.json"), body: body).json
78
+ end
79
+
80
+ # Moves a card to a different column.
81
+ #
82
+ # @param project_id [Integer, String] project (bucket) ID
83
+ # @param card_id [Integer, String] card ID
84
+ # @param column_id [Integer, String] destination column ID
85
+ # @return [void]
86
+ def move(project_id:, card_id:, column_id:)
87
+ http_post(bucket_path(project_id, "/card_tables/cards/#{card_id}/moves.json"),
88
+ body: { column_id: column_id })
89
+ nil
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for automatic check-in operations.
6
+ #
7
+ # Checkins (also called Automatic Check-ins) are scheduled questions
8
+ # that get sent to team members. The questionnaire contains questions,
9
+ # and each question can have multiple answers from different people.
10
+ #
11
+ # @example List questions
12
+ # account.checkins.list_questions(project_id: 123, questionnaire_id: 456).each do |q|
13
+ # puts "#{q["title"]} - #{q["paused"] ? "(paused)" : ""}"
14
+ # end
15
+ #
16
+ # @example Create an answer
17
+ # answer = account.checkins.create_answer(
18
+ # project_id: 123,
19
+ # question_id: 456,
20
+ # content: "<p>Making great progress!</p>"
21
+ # )
22
+ class CheckinsService < BaseService
23
+ # Gets a questionnaire by ID.
24
+ #
25
+ # @param project_id [Integer, String] project (bucket) ID
26
+ # @param questionnaire_id [Integer, String] questionnaire ID
27
+ # @return [Hash] questionnaire data
28
+ def get_questionnaire(project_id:, questionnaire_id:)
29
+ http_get(bucket_path(project_id, "/questionnaires/#{questionnaire_id}.json")).json
30
+ end
31
+
32
+ # Lists all questions in a questionnaire.
33
+ #
34
+ # @param project_id [Integer, String] project (bucket) ID
35
+ # @param questionnaire_id [Integer, String] questionnaire ID
36
+ # @return [Enumerator<Hash>] questions
37
+ def list_questions(project_id:, questionnaire_id:)
38
+ paginate(bucket_path(project_id, "/questionnaires/#{questionnaire_id}/questions.json"))
39
+ end
40
+
41
+ # Gets a question by ID.
42
+ #
43
+ # @param project_id [Integer, String] project (bucket) ID
44
+ # @param question_id [Integer, String] question ID
45
+ # @return [Hash] question data
46
+ def get_question(project_id:, question_id:)
47
+ http_get(bucket_path(project_id, "/questions/#{question_id}.json")).json
48
+ end
49
+
50
+ # Creates a new question in a questionnaire.
51
+ #
52
+ # @param project_id [Integer, String] project (bucket) ID
53
+ # @param questionnaire_id [Integer, String] questionnaire ID
54
+ # @param title [String] question text
55
+ # @param schedule [Hash] schedule configuration with frequency, days, hour, minute
56
+ # @return [Hash] created question
57
+ def create_question(project_id:, questionnaire_id:, title:, schedule:)
58
+ body = {
59
+ title: title,
60
+ schedule: schedule
61
+ }
62
+ http_post(bucket_path(project_id, "/questionnaires/#{questionnaire_id}/questions.json"), body: body).json
63
+ end
64
+
65
+ # Updates an existing question.
66
+ #
67
+ # @param project_id [Integer, String] project (bucket) ID
68
+ # @param question_id [Integer, String] question ID
69
+ # @param title [String, nil] new question text
70
+ # @param schedule [Hash, nil] new schedule configuration
71
+ # @param paused [Boolean, nil] whether the question is paused
72
+ # @return [Hash] updated question
73
+ def update_question(project_id:, question_id:, title: nil, schedule: nil, paused: nil)
74
+ body = compact_params(
75
+ title: title,
76
+ schedule: schedule,
77
+ paused: paused
78
+ )
79
+ http_put(bucket_path(project_id, "/questions/#{question_id}.json"), body: body).json
80
+ end
81
+
82
+ # Lists all answers for a question.
83
+ #
84
+ # @param project_id [Integer, String] project (bucket) ID
85
+ # @param question_id [Integer, String] question ID
86
+ # @return [Enumerator<Hash>] answers
87
+ def list_answers(project_id:, question_id:)
88
+ paginate(bucket_path(project_id, "/questions/#{question_id}/answers.json"))
89
+ end
90
+
91
+ # Gets an answer by ID.
92
+ #
93
+ # @param project_id [Integer, String] project (bucket) ID
94
+ # @param answer_id [Integer, String] answer ID
95
+ # @return [Hash] answer data
96
+ def get_answer(project_id:, answer_id:)
97
+ http_get(bucket_path(project_id, "/question_answers/#{answer_id}.json")).json
98
+ end
99
+
100
+ # Creates a new answer for a question.
101
+ #
102
+ # @param project_id [Integer, String] project (bucket) ID
103
+ # @param question_id [Integer, String] question ID
104
+ # @param content [String] answer content in HTML
105
+ # @param group_on [String, nil] date to group the answer with (ISO 8601)
106
+ # @return [Hash] created answer
107
+ def create_answer(project_id:, question_id:, content:, group_on: nil)
108
+ body = compact_params(
109
+ content: content,
110
+ group_on: group_on
111
+ )
112
+ http_post(bucket_path(project_id, "/questions/#{question_id}/answers.json"), body: body).json
113
+ end
114
+
115
+ # Updates an existing answer.
116
+ #
117
+ # @param project_id [Integer, String] project (bucket) ID
118
+ # @param answer_id [Integer, String] answer ID
119
+ # @param content [String] updated answer content in HTML
120
+ # @return [void]
121
+ def update_answer(project_id:, answer_id:, content:)
122
+ http_put(bucket_path(project_id, "/question_answers/#{answer_id}.json"), body: { content: content })
123
+ nil
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for client approval operations.
6
+ #
7
+ # Client approvals allow you to request approval from clients
8
+ # on specific deliverables or decisions within a project.
9
+ #
10
+ # @example List client approvals
11
+ # account.client_approvals.list(project_id: 123).each do |approval|
12
+ # puts "#{approval["subject"]} - #{approval["approval_status"]}"
13
+ # end
14
+ class ClientApprovalsService < BaseService
15
+ # Lists all client approvals in a project.
16
+ #
17
+ # @param project_id [Integer, String] project (bucket) ID
18
+ # @return [Enumerator<Hash>] client approvals
19
+ def list(project_id:)
20
+ paginate(bucket_path(project_id, "/client/approvals.json"))
21
+ end
22
+
23
+ # Gets a client approval by ID.
24
+ #
25
+ # @param project_id [Integer, String] project (bucket) ID
26
+ # @param approval_id [Integer, String] client approval ID
27
+ # @return [Hash] client approval data
28
+ def get(project_id:, approval_id:)
29
+ http_get(bucket_path(project_id, "/client/approvals/#{approval_id}.json")).json
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for client correspondence operations.
6
+ #
7
+ # Client correspondences are messages sent to and from clients
8
+ # within a project's client portal.
9
+ #
10
+ # @example List client correspondences
11
+ # account.client_correspondences.list(project_id: 123).each do |c|
12
+ # puts "#{c["subject"]} - #{c["replies_count"]} replies"
13
+ # end
14
+ class ClientCorrespondencesService < BaseService
15
+ # Lists all client correspondences in a project.
16
+ #
17
+ # @param project_id [Integer, String] project (bucket) ID
18
+ # @return [Enumerator<Hash>] client correspondences
19
+ def list(project_id:)
20
+ paginate(bucket_path(project_id, "/client/correspondences.json"))
21
+ end
22
+
23
+ # Gets a client correspondence by ID.
24
+ #
25
+ # @param project_id [Integer, String] project (bucket) ID
26
+ # @param correspondence_id [Integer, String] client correspondence ID
27
+ # @return [Hash] client correspondence data
28
+ def get(project_id:, correspondence_id:)
29
+ http_get(bucket_path(project_id, "/client/correspondences/#{correspondence_id}.json")).json
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for client reply operations.
6
+ #
7
+ # Client replies are responses to client correspondences or approvals
8
+ # within a project's client portal.
9
+ #
10
+ # @example List client replies
11
+ # account.client_replies.list(project_id: 123, recording_id: 456).each do |r|
12
+ # puts "#{r["creator"]["name"]}: #{r["content"]}"
13
+ # end
14
+ class ClientRepliesService < BaseService
15
+ # Lists all replies for a client recording (correspondence or approval).
16
+ #
17
+ # @param project_id [Integer, String] project (bucket) ID
18
+ # @param recording_id [Integer, String] parent correspondence/approval ID
19
+ # @return [Enumerator<Hash>] client replies
20
+ def list(project_id:, recording_id:)
21
+ paginate(bucket_path(project_id, "/client/recordings/#{recording_id}/replies.json"))
22
+ end
23
+
24
+ # Gets a specific client reply.
25
+ #
26
+ # @param project_id [Integer, String] project (bucket) ID
27
+ # @param recording_id [Integer, String] parent correspondence/approval ID
28
+ # @param reply_id [Integer, String] client reply ID
29
+ # @return [Hash] client reply data
30
+ def get(project_id:, recording_id:, reply_id:)
31
+ http_get(bucket_path(project_id, "/client/recordings/#{recording_id}/replies/#{reply_id}.json")).json
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for comment operations.
6
+ #
7
+ # Comments can be added to most recordings (todos, messages, etc.)
8
+ # in Basecamp. They support HTML content.
9
+ #
10
+ # @example List comments on a todo
11
+ # account.comments.list(project_id: 123, recording_id: 456).each do |comment|
12
+ # puts "#{comment["creator"]["name"]}: #{comment["content"]}"
13
+ # end
14
+ #
15
+ # @example Create a comment
16
+ # comment = account.comments.create(
17
+ # project_id: 123,
18
+ # recording_id: 456,
19
+ # content: "<p>Great work on this!</p>"
20
+ # )
21
+ class CommentsService < BaseService
22
+ # Lists all comments on a recording.
23
+ #
24
+ # @param project_id [Integer, String] project (bucket) ID
25
+ # @param recording_id [Integer, String] recording ID (todo, message, etc.)
26
+ # @return [Enumerator<Hash>] comments
27
+ def list(project_id:, recording_id:)
28
+ paginate(bucket_path(project_id, "/recordings/#{recording_id}/comments.json"))
29
+ end
30
+
31
+ # Gets a specific comment.
32
+ #
33
+ # @param project_id [Integer, String] project (bucket) ID
34
+ # @param comment_id [Integer, String] comment ID
35
+ # @return [Hash] comment data
36
+ def get(project_id:, comment_id:)
37
+ http_get(bucket_path(project_id, "/comments/#{comment_id}.json")).json
38
+ end
39
+
40
+ # Creates a new comment on a recording.
41
+ #
42
+ # @param project_id [Integer, String] project (bucket) ID
43
+ # @param recording_id [Integer, String] recording ID to comment on
44
+ # @param content [String] comment content in HTML
45
+ # @return [Hash] created comment
46
+ def create(project_id:, recording_id:, content:)
47
+ body = { content: content }
48
+ http_post(bucket_path(project_id, "/recordings/#{recording_id}/comments.json"), body: body).json
49
+ end
50
+
51
+ # Updates an existing comment.
52
+ #
53
+ # @param project_id [Integer, String] project (bucket) ID
54
+ # @param comment_id [Integer, String] comment ID
55
+ # @param content [String] new comment content in HTML
56
+ # @return [Hash] updated comment
57
+ def update(project_id:, comment_id:, content:)
58
+ body = { content: content }
59
+ http_put(bucket_path(project_id, "/comments/#{comment_id}.json"), body: body).json
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for document operations.
6
+ #
7
+ # Documents are rich text files stored within vaults. They support
8
+ # HTML content and can be in draft or active status.
9
+ #
10
+ # @example List documents in a vault
11
+ # account.documents.list(project_id: 123, vault_id: 456).each do |doc|
12
+ # puts "#{doc["title"]} - #{doc["comments_count"]} comments"
13
+ # end
14
+ #
15
+ # @example Create a document
16
+ # doc = account.documents.create(
17
+ # project_id: 123,
18
+ # vault_id: 456,
19
+ # title: "Meeting Notes",
20
+ # content: "<p>Notes from today's meeting...</p>"
21
+ # )
22
+ class DocumentsService < BaseService
23
+ # Lists all documents in a vault.
24
+ #
25
+ # @param project_id [Integer, String] project (bucket) ID
26
+ # @param vault_id [Integer, String] vault ID
27
+ # @return [Enumerator<Hash>] documents
28
+ def list(project_id:, vault_id:)
29
+ paginate(bucket_path(project_id, "/vaults/#{vault_id}/documents.json"))
30
+ end
31
+
32
+ # Gets a specific document.
33
+ #
34
+ # @param project_id [Integer, String] project (bucket) ID
35
+ # @param document_id [Integer, String] document ID
36
+ # @return [Hash] document data
37
+ def get(project_id:, document_id:)
38
+ http_get(bucket_path(project_id, "/documents/#{document_id}.json")).json
39
+ end
40
+
41
+ # Creates a new document in a vault.
42
+ #
43
+ # @param project_id [Integer, String] project (bucket) ID
44
+ # @param vault_id [Integer, String] vault ID
45
+ # @param title [String] document title
46
+ # @param content [String, nil] document body in HTML
47
+ # @param status [String, nil] status ("drafted" or "active")
48
+ # @return [Hash] created document
49
+ def create(project_id:, vault_id:, title:, content: nil, status: nil)
50
+ body = compact_params(
51
+ title: title,
52
+ content: content,
53
+ status: status
54
+ )
55
+ http_post(bucket_path(project_id, "/vaults/#{vault_id}/documents.json"), body: body).json
56
+ end
57
+
58
+ # Updates an existing document.
59
+ #
60
+ # @param project_id [Integer, String] project (bucket) ID
61
+ # @param document_id [Integer, String] document ID
62
+ # @param title [String, nil] new title
63
+ # @param content [String, nil] new content
64
+ # @return [Hash] updated document
65
+ def update(project_id:, document_id:, title: nil, content: nil)
66
+ body = compact_params(
67
+ title: title,
68
+ content: content
69
+ )
70
+ http_put(bucket_path(project_id, "/documents/#{document_id}.json"), body: body).json
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for event operations.
6
+ #
7
+ # Events are activity records that track changes to recordings.
8
+ # An event is created any time a recording is modified (created,
9
+ # updated, completed, etc.).
10
+ #
11
+ # @example List events for a recording
12
+ # account.events.list(project_id: 123, recording_id: 456).each do |event|
13
+ # puts "#{event["action"]} by #{event["creator"]["name"]} at #{event["created_at"]}"
14
+ # end
15
+ class EventsService < BaseService
16
+ # Lists all events for a recording.
17
+ # Events track all changes made to a recording over time.
18
+ #
19
+ # @param project_id [Integer, String] project (bucket) ID
20
+ # @param recording_id [Integer, String] recording ID
21
+ # @return [Enumerator<Hash>] events
22
+ def list(project_id:, recording_id:)
23
+ paginate(bucket_path(project_id, "/recordings/#{recording_id}/events.json"))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for email forward operations.
6
+ #
7
+ # Forwards are emails that have been forwarded to a project's inbox.
8
+ # Team members can reply to forwarded emails from within Basecamp.
9
+ #
10
+ # @example List forwards in an inbox
11
+ # account.forwards.list(project_id: 123, inbox_id: 456).each do |f|
12
+ # puts "#{f["subject"]} - from #{f["from"]}"
13
+ # end
14
+ #
15
+ # @example Create a reply
16
+ # reply = account.forwards.create_reply(
17
+ # project_id: 123,
18
+ # forward_id: 456,
19
+ # content: "<p>Thanks for reaching out!</p>"
20
+ # )
21
+ class ForwardsService < BaseService
22
+ # Gets an inbox by ID.
23
+ #
24
+ # @param project_id [Integer, String] project (bucket) ID
25
+ # @param inbox_id [Integer, String] inbox ID
26
+ # @return [Hash] inbox data
27
+ def get_inbox(project_id:, inbox_id:)
28
+ http_get(bucket_path(project_id, "/inboxes/#{inbox_id}.json")).json
29
+ end
30
+
31
+ # Lists all forwards in an inbox.
32
+ #
33
+ # @param project_id [Integer, String] project (bucket) ID
34
+ # @param inbox_id [Integer, String] inbox ID
35
+ # @return [Enumerator<Hash>] forwards
36
+ def list(project_id:, inbox_id:)
37
+ paginate(bucket_path(project_id, "/inboxes/#{inbox_id}/forwards.json"))
38
+ end
39
+
40
+ # Gets a forward by ID.
41
+ #
42
+ # @param project_id [Integer, String] project (bucket) ID
43
+ # @param forward_id [Integer, String] forward ID
44
+ # @return [Hash] forward data
45
+ def get(project_id:, forward_id:)
46
+ http_get(bucket_path(project_id, "/inbox_forwards/#{forward_id}.json")).json
47
+ end
48
+
49
+ # Lists all replies to a forward.
50
+ #
51
+ # @param project_id [Integer, String] project (bucket) ID
52
+ # @param forward_id [Integer, String] forward ID
53
+ # @return [Enumerator<Hash>] replies
54
+ def list_replies(project_id:, forward_id:)
55
+ paginate(bucket_path(project_id, "/inbox_forwards/#{forward_id}/replies.json"))
56
+ end
57
+
58
+ # Gets a specific reply.
59
+ #
60
+ # @param project_id [Integer, String] project (bucket) ID
61
+ # @param forward_id [Integer, String] forward ID
62
+ # @param reply_id [Integer, String] reply ID
63
+ # @return [Hash] reply data
64
+ def get_reply(project_id:, forward_id:, reply_id:)
65
+ http_get(bucket_path(project_id, "/inbox_forwards/#{forward_id}/replies/#{reply_id}.json")).json
66
+ end
67
+
68
+ # Creates a reply to a forwarded email.
69
+ #
70
+ # @param project_id [Integer, String] project (bucket) ID
71
+ # @param forward_id [Integer, String] forward ID
72
+ # @param content [String] reply body in HTML
73
+ # @return [Hash] created reply
74
+ def create_reply(project_id:, forward_id:, content:)
75
+ body = { content: content }
76
+ http_post(bucket_path(project_id, "/inbox_forwards/#{forward_id}/replies.json"), body: body).json
77
+ end
78
+ end
79
+ end
80
+ end