pylon-api 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/pylon/client.rb +210 -79
- data/lib/pylon/models/account.rb +8 -0
- data/lib/pylon/models/article.rb +8 -0
- data/lib/pylon/models/attachment.rb +8 -0
- data/lib/pylon/models/base.rb +40 -0
- data/lib/pylon/models/collection.rb +39 -0
- data/lib/pylon/models/contact.rb +8 -0
- data/lib/pylon/models/issue.rb +8 -0
- data/lib/pylon/models/tag.rb +8 -0
- data/lib/pylon/models/team.rb +8 -0
- data/lib/pylon/models/ticket_form.rb +8 -0
- data/lib/pylon/models/user.rb +8 -0
- data/lib/pylon/version.rb +2 -2
- data/lib/pylon.rb +12 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f54f7ed75e823866e06256d1a598bb3158e686131156932b7aad3d9a90da9c7
|
4
|
+
data.tar.gz: fe68e2c1be8128acadd4179b2baa2751fd9223fdbe1a5c66cb35d2d41c13fae0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6282c28251d01ed1f7c196fbb421c3e895d37681da8e71e5941ed74db3ff88aff39e8e9c8604fe899d555e61aec5bc7b8824fcdf30e6e789c3b74086d25ae7ee
|
7
|
+
data.tar.gz: bf00a26818a96154c91a2a1f281d219372f0fd97b8b0075ddbb70fcebaa675c28b31e55a10bf218303fcc4e2b0ad15229a30950e2f2a5e41cd174995940c0a2a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [1.1.1] - 2025-04-18
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
- Fixed URL-based file attachments by ensuring they use proper multipart form encoding
|
7
|
+
|
8
|
+
## [1.1.0] - 2025-04-17
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Support for URL-based file attachments
|
12
|
+
- Enhanced attachment handling for various file types
|
13
|
+
- Added MIME type detection for file uploads
|
14
|
+
|
3
15
|
## [1.0.0] - 2024-03-21
|
4
16
|
|
5
17
|
### Changed
|
data/lib/pylon/client.rb
CHANGED
@@ -6,7 +6,23 @@ module Pylon
|
|
6
6
|
# @example
|
7
7
|
# client = Pylon::Client.new(api_key: 'your_api_key')
|
8
8
|
# issues = client.list_issues(start_time: '2024-03-01T00:00:00Z', end_time: '2024-03-31T23:59:59Z')
|
9
|
+
# issues.each { |issue| puts issue.title }
|
10
|
+
# rubocop:disable Metrics/ClassLength
|
9
11
|
class Client
|
12
|
+
# Common MIME types for file uploads
|
13
|
+
MIME_TYPES = {
|
14
|
+
".jpg" => "image/jpeg",
|
15
|
+
".jpeg" => "image/jpeg",
|
16
|
+
".png" => "image/png",
|
17
|
+
".gif" => "image/gif",
|
18
|
+
".pdf" => "application/pdf",
|
19
|
+
".doc" => "application/msword",
|
20
|
+
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
21
|
+
".xls" => "application/vnd.ms-excel",
|
22
|
+
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
23
|
+
".csv" => "text/csv",
|
24
|
+
".txt" => "text/plain"
|
25
|
+
}
|
10
26
|
# Base URL for the Pylon API
|
11
27
|
BASE_URL = "https://api.usepylon.com"
|
12
28
|
|
@@ -28,76 +44,104 @@ module Pylon
|
|
28
44
|
#
|
29
45
|
# @param page [Integer] Page number for pagination
|
30
46
|
# @param per_page [Integer] Number of items per page
|
31
|
-
# @return [
|
47
|
+
# @return [Models::Collection<Models::Account>] Collection of account objects
|
32
48
|
def list_accounts(page: 1, per_page: 20)
|
33
|
-
get("/accounts", query: { page: page, per_page: per_page }
|
49
|
+
get("/accounts", query: { page: page, per_page: per_page },
|
50
|
+
model_class: Models::Account, collection: true)
|
34
51
|
end
|
35
52
|
|
36
53
|
# Get details for a specific account
|
37
54
|
#
|
38
55
|
# @param account_id [String] The ID of the account to retrieve
|
39
|
-
# @return [
|
56
|
+
# @return [Models::Account] Account object
|
40
57
|
def get_account(account_id)
|
41
|
-
get("/accounts/#{account_id}")
|
58
|
+
get("/accounts/#{account_id}", model_class: Models::Account)
|
42
59
|
end
|
43
60
|
|
44
61
|
# Create a new attachment
|
45
62
|
#
|
46
|
-
# @param file [String] The file
|
47
|
-
# @
|
48
|
-
|
49
|
-
|
63
|
+
# @param file [File, String, nil] The file to upload (File object or file content)
|
64
|
+
# @param description [String, nil] A text description of the file
|
65
|
+
# @param file_url [String, nil] A URL to fetch the file from if file is not provided
|
66
|
+
# @return [Models::Attachment] Created attachment object
|
67
|
+
# @raise [ArgumentError] If neither file nor file_url is provided
|
68
|
+
def create_attachment(file = nil, description: nil, file_url: nil)
|
69
|
+
if file.nil? && file_url.nil?
|
70
|
+
raise ArgumentError, "Either file or file_url must be provided"
|
71
|
+
end
|
72
|
+
|
73
|
+
params = {}
|
74
|
+
params[:description] = description if description
|
75
|
+
params[:file_url] = file_url if file_url
|
76
|
+
|
77
|
+
if file
|
78
|
+
if file.is_a?(::File) || (file.respond_to?(:path) && file.respond_to?(:read))
|
79
|
+
# For File objects or IO-like objects
|
80
|
+
params[:file] = Faraday::UploadIO.new(file, mime_type_for_file(file), File.basename(file.path))
|
81
|
+
else
|
82
|
+
# For raw content as string, create a temp file
|
83
|
+
temp_file = Tempfile.new(["upload", ".bin"])
|
84
|
+
temp_file.binmode
|
85
|
+
temp_file.write(file)
|
86
|
+
temp_file.rewind
|
87
|
+
params[:file] = Faraday::UploadIO.new(temp_file, "application/octet-stream")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Always use multipart for attachments, whether file or file_url
|
92
|
+
post_multipart("/attachments", params, model_class: Models::Attachment)
|
50
93
|
end
|
51
94
|
|
52
95
|
# Get details for a specific attachment
|
53
96
|
#
|
54
97
|
# @param attachment_id [String] The ID of the attachment to retrieve
|
55
|
-
# @return [
|
98
|
+
# @return [Models::Attachment] Attachment object
|
56
99
|
def get_attachment(attachment_id)
|
57
|
-
get("/attachments/#{attachment_id}")
|
100
|
+
get("/attachments/#{attachment_id}", model_class: Models::Attachment)
|
58
101
|
end
|
59
102
|
|
60
103
|
# List all contacts with pagination
|
61
104
|
#
|
62
105
|
# @param page [Integer] Page number for pagination
|
63
106
|
# @param per_page [Integer] Number of items per page
|
64
|
-
# @return [
|
107
|
+
# @return [Models::Collection<Models::Contact>] Collection of contact objects
|
65
108
|
def list_contacts(page: 1, per_page: 20)
|
66
|
-
get("/contacts", query: { page: page, per_page: per_page }
|
109
|
+
get("/contacts", query: { page: page, per_page: per_page },
|
110
|
+
model_class: Models::Contact, collection: true)
|
67
111
|
end
|
68
112
|
|
69
113
|
# Create a new contact
|
70
114
|
#
|
71
115
|
# @param params [Hash] Contact parameters
|
72
|
-
# @return [
|
116
|
+
# @return [Models::Contact] Created contact object
|
73
117
|
def create_contact(params)
|
74
|
-
post("/contacts", body: params)
|
118
|
+
post("/contacts", body: params, model_class: Models::Contact)
|
75
119
|
end
|
76
120
|
|
77
121
|
# Get details for a specific contact
|
78
122
|
#
|
79
123
|
# @param contact_id [String] The ID of the contact to retrieve
|
80
|
-
# @return [
|
124
|
+
# @return [Models::Contact] Contact object
|
81
125
|
def get_contact(contact_id)
|
82
|
-
get("/contacts/#{contact_id}")
|
126
|
+
get("/contacts/#{contact_id}", model_class: Models::Contact)
|
83
127
|
end
|
84
128
|
|
85
129
|
# Update an existing contact
|
86
130
|
#
|
87
131
|
# @param contact_id [String] The ID of the contact to update
|
88
132
|
# @param params [Hash] Updated contact parameters
|
89
|
-
# @return [
|
133
|
+
# @return [Models::Contact] Updated contact object
|
90
134
|
def update_contact(contact_id, params)
|
91
|
-
patch("/contacts/#{contact_id}", body: params)
|
135
|
+
patch("/contacts/#{contact_id}", body: params, model_class: Models::Contact)
|
92
136
|
end
|
93
137
|
|
94
138
|
# List all custom fields with pagination
|
95
139
|
#
|
96
140
|
# @param page [Integer] Page number for pagination
|
97
141
|
# @param per_page [Integer] Number of items per page
|
98
|
-
# @return [
|
142
|
+
# @return [Models::Collection] Collection of custom fields
|
99
143
|
def list_custom_fields(page: 1, per_page: 20)
|
100
|
-
get("/custom_fields", query: { page: page, per_page: per_page })
|
144
|
+
get("/custom_fields", query: { page: page, per_page: per_page }, collection: true)
|
101
145
|
end
|
102
146
|
|
103
147
|
# Create a new custom field
|
@@ -115,7 +159,7 @@ module Pylon
|
|
115
159
|
# @param page [Integer] Page number for pagination
|
116
160
|
# @param per_page [Integer] Number of items per page
|
117
161
|
# @param filters [Hash] Additional filters to apply
|
118
|
-
# @return [
|
162
|
+
# @return [Models::Collection<Models::Issue>] Collection of issue objects
|
119
163
|
# @raise [ArgumentError] If start_time or end_time is missing
|
120
164
|
def list_issues(start_time:, end_time:, page: 1, per_page: 20, **filters)
|
121
165
|
raise ArgumentError, "start_time is required" unless start_time
|
@@ -126,135 +170,140 @@ module Pylon
|
|
126
170
|
end_time: end_time,
|
127
171
|
page: page,
|
128
172
|
per_page: per_page
|
129
|
-
))
|
173
|
+
), model_class: Models::Issue, collection: true)
|
130
174
|
end
|
131
175
|
|
132
176
|
# Create a new issue
|
133
177
|
#
|
134
178
|
# @param params [Hash] Issue parameters
|
135
|
-
# @return [
|
179
|
+
# @return [Models::Issue] Created issue object
|
136
180
|
def create_issue(params)
|
137
|
-
post("/issues", body: params)
|
181
|
+
post("/issues", body: params, model_class: Models::Issue)
|
138
182
|
end
|
139
183
|
|
140
184
|
# Get details for a specific issue
|
141
185
|
#
|
142
186
|
# @param issue_id [String] The ID of the issue to retrieve
|
143
|
-
# @return [
|
187
|
+
# @return [Models::Issue] Issue object
|
144
188
|
def get_issue(issue_id)
|
145
|
-
get("/issues/#{issue_id}")
|
189
|
+
get("/issues/#{issue_id}", model_class: Models::Issue)
|
146
190
|
end
|
147
191
|
|
148
192
|
# Update an existing issue
|
149
193
|
#
|
150
194
|
# @param issue_id [String] The ID of the issue to update
|
151
195
|
# @param params [Hash] Updated issue parameters
|
152
|
-
# @return [
|
196
|
+
# @return [Models::Issue] Updated issue object
|
153
197
|
def update_issue(issue_id, params)
|
154
|
-
patch("/issues/#{issue_id}", body: params)
|
198
|
+
patch("/issues/#{issue_id}", body: params, model_class: Models::Issue)
|
155
199
|
end
|
156
200
|
|
157
201
|
# Snooze an issue until a specified time
|
158
202
|
#
|
159
203
|
# @param issue_id [String] The ID or number of the issue to snooze
|
160
204
|
# @param snooze_until [String] The date and time to snooze the issue until (RFC3339 format)
|
161
|
-
# @return [
|
205
|
+
# @return [Models::Issue] Updated issue object
|
162
206
|
def snooze_issue(issue_id, snooze_until:)
|
163
|
-
post("/issues/#{issue_id}/snooze", body: { snooze_until: snooze_until }
|
207
|
+
post("/issues/#{issue_id}/snooze", body: { snooze_until: snooze_until },
|
208
|
+
model_class: Models::Issue)
|
164
209
|
end
|
165
210
|
|
166
211
|
# List all knowledge base articles with pagination
|
167
212
|
#
|
168
213
|
# @param page [Integer] Page number for pagination
|
169
214
|
# @param per_page [Integer] Number of items per page
|
170
|
-
# @return [
|
215
|
+
# @return [Models::Collection<Models::Article>] Collection of article objects
|
171
216
|
def list_articles(page: 1, per_page: 20)
|
172
|
-
get("/knowledge_base/articles", query: { page: page, per_page: per_page }
|
217
|
+
get("/knowledge_base/articles", query: { page: page, per_page: per_page },
|
218
|
+
model_class: Models::Article, collection: true)
|
173
219
|
end
|
174
220
|
|
175
221
|
# Get details for a specific article
|
176
222
|
#
|
177
223
|
# @param article_id [String] The ID of the article to retrieve
|
178
|
-
# @return [
|
224
|
+
# @return [Models::Article] Article object
|
179
225
|
def get_article(article_id)
|
180
|
-
get("/knowledge_base/articles/#{article_id}")
|
226
|
+
get("/knowledge_base/articles/#{article_id}", model_class: Models::Article)
|
181
227
|
end
|
182
228
|
|
183
229
|
# Get details for the current user
|
184
230
|
#
|
185
|
-
# @return [
|
231
|
+
# @return [Models::User] Current user object
|
186
232
|
def get_current_user
|
187
|
-
get("/me")
|
233
|
+
get("/me", model_class: Models::User)
|
188
234
|
end
|
189
235
|
|
190
236
|
# List all tags with pagination
|
191
237
|
#
|
192
238
|
# @param page [Integer] Page number for pagination
|
193
239
|
# @param per_page [Integer] Number of items per page
|
194
|
-
# @return [
|
240
|
+
# @return [Models::Collection<Models::Tag>] Collection of tag objects
|
195
241
|
def list_tags(page: 1, per_page: 20)
|
196
|
-
get("/tags", query: { page: page, per_page: per_page }
|
242
|
+
get("/tags", query: { page: page, per_page: per_page },
|
243
|
+
model_class: Models::Tag, collection: true)
|
197
244
|
end
|
198
245
|
|
199
246
|
# Create a new tag
|
200
247
|
#
|
201
248
|
# @param name [String] The name of the tag
|
202
249
|
# @param color [String] Optional hex color code for the tag
|
203
|
-
# @return [
|
250
|
+
# @return [Models::Tag] Created tag object
|
204
251
|
def create_tag(name:, color: nil)
|
205
|
-
post("/tags", body: { name: name, color: color }.compact)
|
252
|
+
post("/tags", body: { name: name, color: color }.compact, model_class: Models::Tag)
|
206
253
|
end
|
207
254
|
|
208
255
|
# List all teams with pagination
|
209
256
|
#
|
210
257
|
# @param page [Integer] Page number for pagination
|
211
258
|
# @param per_page [Integer] Number of items per page
|
212
|
-
# @return [
|
259
|
+
# @return [Models::Collection<Models::Team>] Collection of team objects
|
213
260
|
def list_teams(page: 1, per_page: 20)
|
214
|
-
get("/teams", query: { page: page, per_page: per_page }
|
261
|
+
get("/teams", query: { page: page, per_page: per_page },
|
262
|
+
model_class: Models::Team, collection: true)
|
215
263
|
end
|
216
264
|
|
217
265
|
# Create a new team
|
218
266
|
#
|
219
267
|
# @param params [Hash] Team parameters
|
220
|
-
# @return [
|
268
|
+
# @return [Models::Team] Created team object
|
221
269
|
def create_team(params)
|
222
|
-
post("/teams", body: params)
|
270
|
+
post("/teams", body: params, model_class: Models::Team)
|
223
271
|
end
|
224
272
|
|
225
273
|
# Get details for a specific team
|
226
274
|
#
|
227
275
|
# @param team_id [String] The ID of the team to retrieve
|
228
|
-
# @return [
|
276
|
+
# @return [Models::Team] Team object
|
229
277
|
def get_team(team_id)
|
230
|
-
get("/teams/#{team_id}")
|
278
|
+
get("/teams/#{team_id}", model_class: Models::Team)
|
231
279
|
end
|
232
280
|
|
233
281
|
# List all ticket forms with pagination
|
234
282
|
#
|
235
283
|
# @param page [Integer] Page number for pagination
|
236
284
|
# @param per_page [Integer] Number of items per page
|
237
|
-
# @return [
|
285
|
+
# @return [Models::Collection<Models::TicketForm>] Collection of ticket form objects
|
238
286
|
def list_ticket_forms(page: 1, per_page: 20)
|
239
|
-
get("/ticket-forms", query: { page: page, per_page: per_page }
|
287
|
+
get("/ticket-forms", query: { page: page, per_page: per_page },
|
288
|
+
model_class: Models::TicketForm, collection: true)
|
240
289
|
end
|
241
290
|
|
242
291
|
# Create a new ticket form
|
243
292
|
#
|
244
293
|
# @param name [String] The name of the form
|
245
294
|
# @param fields [Array<Hash>] Array of form field definitions
|
246
|
-
# @return [
|
295
|
+
# @return [Models::TicketForm] Created ticket form object
|
247
296
|
def create_ticket_form(name:, fields: [])
|
248
|
-
post("/ticket-forms", body: { name: name, fields: fields })
|
297
|
+
post("/ticket-forms", body: { name: name, fields: fields }, model_class: Models::TicketForm)
|
249
298
|
end
|
250
299
|
|
251
300
|
# List all user roles with pagination
|
252
301
|
#
|
253
302
|
# @param page [Integer] Page number for pagination
|
254
303
|
# @param per_page [Integer] Number of items per page
|
255
|
-
# @return [
|
304
|
+
# @return [Models::Collection] Collection of user role objects
|
256
305
|
def list_user_roles(page: 1, per_page: 20)
|
257
|
-
get("/user_roles", query: { page: page, per_page: per_page })
|
306
|
+
get("/user_roles", query: { page: page, per_page: per_page }, collection: true)
|
258
307
|
end
|
259
308
|
|
260
309
|
# Get details for a specific user role
|
@@ -269,34 +318,35 @@ module Pylon
|
|
269
318
|
#
|
270
319
|
# @param page [Integer] Page number for pagination
|
271
320
|
# @param per_page [Integer] Number of items per page
|
272
|
-
# @return [
|
321
|
+
# @return [Models::Collection<Models::User>] Collection of user objects
|
273
322
|
def list_users(page: 1, per_page: 20)
|
274
|
-
get("/users", query: { page: page, per_page: per_page }
|
323
|
+
get("/users", query: { page: page, per_page: per_page },
|
324
|
+
model_class: Models::User, collection: true)
|
275
325
|
end
|
276
326
|
|
277
327
|
# Create a new user
|
278
328
|
#
|
279
329
|
# @param params [Hash] User parameters
|
280
|
-
# @return [
|
330
|
+
# @return [Models::User] Created user object
|
281
331
|
def create_user(params)
|
282
|
-
post("/users", body: params)
|
332
|
+
post("/users", body: params, model_class: Models::User)
|
283
333
|
end
|
284
334
|
|
285
335
|
# Get details for a specific user
|
286
336
|
#
|
287
337
|
# @param user_id [String] The ID of the user to retrieve
|
288
|
-
# @return [
|
338
|
+
# @return [Models::User] User object
|
289
339
|
def get_user(user_id)
|
290
|
-
get("/users/#{user_id}")
|
340
|
+
get("/users/#{user_id}", model_class: Models::User)
|
291
341
|
end
|
292
342
|
|
293
343
|
# Update an existing user
|
294
344
|
#
|
295
345
|
# @param user_id [String] The ID of the user to update
|
296
346
|
# @param params [Hash] Updated user parameters
|
297
|
-
# @return [
|
347
|
+
# @return [Models::User] Updated user object
|
298
348
|
def update_user(user_id, params)
|
299
|
-
patch("/users/#{user_id}", body: params)
|
349
|
+
patch("/users/#{user_id}", body: params, model_class: Models::User)
|
300
350
|
end
|
301
351
|
|
302
352
|
private
|
@@ -315,26 +365,71 @@ module Pylon
|
|
315
365
|
end
|
316
366
|
end
|
317
367
|
|
368
|
+
# Determine MIME type for file
|
369
|
+
#
|
370
|
+
# @param file [File] The file to determine MIME type for
|
371
|
+
# @return [String] The MIME type
|
372
|
+
def mime_type_for_file(file)
|
373
|
+
return "application/octet-stream" unless file.respond_to?(:path)
|
374
|
+
|
375
|
+
ext = File.extname(file.path).downcase
|
376
|
+
MIME_TYPES[ext] || "application/octet-stream"
|
377
|
+
end
|
378
|
+
|
318
379
|
# Handle API response and raise appropriate errors
|
319
380
|
#
|
320
381
|
# @param response [Faraday::Response] The API response
|
321
|
-
# @
|
382
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
383
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
384
|
+
# @return [Models::Base, Models::Collection, Array] Model, Collection, or response data array
|
322
385
|
# @raise [AuthenticationError] If authentication fails
|
323
386
|
# @raise [ResourceNotFoundError] If resource is not found
|
324
387
|
# @raise [ValidationError] If request parameters are invalid
|
325
388
|
# @raise [ApiError] For other API errors
|
326
|
-
def handle_response(response)
|
389
|
+
def handle_response(response, model_class = nil, collection = false)
|
327
390
|
if @debug
|
328
391
|
puts "Request URL: #{response.env.url}"
|
329
392
|
puts "Response status: #{response.status}"
|
330
393
|
puts "Response body: #{response.body.inspect}"
|
331
394
|
end
|
332
395
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
396
|
+
if response.status.between?(200, 299)
|
397
|
+
return handle_successful_response(response, model_class, collection)
|
398
|
+
else
|
399
|
+
handle_error_response(response)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# Handle successful API response
|
404
|
+
#
|
405
|
+
# @param response [Faraday::Response] The API response
|
406
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
407
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
408
|
+
# @return [Models::Base, Models::Collection, Array] Wrapped response data
|
409
|
+
def handle_successful_response(response, model_class, collection)
|
410
|
+
data = response.body
|
411
|
+
data = data["data"] if data.is_a?(Hash) && data.key?("data")
|
412
|
+
|
413
|
+
if model_class
|
414
|
+
if collection
|
415
|
+
Models::Collection.new(data, model_class, response)
|
416
|
+
else
|
417
|
+
model_class.new(data, response)
|
418
|
+
end
|
419
|
+
else
|
337
420
|
[data, response]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Handle error API response
|
425
|
+
#
|
426
|
+
# @param response [Faraday::Response] The API response
|
427
|
+
# @raise [AuthenticationError] If authentication fails
|
428
|
+
# @raise [ResourceNotFoundError] If resource is not found
|
429
|
+
# @raise [ValidationError] If request parameters are invalid
|
430
|
+
# @raise [ApiError] For other API errors
|
431
|
+
def handle_error_response(response)
|
432
|
+
case response.status
|
338
433
|
when 401
|
339
434
|
raise AuthenticationError, parse_error_message(response) || "Invalid API key"
|
340
435
|
when 404
|
@@ -366,35 +461,71 @@ module Pylon
|
|
366
461
|
#
|
367
462
|
# @param path [String] The API endpoint path
|
368
463
|
# @param query [Hash] Query parameters
|
369
|
-
# @
|
370
|
-
|
371
|
-
|
464
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
465
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
466
|
+
# @return [Models::Base, Models::Collection, Array] Model, Collection, or response data array
|
467
|
+
def get(path, query: {}, model_class: nil, collection: false)
|
468
|
+
handle_response(connection.get(path, query), model_class, collection)
|
372
469
|
end
|
373
470
|
|
374
471
|
# Make a POST request
|
375
472
|
#
|
376
473
|
# @param path [String] The API endpoint path
|
377
474
|
# @param body [Hash] Request body
|
378
|
-
# @
|
379
|
-
|
380
|
-
|
475
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
476
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
477
|
+
# @return [Models::Base, Models::Collection, Array] Model, Collection, or response data array
|
478
|
+
def post(path, body: {}, model_class: nil, collection: false)
|
479
|
+
handle_response(connection.post(path, body.to_json), model_class, collection)
|
480
|
+
end
|
481
|
+
|
482
|
+
# Make a multipart POST request for file uploads
|
483
|
+
#
|
484
|
+
# @param path [String] The API endpoint path
|
485
|
+
# @param params [Hash] Request parameters including file uploads
|
486
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
487
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
488
|
+
# @return [Models::Base, Models::Collection, Array] Model, Collection, or response data array
|
489
|
+
def post_multipart(path, params = {}, model_class: nil, collection: false)
|
490
|
+
# Create a connection without the default JSON content type for multipart uploads
|
491
|
+
multipart_conn = Faraday.new(@base_url) do |f|
|
492
|
+
f.request :multipart
|
493
|
+
# Only force multipart for file_url uploads, omit url_encoded middleware
|
494
|
+
f.response :json, content_type: /\bjson$/
|
495
|
+
f.response :logger if @debug
|
496
|
+
f.adapter Faraday.default_adapter
|
497
|
+
f.headers["Authorization"] = "Bearer #{api_key}"
|
498
|
+
f.headers["Accept"] = "application/json"
|
499
|
+
end
|
500
|
+
|
501
|
+
# If we have a file_url but no actual file, create a dummy file part to force multipart encoding
|
502
|
+
if params[:file_url] && !params[:file]
|
503
|
+
# Create an empty file part to force multipart encoding
|
504
|
+
params[:_dummy] = Faraday::FilePart.new(StringIO.new(""), "application/octet-stream", "dummy")
|
505
|
+
end
|
506
|
+
|
507
|
+
handle_response(multipart_conn.post(path, params), model_class, collection)
|
381
508
|
end
|
382
509
|
|
383
510
|
# Make a PATCH request
|
384
511
|
#
|
385
512
|
# @param path [String] The API endpoint path
|
386
513
|
# @param body [Hash] Request body
|
387
|
-
# @
|
388
|
-
|
389
|
-
|
514
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
515
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
516
|
+
# @return [Models::Base, Models::Collection, Array] Model, Collection, or response data array
|
517
|
+
def patch(path, body: {}, model_class: nil, collection: false)
|
518
|
+
handle_response(connection.patch(path, body.to_json), model_class, collection)
|
390
519
|
end
|
391
520
|
|
392
521
|
# Make a DELETE request
|
393
522
|
#
|
394
523
|
# @param path [String] The API endpoint path
|
395
|
-
# @
|
396
|
-
|
397
|
-
|
524
|
+
# @param model_class [Class] The model class to use for wrapping the response
|
525
|
+
# @param collection [Boolean] Whether the response is a collection of items
|
526
|
+
# @return [Models::Base, Models::Collection, Array] Model, Collection, or response data array
|
527
|
+
def delete(path, model_class: nil, collection: false)
|
528
|
+
handle_response(connection.delete(path), model_class, collection)
|
398
529
|
end
|
399
530
|
end
|
400
|
-
end
|
531
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pylon
|
4
|
+
module Models
|
5
|
+
class Base
|
6
|
+
attr_reader :attributes, :_response
|
7
|
+
|
8
|
+
def initialize(attributes = {}, response = nil)
|
9
|
+
@attributes = attributes || {}
|
10
|
+
@_response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_name, *args, &block)
|
14
|
+
key = method_name.to_s
|
15
|
+
if @attributes.key?(key)
|
16
|
+
@attributes[key]
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(method_name, include_private = false)
|
23
|
+
@attributes.key?(method_name.to_s) || super
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](key)
|
27
|
+
@attributes[key.to_s]
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
@attributes
|
32
|
+
end
|
33
|
+
alias to_hash to_h
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
"#<#{self.class.name} #{@attributes.inspect}>"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pylon
|
4
|
+
module Models
|
5
|
+
class Collection
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :items, :_response
|
9
|
+
|
10
|
+
def initialize(items = [], model_class = nil, response = nil)
|
11
|
+
@items = items.map do |item|
|
12
|
+
model_class ? model_class.new(item) : item
|
13
|
+
end
|
14
|
+
@_response = response
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
@items.each(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](index)
|
22
|
+
@items[index]
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
@items.size
|
27
|
+
end
|
28
|
+
alias length size
|
29
|
+
|
30
|
+
def to_a
|
31
|
+
@items
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
"#<#{self.class.name} items=#{@items.size}>"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/pylon/version.rb
CHANGED
data/lib/pylon.rb
CHANGED
@@ -3,8 +3,20 @@
|
|
3
3
|
require "faraday"
|
4
4
|
require "faraday/multipart"
|
5
5
|
require "json"
|
6
|
+
require "tempfile"
|
6
7
|
|
7
8
|
require_relative "pylon/version"
|
9
|
+
require_relative "pylon/models/base"
|
10
|
+
require_relative "pylon/models/collection"
|
11
|
+
require_relative "pylon/models/user"
|
12
|
+
require_relative "pylon/models/account"
|
13
|
+
require_relative "pylon/models/issue"
|
14
|
+
require_relative "pylon/models/team"
|
15
|
+
require_relative "pylon/models/tag"
|
16
|
+
require_relative "pylon/models/attachment"
|
17
|
+
require_relative "pylon/models/contact"
|
18
|
+
require_relative "pylon/models/ticket_form"
|
19
|
+
require_relative "pylon/models/article"
|
8
20
|
require_relative "pylon/client"
|
9
21
|
|
10
22
|
module Pylon
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pylon-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Odom
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -134,6 +134,17 @@ files:
|
|
134
134
|
- README.md
|
135
135
|
- lib/pylon.rb
|
136
136
|
- lib/pylon/client.rb
|
137
|
+
- lib/pylon/models/account.rb
|
138
|
+
- lib/pylon/models/article.rb
|
139
|
+
- lib/pylon/models/attachment.rb
|
140
|
+
- lib/pylon/models/base.rb
|
141
|
+
- lib/pylon/models/collection.rb
|
142
|
+
- lib/pylon/models/contact.rb
|
143
|
+
- lib/pylon/models/issue.rb
|
144
|
+
- lib/pylon/models/tag.rb
|
145
|
+
- lib/pylon/models/team.rb
|
146
|
+
- lib/pylon/models/ticket_form.rb
|
147
|
+
- lib/pylon/models/user.rb
|
137
148
|
- lib/pylon/version.rb
|
138
149
|
- lib/tasks/version.rake
|
139
150
|
homepage: https://github.com/benjodo/pylon-api
|