wip-ruby 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.simplecov +36 -0
- data/CHANGELOG.md +41 -0
- data/LICENSE.txt +21 -0
- data/README.md +846 -0
- data/Rakefile +45 -0
- data/lib/wip/client.rb +84 -0
- data/lib/wip/configuration.rb +89 -0
- data/lib/wip/error.rb +76 -0
- data/lib/wip/http_client.rb +221 -0
- data/lib/wip/models/base.rb +160 -0
- data/lib/wip/models/collection.rb +81 -0
- data/lib/wip/models/comment.rb +28 -0
- data/lib/wip/models/concerns/reactable.rb +25 -0
- data/lib/wip/models/project.rb +48 -0
- data/lib/wip/models/reaction.rb +32 -0
- data/lib/wip/models/todo.rb +57 -0
- data/lib/wip/models/user.rb +51 -0
- data/lib/wip/resources/base.rb +80 -0
- data/lib/wip/resources/comments.rb +93 -0
- data/lib/wip/resources/projects.rb +42 -0
- data/lib/wip/resources/reactions.rb +74 -0
- data/lib/wip/resources/todos.rb +47 -0
- data/lib/wip/resources/uploads.rb +111 -0
- data/lib/wip/resources/users.rb +61 -0
- data/lib/wip/resources/viewer.rb +52 -0
- data/lib/wip/version.rb +5 -0
- data/lib/wip-ruby.rb +1 -0
- data/lib/wip.rb +51 -0
- data/sig/wip/ruby.rbs +6 -0
- data/test_examples.rb +435 -0
- metadata +119 -0
data/test_examples.rb
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "logger"
|
|
5
|
+
require "tempfile"
|
|
6
|
+
require "securerandom"
|
|
7
|
+
require "wip-ruby"
|
|
8
|
+
require 'chunky_png'
|
|
9
|
+
|
|
10
|
+
# -----------------------------------------------------------------------------
|
|
11
|
+
# Configuration
|
|
12
|
+
# -----------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
# Set your API key via the WIP_API_KEY environment variable.
|
|
15
|
+
Wip.configure do |config|
|
|
16
|
+
config.api_key = ENV.fetch("WIP_API_KEY") { raise "WIP_API_KEY environment variable is required" }
|
|
17
|
+
config.base_url = "https://api.wip.co"
|
|
18
|
+
config.logger = Logger.new($stdout)
|
|
19
|
+
config.logger.level = Logger::DEBUG
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Initialize the main API client.
|
|
23
|
+
client = Wip::Client.new
|
|
24
|
+
|
|
25
|
+
# -----------------------------------------------------------------------------
|
|
26
|
+
# Helper to run tests
|
|
27
|
+
# -----------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
def run_test(test_name)
|
|
30
|
+
puts "\n=== #{test_name} ==="
|
|
31
|
+
yield
|
|
32
|
+
puts "✅ #{test_name} passed"
|
|
33
|
+
rescue => e
|
|
34
|
+
puts "❌ #{test_name} failed: #{e.class} - #{e.message}"
|
|
35
|
+
e.backtrace.each { |line| puts " #{line}" }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# -----------------------------------------------------------------------------
|
|
39
|
+
# Test: Configuration Validation
|
|
40
|
+
# -----------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
run_test("Configuration Validation") do
|
|
43
|
+
# Test missing API key
|
|
44
|
+
invalid_config = Wip::Configuration.new
|
|
45
|
+
begin
|
|
46
|
+
Wip::Client.new(invalid_config)
|
|
47
|
+
raise "Expected ConfigurationError, but none was raised"
|
|
48
|
+
rescue Wip::Error::ConfigurationError
|
|
49
|
+
# Expected error
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Test invalid timeout
|
|
53
|
+
invalid_timeout_config = Wip::Configuration.new
|
|
54
|
+
invalid_timeout_config.api_key = "test_key"
|
|
55
|
+
invalid_timeout_config.timeout = 0
|
|
56
|
+
begin
|
|
57
|
+
Wip::Client.new(invalid_timeout_config)
|
|
58
|
+
raise "Expected ConfigurationError for invalid timeout"
|
|
59
|
+
rescue Wip::Error::ConfigurationError
|
|
60
|
+
# Expected error
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Test invalid max_retries
|
|
64
|
+
invalid_retries_config = Wip::Configuration.new
|
|
65
|
+
invalid_retries_config.api_key = "test_key"
|
|
66
|
+
invalid_retries_config.max_retries = -1
|
|
67
|
+
begin
|
|
68
|
+
Wip::Client.new(invalid_retries_config)
|
|
69
|
+
raise "Expected ConfigurationError for invalid max_retries"
|
|
70
|
+
rescue Wip::Error::ConfigurationError
|
|
71
|
+
# Expected error
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Test invalid logger
|
|
75
|
+
invalid_logger_config = Wip::Configuration.new
|
|
76
|
+
invalid_logger_config.api_key = "test_key"
|
|
77
|
+
invalid_logger_config.logger = Object.new # Object without logger methods
|
|
78
|
+
begin
|
|
79
|
+
Wip::Client.new(invalid_logger_config)
|
|
80
|
+
raise "Expected ConfigurationError for invalid logger"
|
|
81
|
+
rescue Wip::Error::ConfigurationError
|
|
82
|
+
# Expected error
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# -----------------------------------------------------------------------------
|
|
87
|
+
# Test: Todos Endpoints
|
|
88
|
+
# -----------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
run_test("Todos Endpoints") do
|
|
91
|
+
# Create a todo with a simple body
|
|
92
|
+
todo_body = "Test todo from manual script at #{Time.now.to_i}"
|
|
93
|
+
new_todo = client.todos.create(body: todo_body, attachments: [])
|
|
94
|
+
raise "Todo creation failed" unless new_todo["id"]
|
|
95
|
+
|
|
96
|
+
# Fetch and verify the created todo
|
|
97
|
+
fetched_todo = client.todos.find(new_todo["id"])
|
|
98
|
+
raise "Fetched todo body mismatch" unless fetched_todo["body"] == todo_body
|
|
99
|
+
raise "Todo URL missing" unless fetched_todo["url"].is_a?(String)
|
|
100
|
+
raise "Todo creator_id missing" unless fetched_todo["creator_id"].is_a?(String)
|
|
101
|
+
raise "Todo projects not an array" unless fetched_todo["projects"].is_a?(Array)
|
|
102
|
+
raise "Todo attachments not an array" unless fetched_todo["attachments"].is_a?(Array)
|
|
103
|
+
|
|
104
|
+
# Test todo with markdown formatting
|
|
105
|
+
markdown_body = "# Test Todo\n\n- Item 1\n- Item 2\n\n**Bold** and *italic*"
|
|
106
|
+
markdown_todo = client.todos.create(body: markdown_body, attachments: [])
|
|
107
|
+
raise "Markdown todo creation failed" unless markdown_todo["id"]
|
|
108
|
+
|
|
109
|
+
# List todos with pagination
|
|
110
|
+
first_page = client.viewer.todos(limit: 5)
|
|
111
|
+
raise "First page data missing" unless first_page["data"].is_a?(Array)
|
|
112
|
+
raise "First page missing total_count" unless first_page["total_count"].is_a?(Integer)
|
|
113
|
+
raise "First page missing has_more" unless [true, false].include?(first_page["has_more"])
|
|
114
|
+
|
|
115
|
+
if first_page["has_more"]
|
|
116
|
+
second_page = client.viewer.todos(limit: 5, starting_after: first_page["data"].last["id"])
|
|
117
|
+
raise "Second page data missing" unless second_page["data"].is_a?(Array)
|
|
118
|
+
raise "Second page todos overlap with first page" if (second_page["data"].map { |t| t["id"] } & first_page["data"].map { |t| t["id"] }).any?
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Test todos for specific user
|
|
122
|
+
user_todos = client.users.todos("rameerez", limit: 5)
|
|
123
|
+
raise "User todos data missing" unless user_todos["data"].is_a?(Array)
|
|
124
|
+
puts "Found #{user_todos["data"].size} todos for user 'rameerez'"
|
|
125
|
+
|
|
126
|
+
# Test todos for specific project
|
|
127
|
+
if first_page["data"].any? && first_page["data"].first["projects"].any?
|
|
128
|
+
project_id = first_page["data"].first["projects"].first["id"]
|
|
129
|
+
project_todos = client.projects.todos(project_id, limit: 5)
|
|
130
|
+
raise "Project todos data missing" unless project_todos["data"].is_a?(Array)
|
|
131
|
+
puts "Found #{project_todos["data"].size} todos for project #{project_id}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# -----------------------------------------------------------------------------
|
|
136
|
+
# Test: Users Endpoints
|
|
137
|
+
# -----------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
run_test("Users Endpoints") do
|
|
140
|
+
# Test finding a known user
|
|
141
|
+
user = client.users.find("rameerez")
|
|
142
|
+
raise "User not found" unless user["username"] == "rameerez"
|
|
143
|
+
raise "User ID missing" unless user["id"].is_a?(String)
|
|
144
|
+
raise "User streak not a number" unless user["streak"].is_a?(Integer)
|
|
145
|
+
raise "User todos_count not a number" unless user["todos_count"].is_a?(Integer)
|
|
146
|
+
raise "User avatar missing" unless user["avatar"].is_a?(Hash)
|
|
147
|
+
raise "User avatar URLs missing" unless user["avatar"]["small"].is_a?(String) &&
|
|
148
|
+
user["avatar"]["medium"].is_a?(String) &&
|
|
149
|
+
user["avatar"]["large"].is_a?(String)
|
|
150
|
+
|
|
151
|
+
# Test authenticated user endpoints
|
|
152
|
+
auth_user1 = client.viewer.me
|
|
153
|
+
auth_user2 = client.viewer.me
|
|
154
|
+
raise "Authenticated user mismatch" unless auth_user1["id"] == auth_user2["id"]
|
|
155
|
+
raise "Authenticated user missing username" unless auth_user1["username"].is_a?(String)
|
|
156
|
+
|
|
157
|
+
# Test user's projects with pagination
|
|
158
|
+
user_projects = client.users.projects("rameerez", limit: 5)
|
|
159
|
+
raise "User projects data missing" unless user_projects["data"].is_a?(Array)
|
|
160
|
+
if user_projects["has_more"]
|
|
161
|
+
next_projects = client.users.projects("rameerez",
|
|
162
|
+
limit: 5,
|
|
163
|
+
starting_after: user_projects["data"].last["id"])
|
|
164
|
+
raise "Next page projects data missing" unless next_projects["data"].is_a?(Array)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Test user's todos with pagination
|
|
168
|
+
user_todos = client.users.todos("rameerez", limit: 5)
|
|
169
|
+
raise "User todos data missing" unless user_todos["data"].is_a?(Array)
|
|
170
|
+
if user_todos["has_more"]
|
|
171
|
+
next_todos = client.users.todos("rameerez",
|
|
172
|
+
limit: 5,
|
|
173
|
+
starting_after: user_todos["data"].last["id"])
|
|
174
|
+
raise "Next page todos data missing" unless next_todos["data"].is_a?(Array)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
puts "User 'rameerez' has #{user_projects['data'].size} projects and #{user_todos['data'].size} todos"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# -----------------------------------------------------------------------------
|
|
181
|
+
# Test: Projects Endpoints
|
|
182
|
+
# -----------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
run_test("Projects Endpoints") do
|
|
185
|
+
# Test authenticated user's projects
|
|
186
|
+
my_projects = client.viewer.projects(limit: 5)
|
|
187
|
+
raise "My projects data missing" unless my_projects["data"].is_a?(Array)
|
|
188
|
+
|
|
189
|
+
if my_projects["data"].any?
|
|
190
|
+
project = my_projects["data"].first
|
|
191
|
+
|
|
192
|
+
# Test fetching single project
|
|
193
|
+
proj_details = client.projects.find(project["id"])
|
|
194
|
+
raise "Project name missing" unless proj_details["name"].is_a?(String)
|
|
195
|
+
raise "Project URL missing" unless proj_details["url"].is_a?(String)
|
|
196
|
+
raise "Project owner missing" unless proj_details["owner"].is_a?(Hash)
|
|
197
|
+
raise "Project makers not an array" unless proj_details["makers"].is_a?(Array)
|
|
198
|
+
raise "Project logo missing" unless proj_details["logo"].is_a?(Hash)
|
|
199
|
+
|
|
200
|
+
# Test project todos with pagination
|
|
201
|
+
proj_todos = client.projects.todos(project["id"], limit: 5)
|
|
202
|
+
raise "Project todos data missing" unless proj_todos["data"].is_a?(Array)
|
|
203
|
+
if proj_todos["has_more"]
|
|
204
|
+
next_todos = client.projects.todos(project["id"],
|
|
205
|
+
limit: 5,
|
|
206
|
+
starting_after: proj_todos["data"].last["id"])
|
|
207
|
+
raise "Next page project todos missing" unless next_todos["data"].is_a?(Array)
|
|
208
|
+
end
|
|
209
|
+
puts "Project '#{proj_details['name']}' has #{proj_todos['data'].size} todos"
|
|
210
|
+
else
|
|
211
|
+
puts "No projects found for authenticated user"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Test listing projects for a specific user with pagination
|
|
215
|
+
user_projects = client.users.projects("rameerez", limit: 5)
|
|
216
|
+
raise "User projects data missing" unless user_projects["data"].is_a?(Array)
|
|
217
|
+
if user_projects["has_more"]
|
|
218
|
+
next_projects = client.users.projects("rameerez",
|
|
219
|
+
limit: 5,
|
|
220
|
+
starting_after: user_projects["data"].last["id"])
|
|
221
|
+
raise "Next page user projects missing" unless next_projects["data"].is_a?(Array)
|
|
222
|
+
end
|
|
223
|
+
puts "User 'rameerez' has #{user_projects['data'].size} projects"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# -----------------------------------------------------------------------------
|
|
227
|
+
# Test: Uploads Endpoints
|
|
228
|
+
# -----------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
run_test("Uploads Endpoints") do
|
|
231
|
+
# Test uploading different file types
|
|
232
|
+
[
|
|
233
|
+
# Test PNG image
|
|
234
|
+
{
|
|
235
|
+
ext: ".png",
|
|
236
|
+
content_type: "image/png",
|
|
237
|
+
create: ->(file) {
|
|
238
|
+
png = ChunkyPNG::Image.new(100, 100, ChunkyPNG::Color.rgb(255, 0, 0))
|
|
239
|
+
png.rect(0, 0, 99, 99, ChunkyPNG::Color.rgb(0, 0, 0), ChunkyPNG::Color.rgb(255, 0, 0))
|
|
240
|
+
png.save(file.path)
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
# Test text file
|
|
244
|
+
{
|
|
245
|
+
ext: ".md",
|
|
246
|
+
content_type: "text/markdown",
|
|
247
|
+
create: ->(file) {
|
|
248
|
+
file.write("# Test Markdown\n\nThis is a test file for wip-ruby uploads.")
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
].each do |test_file|
|
|
252
|
+
Tempfile.create(["wip_test", test_file[:ext]]) do |file|
|
|
253
|
+
# Create test file
|
|
254
|
+
test_file[:create].call(file)
|
|
255
|
+
file.flush
|
|
256
|
+
|
|
257
|
+
# Upload file and verify signed ID
|
|
258
|
+
signed_id = client.uploads.upload(file.path)
|
|
259
|
+
raise "Upload did not return a signed_id" unless signed_id && !signed_id.empty?
|
|
260
|
+
puts "Successfully uploaded #{test_file[:ext]} file with signed_id: #{signed_id}"
|
|
261
|
+
|
|
262
|
+
# Create todo with attachment
|
|
263
|
+
todo_body = "Todo with #{test_file[:ext]} attachment from test at #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
264
|
+
todo = client.todos.create(
|
|
265
|
+
body: todo_body,
|
|
266
|
+
attachments: [signed_id]
|
|
267
|
+
)
|
|
268
|
+
raise "Todo creation failed" unless todo["id"]
|
|
269
|
+
|
|
270
|
+
# Verify todo and attachment
|
|
271
|
+
fetched_todo = client.todos.find(todo["id"])
|
|
272
|
+
raise "Todo body mismatch" unless fetched_todo["body"] == todo_body
|
|
273
|
+
raise "Attachment missing" if fetched_todo["attachments"].empty?
|
|
274
|
+
raise "Attachment ID mismatch" unless fetched_todo["attachments"].any? { |a| a["signed_id"] == signed_id }
|
|
275
|
+
puts "Successfully verified todo has the correct #{test_file[:ext]} attachment"
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Test invalid file type
|
|
280
|
+
Tempfile.create(["wip_test", ".invalid"]) do |file|
|
|
281
|
+
file.write("Invalid file type test")
|
|
282
|
+
file.flush
|
|
283
|
+
|
|
284
|
+
begin
|
|
285
|
+
client.uploads.upload(file.path)
|
|
286
|
+
raise "Expected ValidationError for invalid file type"
|
|
287
|
+
rescue Wip::Error::ValidationError => e
|
|
288
|
+
puts "Caught expected ValidationError for invalid file type: #{e.message}"
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# -----------------------------------------------------------------------------
|
|
294
|
+
# Test: Error Handling
|
|
295
|
+
# -----------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
run_test("Error Handling - Not Found") do
|
|
298
|
+
# Test non-existent user
|
|
299
|
+
non_existent_user = "nonexistent_user_#{SecureRandom.hex(4)}"
|
|
300
|
+
begin
|
|
301
|
+
client.users.find(non_existent_user)
|
|
302
|
+
raise "Expected NotFoundError but none was raised"
|
|
303
|
+
rescue Wip::Error::NotFoundError => e
|
|
304
|
+
puts "Caught expected NotFoundError: #{e.message}"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Test non-existent project
|
|
308
|
+
begin
|
|
309
|
+
client.projects.find("non_existent_project_#{SecureRandom.hex(4)}")
|
|
310
|
+
raise "Expected NotFoundError but none was raised"
|
|
311
|
+
rescue Wip::Error::NotFoundError => e
|
|
312
|
+
puts "Caught expected NotFoundError for project: #{e.message}"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Test non-existent todo
|
|
316
|
+
begin
|
|
317
|
+
client.todos.find("non_existent_todo_#{SecureRandom.hex(4)}")
|
|
318
|
+
raise "Expected NotFoundError but none was raised"
|
|
319
|
+
rescue Wip::Error::NotFoundError => e
|
|
320
|
+
puts "Caught expected NotFoundError for todo: #{e.message}"
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
run_test("Error Handling - Unauthorized") do
|
|
325
|
+
# Test invalid API key
|
|
326
|
+
bad_config = Wip::Configuration.new.tap { |c| c.api_key = "invalid_key" }
|
|
327
|
+
bad_client = Wip::Client.new(bad_config)
|
|
328
|
+
|
|
329
|
+
# Test different endpoints with invalid key
|
|
330
|
+
[
|
|
331
|
+
-> { bad_client.viewer.me },
|
|
332
|
+
-> { bad_client.viewer.todos },
|
|
333
|
+
-> { bad_client.viewer.projects },
|
|
334
|
+
-> { bad_client.viewer.me }
|
|
335
|
+
].each do |endpoint|
|
|
336
|
+
begin
|
|
337
|
+
endpoint.call
|
|
338
|
+
raise "Expected UnauthorizedError but none was raised"
|
|
339
|
+
rescue Wip::Error::UnauthorizedError => e
|
|
340
|
+
puts "Caught expected UnauthorizedError: #{e.message}"
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
run_test("Error Handling - Validation") do
|
|
346
|
+
# Test creating todo without body
|
|
347
|
+
begin
|
|
348
|
+
client.todos.create(body: "", attachments: [])
|
|
349
|
+
raise "Expected ValidationError but none was raised"
|
|
350
|
+
rescue Wip::Error::ValidationError => e
|
|
351
|
+
puts "Caught expected ValidationError for empty todo body: #{e.message}"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Test upload with invalid content type
|
|
355
|
+
begin
|
|
356
|
+
client.uploads.request_upload_url(
|
|
357
|
+
filename: "test.xyz",
|
|
358
|
+
byte_size: 100,
|
|
359
|
+
checksum: "invalid",
|
|
360
|
+
content_type: "application/invalid"
|
|
361
|
+
)
|
|
362
|
+
raise "Expected ValidationError but none was raised"
|
|
363
|
+
rescue Wip::Error::ValidationError => e
|
|
364
|
+
puts "Caught expected ValidationError for invalid content type: #{e.message}"
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# -----------------------------------------------------------------------------
|
|
369
|
+
# Test: Pagination
|
|
370
|
+
# -----------------------------------------------------------------------------
|
|
371
|
+
|
|
372
|
+
run_test("Pagination") do
|
|
373
|
+
# Test todos pagination
|
|
374
|
+
page1 = client.viewer.todos(limit: 2)
|
|
375
|
+
raise "Page1 data missing" unless page1["data"].is_a?(Array)
|
|
376
|
+
puts "Page1 contains #{page1['data'].size} items"
|
|
377
|
+
|
|
378
|
+
if page1["has_more"] && page1["data"].any?
|
|
379
|
+
page2 = client.viewer.todos(limit: 2, starting_after: page1["data"].last["id"])
|
|
380
|
+
raise "Page2 data missing" unless page2["data"].is_a?(Array)
|
|
381
|
+
puts "Page2 contains #{page2['data'].size} items"
|
|
382
|
+
|
|
383
|
+
# Verify no overlap between pages
|
|
384
|
+
page1_ids = page1["data"].map { |t| t["id"] }
|
|
385
|
+
page2_ids = page2["data"].map { |t| t["id"] }
|
|
386
|
+
raise "Page IDs overlap" unless (page1_ids & page2_ids).empty?
|
|
387
|
+
|
|
388
|
+
if page2["has_more"] && page2["data"].any?
|
|
389
|
+
page3 = client.viewer.todos(limit: 2, starting_after: page2["data"].last["id"])
|
|
390
|
+
raise "Page3 data missing" unless page3["data"].is_a?(Array)
|
|
391
|
+
puts "Page3 contains #{page3['data'].size} items"
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Test projects pagination
|
|
396
|
+
projects_page1 = client.viewer.projects(limit: 2)
|
|
397
|
+
raise "Projects page1 data missing" unless projects_page1["data"].is_a?(Array)
|
|
398
|
+
puts "Projects page1 contains #{projects_page1['data'].size} items"
|
|
399
|
+
|
|
400
|
+
if projects_page1["has_more"] && projects_page1["data"].any?
|
|
401
|
+
projects_page2 = client.viewer.projects(
|
|
402
|
+
limit: 2,
|
|
403
|
+
starting_after: projects_page1["data"].last["id"]
|
|
404
|
+
)
|
|
405
|
+
raise "Projects page2 data missing" unless projects_page2["data"].is_a?(Array)
|
|
406
|
+
puts "Projects page2 contains #{projects_page2['data'].size} items"
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# -----------------------------------------------------------------------------
|
|
411
|
+
# Test: Rate Limiting
|
|
412
|
+
# -----------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
run_test("Rate Limiting") do
|
|
415
|
+
rate_limit_triggered = false
|
|
416
|
+
requests_made = 0
|
|
417
|
+
|
|
418
|
+
# Make rapid requests to try to trigger rate limiting
|
|
419
|
+
20.times do |i|
|
|
420
|
+
begin
|
|
421
|
+
client.viewer.me
|
|
422
|
+
requests_made += 1
|
|
423
|
+
sleep(0.1) # Small delay between requests
|
|
424
|
+
rescue Wip::Error::RateLimitError => e
|
|
425
|
+
puts "Rate limit triggered after #{requests_made} requests: #{e.message}"
|
|
426
|
+
rate_limit_triggered = true
|
|
427
|
+
break
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
puts "Rate limiting test: #{rate_limit_triggered ? 'Rate limit was triggered' : 'No rate limit encountered'}"
|
|
432
|
+
puts "Completed #{requests_made} requests"
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
puts "\nAll manual tests completed!"
|
metadata
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: wip-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Javi R
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-02-03 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.12'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.12'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday-retry
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.2'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: mime-types
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.5'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.5'
|
|
54
|
+
description: WIP.co API wrapper for Ruby on Rails apps. WIP (wip.co) is a community
|
|
55
|
+
of makers who share what they're working on. wip-ruby provides an elegant, intuitive,
|
|
56
|
+
no-frills interface for interacting with the WIP API.
|
|
57
|
+
email:
|
|
58
|
+
- rubygems@rameerez.com
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- ".simplecov"
|
|
64
|
+
- CHANGELOG.md
|
|
65
|
+
- LICENSE.txt
|
|
66
|
+
- README.md
|
|
67
|
+
- Rakefile
|
|
68
|
+
- lib/wip-ruby.rb
|
|
69
|
+
- lib/wip.rb
|
|
70
|
+
- lib/wip/client.rb
|
|
71
|
+
- lib/wip/configuration.rb
|
|
72
|
+
- lib/wip/error.rb
|
|
73
|
+
- lib/wip/http_client.rb
|
|
74
|
+
- lib/wip/models/base.rb
|
|
75
|
+
- lib/wip/models/collection.rb
|
|
76
|
+
- lib/wip/models/comment.rb
|
|
77
|
+
- lib/wip/models/concerns/reactable.rb
|
|
78
|
+
- lib/wip/models/project.rb
|
|
79
|
+
- lib/wip/models/reaction.rb
|
|
80
|
+
- lib/wip/models/todo.rb
|
|
81
|
+
- lib/wip/models/user.rb
|
|
82
|
+
- lib/wip/resources/base.rb
|
|
83
|
+
- lib/wip/resources/comments.rb
|
|
84
|
+
- lib/wip/resources/projects.rb
|
|
85
|
+
- lib/wip/resources/reactions.rb
|
|
86
|
+
- lib/wip/resources/todos.rb
|
|
87
|
+
- lib/wip/resources/uploads.rb
|
|
88
|
+
- lib/wip/resources/users.rb
|
|
89
|
+
- lib/wip/resources/viewer.rb
|
|
90
|
+
- lib/wip/version.rb
|
|
91
|
+
- sig/wip/ruby.rbs
|
|
92
|
+
- test_examples.rb
|
|
93
|
+
homepage: https://github.com/rameerez/wip-ruby
|
|
94
|
+
licenses:
|
|
95
|
+
- MIT
|
|
96
|
+
metadata:
|
|
97
|
+
allowed_push_host: https://rubygems.org
|
|
98
|
+
homepage_uri: https://github.com/rameerez/wip-ruby
|
|
99
|
+
source_code_uri: https://github.com/rameerez/wip-ruby
|
|
100
|
+
changelog_uri: https://github.com/rameerez/wip-ruby/blob/main/CHANGELOG.md
|
|
101
|
+
rubygems_mfa_required: 'true'
|
|
102
|
+
rdoc_options: []
|
|
103
|
+
require_paths:
|
|
104
|
+
- lib
|
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 3.1.0
|
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - ">="
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '0'
|
|
115
|
+
requirements: []
|
|
116
|
+
rubygems_version: 3.6.2
|
|
117
|
+
specification_version: 4
|
|
118
|
+
summary: WIP.co API wrapper for Ruby / Rails.
|
|
119
|
+
test_files: []
|