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.
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require "digest/md5"
5
+ require "mime/types"
6
+
7
+ module Wip
8
+ module Resources
9
+ # Resource for handling file uploads
10
+ class Uploads < Base
11
+ # Request a pre-signed URL for direct file upload
12
+ # @param filename [String] Name of the file to upload
13
+ # @param byte_size [Integer] Size of the file in bytes
14
+ # @param checksum [String] MD5 checksum of the file
15
+ # @param content_type [String] MIME type of the file
16
+ # @return [Hash] Upload credentials and information including url, headers, method, and signed_id
17
+ def request_upload_url(filename:, byte_size:, checksum:, content_type:)
18
+ response = client.post("/v1/uploads", {
19
+ filename: filename,
20
+ byte_size: byte_size,
21
+ checksum: checksum,
22
+ content_type: content_type
23
+ })
24
+ response.extract_resource
25
+ end
26
+
27
+ # Upload a file using the pre-signed URL
28
+ # @param url [String] Pre-signed URL for upload
29
+ # @param headers [Hash] Headers required for the upload
30
+ # @param file_path [String] Path to the file to upload
31
+ # @param method [String] HTTP method to use (from API response, defaults to PUT)
32
+ # @return [Boolean] Whether the upload was successful
33
+ def upload_file(url:, headers:, file_path:, method: "PUT")
34
+ # Create a separate connection for file uploads
35
+ # This ensures we don't interfere with the main API client's connection
36
+ connection = Faraday.new(url: url) do |f|
37
+ # Don't process response as JSON
38
+ f.response :raise_error
39
+
40
+ # Add logging if configured
41
+ if client.config.logger
42
+ f.response :logger, client.config.logger, { headers: true }
43
+ end
44
+
45
+ # Use default adapter
46
+ f.adapter Faraday.default_adapter
47
+ end
48
+
49
+ # Ensure we have Content-Length
50
+ file_size = File.size(file_path)
51
+ headers["Content-Length"] ||= file_size.to_s
52
+
53
+ # Use the HTTP method specified by the API
54
+ http_method = method.to_s.downcase.to_sym
55
+
56
+ # Open file in binary mode and stream it
57
+ File.open(file_path, "rb") do |file|
58
+ response = connection.public_send(http_method) do |req|
59
+ # Add required headers
60
+ headers.each { |key, value| req.headers[key] = value }
61
+
62
+ # Stream the file
63
+ req.body = file.read
64
+ end
65
+
66
+ return response.success?
67
+ end
68
+ rescue Faraday::Error => e
69
+ raise Error::UploadError, "Upload failed: #{e.message}"
70
+ rescue SystemCallError => e
71
+ raise Error::UploadError, "File access error: #{e.message}"
72
+ end
73
+
74
+ # Helper method to handle the complete upload process
75
+ # @param file_path [String] Path to the file to upload
76
+ # @return [String] The signed ID for the uploaded file
77
+ def upload(file_path)
78
+ # Validate file exists and is readable
79
+ unless File.file?(file_path) && File.readable?(file_path)
80
+ raise Error::UploadError, "File not found or not readable: #{file_path}"
81
+ end
82
+
83
+ # Get file information
84
+ file_size = File.size(file_path)
85
+ file_name = File.basename(file_path)
86
+ content_type = MIME::Types.type_for(file_path).first&.content_type || "application/octet-stream"
87
+ checksum = Digest::MD5.file(file_path).base64digest
88
+
89
+ # Get upload URL and credentials
90
+ credentials = request_upload_url(
91
+ filename: file_name,
92
+ byte_size: file_size,
93
+ checksum: checksum,
94
+ content_type: content_type
95
+ )
96
+
97
+ # Perform the upload using the method specified by the API
98
+ success = upload_file(
99
+ url: credentials["url"],
100
+ headers: credentials["headers"],
101
+ file_path: file_path,
102
+ method: credentials["method"] || "PUT"
103
+ )
104
+
105
+ raise Error::UploadError, "Failed to upload file" unless success
106
+
107
+ credentials["signed_id"]
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "../models/user"
5
+ require_relative "../models/project"
6
+ require_relative "../models/todo"
7
+ require_relative "../models/collection"
8
+
9
+ module Wip
10
+ module Resources
11
+ # Resource for interacting with users
12
+ #
13
+ # @example Get a user's details
14
+ # client.users.find("username")
15
+ #
16
+ # @example List a user's projects
17
+ # client.users.projects("username")
18
+ #
19
+ # @example List a user's todos
20
+ # client.users.todos("username", since: "2024-01-01")
21
+ #
22
+ # @note For authenticated user operations, use the Viewer resource instead.
23
+ # @see Viewer
24
+ class Users < Base
25
+ # Get a single user by username
26
+ # @param username [String] The username
27
+ # @return [Models::User] The user
28
+ def find(username)
29
+ path = build_path("/v1/users/{username}", username: username)
30
+ response = client.get(path)
31
+ Models::User.from_json(response.extract_resource)
32
+ end
33
+
34
+ # List projects for a user
35
+ # @param username [String] The username
36
+ # @param limit [Integer] Number of items to return (default: 25)
37
+ # @param starting_after [String] Cursor for pagination
38
+ # @return [Models::Collection<Models::Project>] The paginated projects
39
+ def projects(username, limit: nil, starting_after: nil)
40
+ path = build_path("/v1/users/{username}/projects", username: username)
41
+ params = extract_pagination_params(limit: limit, starting_after: starting_after)
42
+ response = client.get(path, params)
43
+ Models::Collection.from_json(response.extract_resource, item_class: Models::Project)
44
+ end
45
+
46
+ # List todos for a user
47
+ # @param username [String] The username
48
+ # @param limit [Integer] Number of items to return (default: 25)
49
+ # @param starting_after [String] Cursor for pagination
50
+ # @param since [Time, Date, String, Integer] Filter todos created since this date
51
+ # @param before [Time, Date, String, Integer] Filter todos created before this date (maps to API's `until` param)
52
+ # @return [Models::Collection<Models::Todo>] The paginated todos
53
+ def todos(username, limit: nil, starting_after: nil, since: nil, before: nil)
54
+ path = build_path("/v1/users/{username}/todos", username: username)
55
+ params = extract_todo_params(limit: limit, starting_after: starting_after, since: since, before: before)
56
+ response = client.get(path, params)
57
+ Models::Collection.from_json(response.extract_resource, item_class: Models::Todo)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "../models/user"
5
+ require_relative "../models/project"
6
+ require_relative "../models/todo"
7
+ require_relative "../models/collection"
8
+
9
+ module Wip
10
+ module Resources
11
+ # Resource for interacting with the authenticated user (viewer)
12
+ #
13
+ # @example Get the authenticated user
14
+ # client.viewer.me
15
+ #
16
+ # @example List your todos
17
+ # client.viewer.todos(limit: 10)
18
+ #
19
+ # @example List your projects
20
+ # client.viewer.projects
21
+ class Viewer < Base
22
+ # Get the authenticated user's details
23
+ # @return [Models::User] The authenticated user
24
+ def me
25
+ response = client.get("/v1/users/me")
26
+ Models::User.from_json(response.extract_resource)
27
+ end
28
+
29
+ # List todos for the authenticated user
30
+ # @param limit [Integer] Number of items to return (default: 25)
31
+ # @param starting_after [String] Cursor for pagination
32
+ # @param since [Time, Date, String, Integer] Filter todos created since this date
33
+ # @param before [Time, Date, String, Integer] Filter todos created before this date (maps to API's `until` param)
34
+ # @return [Models::Collection<Models::Todo>] The paginated todos
35
+ def todos(limit: nil, starting_after: nil, since: nil, before: nil)
36
+ params = extract_todo_params(limit: limit, starting_after: starting_after, since: since, before: before)
37
+ response = client.get("/v1/users/me/todos", params)
38
+ Models::Collection.from_json(response.extract_resource, item_class: Models::Todo)
39
+ end
40
+
41
+ # List projects for the authenticated user
42
+ # @param limit [Integer] Number of items to return (default: 25)
43
+ # @param starting_after [String] Cursor for pagination
44
+ # @return [Models::Collection<Models::Project>] The paginated projects
45
+ def projects(limit: nil, starting_after: nil)
46
+ params = extract_pagination_params(limit: limit, starting_after: starting_after)
47
+ response = client.get("/v1/users/me/projects", params)
48
+ Models::Collection.from_json(response.extract_resource, item_class: Models::Project)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wip
4
+ VERSION = "0.2.0"
5
+ end
data/lib/wip-ruby.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative "wip"
data/lib/wip.rb ADDED
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "wip/version"
4
+ require_relative "wip/configuration"
5
+ require_relative "wip/error"
6
+ require_relative "wip/http_client"
7
+ require_relative "wip/client"
8
+
9
+ # Models
10
+ require_relative "wip/models/base"
11
+ require_relative "wip/models/collection"
12
+ require_relative "wip/models/concerns/reactable"
13
+ require_relative "wip/models/user"
14
+ require_relative "wip/models/project"
15
+ require_relative "wip/models/todo"
16
+ require_relative "wip/models/comment"
17
+ require_relative "wip/models/reaction"
18
+
19
+ module Wip
20
+ class << self
21
+ # Returns the global configuration
22
+ # @return [Wip::Configuration]
23
+ def configuration
24
+ @configuration ||= Configuration.new
25
+ end
26
+
27
+ # Configure the gem
28
+ # @yield [config] Configuration instance
29
+ # @example
30
+ # Wip.configure do |config|
31
+ # config.api_key = "your-api-key"
32
+ # config.base_url = "https://api.wip.co"
33
+ # end
34
+ def configure
35
+ yield(configuration)
36
+ end
37
+
38
+ # Returns a new API client instance
39
+ # @return [Wip::Client]
40
+ def client
41
+ @client ||= Client.new(configuration)
42
+ end
43
+
44
+ # Reset the client and configuration
45
+ # Useful for testing
46
+ def reset!
47
+ @configuration = nil
48
+ @client = nil
49
+ end
50
+ end
51
+ end
data/sig/wip/ruby.rbs ADDED
@@ -0,0 +1,6 @@
1
+ module Wip
2
+ module Ruby
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end