pylon-api 0.1.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/CHANGELOG.md +34 -0
- data/LICENSE +21 -0
- data/README.md +111 -0
- data/lib/pylon/client.rb +387 -0
- data/lib/pylon/version.rb +16 -0
- data/lib/pylon.rb +24 -0
- data/lib/tasks/version.rake +95 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fcfe4ab02c112c8898998add96fc34ff4846029b2fc10bf2e2ce13c1f907a565
|
4
|
+
data.tar.gz: d8fd25a0307bbdc62e1a8921a95cf5859382f52d27303d25fec825db7f416ee7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7205621d5fe56ec66b58974d632eaa03e5be978a42e508656ad59583d499d348eef1ae91e6838da9f2ee3f9add0113f7e45151cce4578aebcd786cb2065a86ff
|
7
|
+
data.tar.gz: d0bd80e6ba44a85fb9d083263cf0ae74904d5fce043fb18de248cf6e4ff8807905b4dedc5508afc05af9c293c1b2f8778b860602969c48992b58a7896fe01c6a
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [0.1.0] - 2024-03-21
|
8
|
+
|
9
|
+
### Added
|
10
|
+
- Initial release of the Pylon API Ruby client
|
11
|
+
- Comprehensive client implementation with support for all major API endpoints:
|
12
|
+
- Accounts management
|
13
|
+
- Attachments handling
|
14
|
+
- Contacts management
|
15
|
+
- Custom fields
|
16
|
+
- Issues tracking
|
17
|
+
- Knowledge base articles
|
18
|
+
- Tags management
|
19
|
+
- Teams management
|
20
|
+
- Ticket forms
|
21
|
+
- User roles
|
22
|
+
- Users management
|
23
|
+
- Robust error handling with specific error classes:
|
24
|
+
- `Pylon::AuthenticationError`
|
25
|
+
- `Pylon::ResourceNotFoundError`
|
26
|
+
- `Pylon::ValidationError`
|
27
|
+
- `Pylon::ApiError`
|
28
|
+
- Pagination support for list endpoints
|
29
|
+
- Debug mode for request/response logging
|
30
|
+
- Comprehensive test suite with RSpec
|
31
|
+
- YARD documentation for all methods
|
32
|
+
- MIT License
|
33
|
+
|
34
|
+
[0.1.0]: https://github.com/benjodo/pylon-api/releases/tag/v0.1.0
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Aptible
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Pylon API Ruby Client
|
2
|
+
|
3
|
+
A Ruby client for the [Pylon API](https://docs.usepylon.com/pylon-docs/developer/api/api-reference).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'pylon-api'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
```bash
|
15
|
+
$ bundle install
|
16
|
+
```
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
```bash
|
20
|
+
$ gem install pylon-api
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
First, initialize a client with your API key:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'pylon'
|
29
|
+
|
30
|
+
client = Pylon::Client.new(api_key: 'your_api_key')
|
31
|
+
|
32
|
+
# Enable debug mode to see request/response details
|
33
|
+
client = Pylon::Client.new(api_key: 'your_api_key', debug: true)
|
34
|
+
```
|
35
|
+
|
36
|
+
### Examples
|
37
|
+
|
38
|
+
#### Current User
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# Get current user details
|
42
|
+
user, response = client.get_current_user
|
43
|
+
```
|
44
|
+
|
45
|
+
#### Accounts
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
# List accounts with pagination
|
49
|
+
accounts, response = client.list_accounts(page: 1, per_page: 20)
|
50
|
+
|
51
|
+
# Get a specific account
|
52
|
+
account, response = client.get_account('account_id')
|
53
|
+
```
|
54
|
+
|
55
|
+
#### Issues
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# List issues (requires time range, max 30 days)
|
59
|
+
start_time = Time.now.utc - 86400 # 24 hours ago
|
60
|
+
end_time = Time.now.utc
|
61
|
+
|
62
|
+
issues, response = client.list_issues(
|
63
|
+
start_time: start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
64
|
+
end_time: end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
65
|
+
page: 1,
|
66
|
+
per_page: 20,
|
67
|
+
status: 'open' # optional filter
|
68
|
+
)
|
69
|
+
|
70
|
+
# Create an issue
|
71
|
+
issue, response = client.create_issue(
|
72
|
+
title: 'New Issue',
|
73
|
+
description: 'Issue description'
|
74
|
+
)
|
75
|
+
```
|
76
|
+
|
77
|
+
#### Teams
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# List teams
|
81
|
+
teams, response = client.list_teams(page: 1, per_page: 20)
|
82
|
+
|
83
|
+
# Create a team
|
84
|
+
team, response = client.create_team(name: 'Engineering')
|
85
|
+
|
86
|
+
# Get a specific team
|
87
|
+
team, response = client.get_team('team_id')
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Users
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
# List users
|
94
|
+
users, response = client.list_users(page: 1, per_page: 20)
|
95
|
+
|
96
|
+
# Create a user
|
97
|
+
user, response = client.create_user(
|
98
|
+
email: 'user@example.com',
|
99
|
+
name: 'John Doe'
|
100
|
+
)
|
101
|
+
```
|
102
|
+
|
103
|
+
## Development
|
104
|
+
|
105
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
|
106
|
+
|
107
|
+
For more information about the project, including contributing guidelines and development setup, see the [main project README](../README.md).
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
Bug reports and pull requests are welcome on GitHub. Please see the [main project README](../README.md) for more details.
|
data/lib/pylon/client.rb
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pylon
|
4
|
+
# Client for interacting with the Pylon API
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# client = Pylon::Client.new(api_key: 'your_api_key')
|
8
|
+
# issues = client.list_issues(start_time: '2024-03-01T00:00:00Z', end_time: '2024-03-31T23:59:59Z')
|
9
|
+
class Client
|
10
|
+
# Base URL for the Pylon API
|
11
|
+
BASE_URL = "https://api.usepylon.com"
|
12
|
+
|
13
|
+
# @return [String] The API key used for authentication
|
14
|
+
attr_reader :api_key, :debug
|
15
|
+
|
16
|
+
# Initialize a new Pylon API client
|
17
|
+
#
|
18
|
+
# @param api_key [String] Your Pylon API key
|
19
|
+
# @param base_url [String] Optional base URL for the API (defaults to production)
|
20
|
+
# @param debug [Boolean] Whether to enable debug mode for request/response logging
|
21
|
+
def initialize(api_key:, base_url: BASE_URL, debug: false)
|
22
|
+
@api_key = api_key
|
23
|
+
@base_url = base_url
|
24
|
+
@debug = debug
|
25
|
+
end
|
26
|
+
|
27
|
+
# List all accounts with pagination
|
28
|
+
#
|
29
|
+
# @param page [Integer] Page number for pagination
|
30
|
+
# @param per_page [Integer] Number of items per page
|
31
|
+
# @return [Array<Hash>] List of accounts
|
32
|
+
def list_accounts(page: 1, per_page: 20)
|
33
|
+
get("/accounts", query: { page: page, per_page: per_page })
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get details for a specific account
|
37
|
+
#
|
38
|
+
# @param account_id [String] The ID of the account to retrieve
|
39
|
+
# @return [Hash] Account details
|
40
|
+
def get_account(account_id)
|
41
|
+
get("/accounts/#{account_id}")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create a new attachment
|
45
|
+
#
|
46
|
+
# @param file [String] The file content to upload
|
47
|
+
# @return [Hash] Created attachment details
|
48
|
+
def create_attachment(file)
|
49
|
+
post("/attachments", body: { file: file })
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get details for a specific attachment
|
53
|
+
#
|
54
|
+
# @param attachment_id [String] The ID of the attachment to retrieve
|
55
|
+
# @return [Hash] Attachment details
|
56
|
+
def get_attachment(attachment_id)
|
57
|
+
get("/attachments/#{attachment_id}")
|
58
|
+
end
|
59
|
+
|
60
|
+
# List all contacts with pagination
|
61
|
+
#
|
62
|
+
# @param page [Integer] Page number for pagination
|
63
|
+
# @param per_page [Integer] Number of items per page
|
64
|
+
# @return [Array<Hash>] List of contacts
|
65
|
+
def list_contacts(page: 1, per_page: 20)
|
66
|
+
get("/contacts", query: { page: page, per_page: per_page })
|
67
|
+
end
|
68
|
+
|
69
|
+
# Create a new contact
|
70
|
+
#
|
71
|
+
# @param params [Hash] Contact parameters
|
72
|
+
# @return [Hash] Created contact details
|
73
|
+
def create_contact(params)
|
74
|
+
post("/contacts", body: params)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Get details for a specific contact
|
78
|
+
#
|
79
|
+
# @param contact_id [String] The ID of the contact to retrieve
|
80
|
+
# @return [Hash] Contact details
|
81
|
+
def get_contact(contact_id)
|
82
|
+
get("/contacts/#{contact_id}")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Update an existing contact
|
86
|
+
#
|
87
|
+
# @param contact_id [String] The ID of the contact to update
|
88
|
+
# @param params [Hash] Updated contact parameters
|
89
|
+
# @return [Hash] Updated contact details
|
90
|
+
def update_contact(contact_id, params)
|
91
|
+
patch("/contacts/#{contact_id}", body: params)
|
92
|
+
end
|
93
|
+
|
94
|
+
# List all custom fields with pagination
|
95
|
+
#
|
96
|
+
# @param page [Integer] Page number for pagination
|
97
|
+
# @param per_page [Integer] Number of items per page
|
98
|
+
# @return [Array<Hash>] List of custom fields
|
99
|
+
def list_custom_fields(page: 1, per_page: 20)
|
100
|
+
get("/custom_fields", query: { page: page, per_page: per_page })
|
101
|
+
end
|
102
|
+
|
103
|
+
# Create a new custom field
|
104
|
+
#
|
105
|
+
# @param params [Hash] Custom field parameters
|
106
|
+
# @return [Hash] Created custom field details
|
107
|
+
def create_custom_field(params)
|
108
|
+
post("/custom_fields", body: params)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Lists issues within a specified time range (max 30 days)
|
112
|
+
#
|
113
|
+
# @param start_time [String] Start time in RFC3339 format
|
114
|
+
# @param end_time [String] End time in RFC3339 format
|
115
|
+
# @param page [Integer] Page number for pagination
|
116
|
+
# @param per_page [Integer] Number of items per page
|
117
|
+
# @param filters [Hash] Additional filters to apply
|
118
|
+
# @return [Array<Hash>] List of issues
|
119
|
+
# @raise [ArgumentError] If start_time or end_time is missing
|
120
|
+
def list_issues(start_time:, end_time:, page: 1, per_page: 20, **filters)
|
121
|
+
raise ArgumentError, "start_time is required" unless start_time
|
122
|
+
raise ArgumentError, "end_time is required" unless end_time
|
123
|
+
|
124
|
+
get("/issues", query: filters.merge(
|
125
|
+
start_time: start_time,
|
126
|
+
end_time: end_time,
|
127
|
+
page: page,
|
128
|
+
per_page: per_page
|
129
|
+
))
|
130
|
+
end
|
131
|
+
|
132
|
+
# Create a new issue
|
133
|
+
#
|
134
|
+
# @param params [Hash] Issue parameters
|
135
|
+
# @return [Hash] Created issue details
|
136
|
+
def create_issue(params)
|
137
|
+
post("/issues", body: params)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Get details for a specific issue
|
141
|
+
#
|
142
|
+
# @param issue_id [String] The ID of the issue to retrieve
|
143
|
+
# @return [Hash] Issue details
|
144
|
+
def get_issue(issue_id)
|
145
|
+
get("/issues/#{issue_id}")
|
146
|
+
end
|
147
|
+
|
148
|
+
# Update an existing issue
|
149
|
+
#
|
150
|
+
# @param issue_id [String] The ID of the issue to update
|
151
|
+
# @param params [Hash] Updated issue parameters
|
152
|
+
# @return [Hash] Updated issue details
|
153
|
+
def update_issue(issue_id, params)
|
154
|
+
patch("/issues/#{issue_id}", body: params)
|
155
|
+
end
|
156
|
+
|
157
|
+
# List all knowledge base articles with pagination
|
158
|
+
#
|
159
|
+
# @param page [Integer] Page number for pagination
|
160
|
+
# @param per_page [Integer] Number of items per page
|
161
|
+
# @return [Array<Hash>] List of articles
|
162
|
+
def list_articles(page: 1, per_page: 20)
|
163
|
+
get("/knowledge_base/articles", query: { page: page, per_page: per_page })
|
164
|
+
end
|
165
|
+
|
166
|
+
# Get details for a specific article
|
167
|
+
#
|
168
|
+
# @param article_id [String] The ID of the article to retrieve
|
169
|
+
# @return [Hash] Article details
|
170
|
+
def get_article(article_id)
|
171
|
+
get("/knowledge_base/articles/#{article_id}")
|
172
|
+
end
|
173
|
+
|
174
|
+
# Get details for the current user
|
175
|
+
#
|
176
|
+
# @return [Hash] Current user details
|
177
|
+
def get_current_user
|
178
|
+
get("/me")
|
179
|
+
end
|
180
|
+
|
181
|
+
# List all tags with pagination
|
182
|
+
#
|
183
|
+
# @param page [Integer] Page number for pagination
|
184
|
+
# @param per_page [Integer] Number of items per page
|
185
|
+
# @return [Array<Hash>] List of tags
|
186
|
+
def list_tags(page: 1, per_page: 20)
|
187
|
+
get("/tags", query: { page: page, per_page: per_page })
|
188
|
+
end
|
189
|
+
|
190
|
+
# Create a new tag
|
191
|
+
#
|
192
|
+
# @param name [String] The name of the tag
|
193
|
+
# @param color [String] Optional hex color code for the tag
|
194
|
+
# @return [Hash] Created tag details
|
195
|
+
def create_tag(name:, color: nil)
|
196
|
+
post("/tags", body: { name: name, color: color }.compact)
|
197
|
+
end
|
198
|
+
|
199
|
+
# List all teams with pagination
|
200
|
+
#
|
201
|
+
# @param page [Integer] Page number for pagination
|
202
|
+
# @param per_page [Integer] Number of items per page
|
203
|
+
# @return [Array<Hash>] List of teams
|
204
|
+
def list_teams(page: 1, per_page: 20)
|
205
|
+
get("/teams", query: { page: page, per_page: per_page })
|
206
|
+
end
|
207
|
+
|
208
|
+
# Create a new team
|
209
|
+
#
|
210
|
+
# @param params [Hash] Team parameters
|
211
|
+
# @return [Hash] Created team details
|
212
|
+
def create_team(params)
|
213
|
+
post("/teams", body: params)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Get details for a specific team
|
217
|
+
#
|
218
|
+
# @param team_id [String] The ID of the team to retrieve
|
219
|
+
# @return [Hash] Team details
|
220
|
+
def get_team(team_id)
|
221
|
+
get("/teams/#{team_id}")
|
222
|
+
end
|
223
|
+
|
224
|
+
# List all ticket forms with pagination
|
225
|
+
#
|
226
|
+
# @param page [Integer] Page number for pagination
|
227
|
+
# @param per_page [Integer] Number of items per page
|
228
|
+
# @return [Array<Hash>] List of ticket forms
|
229
|
+
def list_ticket_forms(page: 1, per_page: 20)
|
230
|
+
get("/ticket-forms", query: { page: page, per_page: per_page })
|
231
|
+
end
|
232
|
+
|
233
|
+
# Create a new ticket form
|
234
|
+
#
|
235
|
+
# @param name [String] The name of the form
|
236
|
+
# @param fields [Array<Hash>] Array of form field definitions
|
237
|
+
# @return [Hash] Created ticket form details
|
238
|
+
def create_ticket_form(name:, fields: [])
|
239
|
+
post("/ticket-forms", body: { name: name, fields: fields })
|
240
|
+
end
|
241
|
+
|
242
|
+
# List all user roles with pagination
|
243
|
+
#
|
244
|
+
# @param page [Integer] Page number for pagination
|
245
|
+
# @param per_page [Integer] Number of items per page
|
246
|
+
# @return [Array<Hash>] List of user roles
|
247
|
+
def list_user_roles(page: 1, per_page: 20)
|
248
|
+
get("/user_roles", query: { page: page, per_page: per_page })
|
249
|
+
end
|
250
|
+
|
251
|
+
# Get details for a specific user role
|
252
|
+
#
|
253
|
+
# @param role_id [String] The ID of the role to retrieve
|
254
|
+
# @return [Hash] Role details
|
255
|
+
def get_user_role(role_id)
|
256
|
+
get("/user_roles/#{role_id}")
|
257
|
+
end
|
258
|
+
|
259
|
+
# List all users with pagination
|
260
|
+
#
|
261
|
+
# @param page [Integer] Page number for pagination
|
262
|
+
# @param per_page [Integer] Number of items per page
|
263
|
+
# @return [Array<Hash>] List of users
|
264
|
+
def list_users(page: 1, per_page: 20)
|
265
|
+
get("/users", query: { page: page, per_page: per_page })
|
266
|
+
end
|
267
|
+
|
268
|
+
# Create a new user
|
269
|
+
#
|
270
|
+
# @param params [Hash] User parameters
|
271
|
+
# @return [Hash] Created user details
|
272
|
+
def create_user(params)
|
273
|
+
post("/users", body: params)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Get details for a specific user
|
277
|
+
#
|
278
|
+
# @param user_id [String] The ID of the user to retrieve
|
279
|
+
# @return [Hash] User details
|
280
|
+
def get_user(user_id)
|
281
|
+
get("/users/#{user_id}")
|
282
|
+
end
|
283
|
+
|
284
|
+
# Update an existing user
|
285
|
+
#
|
286
|
+
# @param user_id [String] The ID of the user to update
|
287
|
+
# @param params [Hash] Updated user parameters
|
288
|
+
# @return [Hash] Updated user details
|
289
|
+
def update_user(user_id, params)
|
290
|
+
patch("/users/#{user_id}", body: params)
|
291
|
+
end
|
292
|
+
|
293
|
+
private
|
294
|
+
|
295
|
+
# @return [Faraday::Connection] Configured Faraday connection
|
296
|
+
def connection
|
297
|
+
@connection ||= Faraday.new(@base_url) do |f|
|
298
|
+
f.request :json
|
299
|
+
f.request :multipart
|
300
|
+
f.response :json
|
301
|
+
f.response :logger if @debug
|
302
|
+
f.adapter Faraday.default_adapter
|
303
|
+
f.headers["Authorization"] = "Bearer #{api_key}"
|
304
|
+
f.headers["Content-Type"] = "application/json"
|
305
|
+
f.headers["Accept"] = "application/json"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Handle API response and raise appropriate errors
|
310
|
+
#
|
311
|
+
# @param response [Faraday::Response] The API response
|
312
|
+
# @return [Array] Array containing response data and response object
|
313
|
+
# @raise [AuthenticationError] If authentication fails
|
314
|
+
# @raise [ResourceNotFoundError] If resource is not found
|
315
|
+
# @raise [ValidationError] If request parameters are invalid
|
316
|
+
# @raise [ApiError] For other API errors
|
317
|
+
def handle_response(response)
|
318
|
+
if @debug
|
319
|
+
puts "Request URL: #{response.env.url}"
|
320
|
+
puts "Response status: #{response.status}"
|
321
|
+
puts "Response body: #{response.body.inspect}"
|
322
|
+
end
|
323
|
+
|
324
|
+
case response.status
|
325
|
+
when 200..299
|
326
|
+
data = response.body
|
327
|
+
data = data["data"] if data.is_a?(Hash) && data.key?("data")
|
328
|
+
[data, response]
|
329
|
+
when 401
|
330
|
+
raise AuthenticationError, parse_error_message(response)
|
331
|
+
when 404
|
332
|
+
raise ResourceNotFoundError, parse_error_message(response)
|
333
|
+
when 422
|
334
|
+
raise ValidationError, parse_error_message(response)
|
335
|
+
else
|
336
|
+
raise ApiError.new(parse_error_message(response), response)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# Parse error message from response
|
341
|
+
#
|
342
|
+
# @param response [Faraday::Response] The API response
|
343
|
+
# @return [String] Error message
|
344
|
+
def parse_error_message(response)
|
345
|
+
if response.body.is_a?(Hash)
|
346
|
+
response.body["errors"]&.first || response.body["error"] || "HTTP #{response.status}"
|
347
|
+
else
|
348
|
+
"HTTP #{response.status}"
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Make a GET request
|
353
|
+
#
|
354
|
+
# @param path [String] The API endpoint path
|
355
|
+
# @param query [Hash] Query parameters
|
356
|
+
# @return [Array] Array containing response data and response object
|
357
|
+
def get(path, query: {})
|
358
|
+
handle_response(connection.get(path, query))
|
359
|
+
end
|
360
|
+
|
361
|
+
# Make a POST request
|
362
|
+
#
|
363
|
+
# @param path [String] The API endpoint path
|
364
|
+
# @param body [Hash] Request body
|
365
|
+
# @return [Array] Array containing response data and response object
|
366
|
+
def post(path, body: {})
|
367
|
+
handle_response(connection.post(path, body.to_json))
|
368
|
+
end
|
369
|
+
|
370
|
+
# Make a PATCH request
|
371
|
+
#
|
372
|
+
# @param path [String] The API endpoint path
|
373
|
+
# @param body [Hash] Request body
|
374
|
+
# @return [Array] Array containing response data and response object
|
375
|
+
def patch(path, body: {})
|
376
|
+
handle_response(connection.patch(path, body.to_json))
|
377
|
+
end
|
378
|
+
|
379
|
+
# Make a DELETE request
|
380
|
+
#
|
381
|
+
# @param path [String] The API endpoint path
|
382
|
+
# @return [Array] Array containing response data and response object
|
383
|
+
def delete(path)
|
384
|
+
handle_response(connection.delete(path))
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pylon
|
4
|
+
# Major version for breaking changes
|
5
|
+
MAJOR = 0
|
6
|
+
# Minor version for new features
|
7
|
+
MINOR = 1
|
8
|
+
# Patch version for bug fixes
|
9
|
+
PATCH = 0
|
10
|
+
# Pre-release version (optional)
|
11
|
+
PRE = nil
|
12
|
+
|
13
|
+
# Version string following SemVer
|
14
|
+
# @return [String] The current version of the gem
|
15
|
+
VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
|
16
|
+
end
|
data/lib/pylon.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "faraday/multipart"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
require_relative "pylon/version"
|
8
|
+
require_relative "pylon/client"
|
9
|
+
|
10
|
+
module Pylon
|
11
|
+
class Error < StandardError; end
|
12
|
+
class AuthenticationError < Error; end
|
13
|
+
class ResourceNotFoundError < Error; end
|
14
|
+
class ValidationError < Error; end
|
15
|
+
|
16
|
+
class ApiError < Error
|
17
|
+
attr_reader :response
|
18
|
+
|
19
|
+
def initialize(message = nil, response = nil)
|
20
|
+
super(message)
|
21
|
+
@response = response
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
namespace :version do
|
6
|
+
desc "Display current version"
|
7
|
+
task :show do
|
8
|
+
require_relative "../pylon/version"
|
9
|
+
puts "Current version: #{Pylon::VERSION}"
|
10
|
+
end
|
11
|
+
|
12
|
+
%w[major minor patch].each do |part|
|
13
|
+
desc "Bump #{part} version"
|
14
|
+
task part.to_sym do
|
15
|
+
require_relative "../pylon/version"
|
16
|
+
|
17
|
+
# Read version.rb
|
18
|
+
version_file = File.read("lib/pylon/version.rb")
|
19
|
+
|
20
|
+
# Update the version constant
|
21
|
+
current_value = Pylon.const_get(part.upcase)
|
22
|
+
new_value = current_value + 1
|
23
|
+
|
24
|
+
# Reset lower-order versions for major/minor bumps
|
25
|
+
case part
|
26
|
+
when "major"
|
27
|
+
version_file.gsub!(/MINOR = \d+/, "MINOR = 0")
|
28
|
+
version_file.gsub!(/PATCH = \d+/, "PATCH = 0")
|
29
|
+
when "minor"
|
30
|
+
version_file.gsub!(/PATCH = \d+/, "PATCH = 0")
|
31
|
+
end
|
32
|
+
|
33
|
+
version_file.gsub!(/#{part.upcase} = \d+/, "#{part.upcase} = #{new_value}")
|
34
|
+
|
35
|
+
# Write the new version
|
36
|
+
File.write("lib/pylon/version.rb", version_file)
|
37
|
+
|
38
|
+
# Update CHANGELOG.md
|
39
|
+
changelog = File.read("CHANGELOG.md")
|
40
|
+
new_version = Pylon::VERSION.sub(/\d+$/, new_value.to_s)
|
41
|
+
|
42
|
+
changelog_entry = <<~ENTRY
|
43
|
+
## [#{new_version}] - #{Date.today.strftime('%Y-%m-%d')}
|
44
|
+
|
45
|
+
### #{part == 'patch' ? 'Fixed' : 'Added'}
|
46
|
+
- TODO: Add #{part} version changes
|
47
|
+
|
48
|
+
[#{new_version}]: https://github.com/benjodo/pylon-api/compare/v#{Pylon::VERSION}...v#{new_version}
|
49
|
+
|
50
|
+
ENTRY
|
51
|
+
|
52
|
+
# Split the changelog into parts
|
53
|
+
header, *rest = changelog.split(/^## \[\d/)
|
54
|
+
|
55
|
+
# Reconstruct the changelog with the new entry
|
56
|
+
updated_changelog = [
|
57
|
+
header,
|
58
|
+
changelog_entry,
|
59
|
+
rest.empty? ? "" : "## [#{rest.join('## [')}"
|
60
|
+
].join
|
61
|
+
|
62
|
+
File.write("CHANGELOG.md", updated_changelog)
|
63
|
+
|
64
|
+
puts "Bumped #{part} version to #{new_version}"
|
65
|
+
puts "Don't forget to:"
|
66
|
+
puts "1. Update CHANGELOG.md with your changes"
|
67
|
+
puts "2. Commit the changes"
|
68
|
+
puts "3. Create and push a new tag: git tag -a v#{new_version} -m 'Version #{new_version}'"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Add a pre-release identifier (e.g., alpha, beta, rc)"
|
73
|
+
task :pre, [:identifier] do |_t, args|
|
74
|
+
abort "Please provide a pre-release identifier (e.g., alpha, beta, rc)" unless args[:identifier]
|
75
|
+
|
76
|
+
version_file = File.read("lib/pylon/version.rb")
|
77
|
+
version_file.gsub!(/PRE = .*$/, "PRE = '#{args[:identifier]}'")
|
78
|
+
|
79
|
+
File.write("lib/pylon/version.rb", version_file)
|
80
|
+
|
81
|
+
require_relative "../pylon/version"
|
82
|
+
puts "Updated version to #{Pylon::VERSION}"
|
83
|
+
end
|
84
|
+
|
85
|
+
desc "Remove pre-release identifier"
|
86
|
+
task :release do
|
87
|
+
version_file = File.read("lib/pylon/version.rb")
|
88
|
+
version_file.gsub!(/PRE = .*$/, "PRE = nil")
|
89
|
+
|
90
|
+
File.write("lib/pylon/version.rb", version_file)
|
91
|
+
|
92
|
+
require_relative "../pylon/version"
|
93
|
+
puts "Updated version to #{Pylon::VERSION}"
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pylon-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Odom
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.12.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.12.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday-multipart
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.22.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.22.0
|
111
|
+
description: A Ruby client library for interacting with the Pylon REST API
|
112
|
+
email:
|
113
|
+
- support@aptible.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- CHANGELOG.md
|
119
|
+
- LICENSE
|
120
|
+
- README.md
|
121
|
+
- lib/pylon.rb
|
122
|
+
- lib/pylon/client.rb
|
123
|
+
- lib/pylon/version.rb
|
124
|
+
- lib/tasks/version.rake
|
125
|
+
homepage: https://github.com/benjodo/pylon-api
|
126
|
+
licenses:
|
127
|
+
- MIT
|
128
|
+
metadata:
|
129
|
+
homepage_uri: https://github.com/benjodo/pylon-api
|
130
|
+
source_code_uri: https://github.com/benjodo/pylon-api
|
131
|
+
changelog_uri: https://github.com/benjodo/pylon-api/blob/main/CHANGELOG.md
|
132
|
+
documentation_uri: https://docs.usepylon.com/pylon-docs/developer/api/api-reference
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 3.0.0
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubygems_version: 3.3.7
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: Ruby client for the Pylon API
|
152
|
+
test_files: []
|