diff --git a/CHANGELOG.md b/CHANGELOG.md index b16ac7d..ace13ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0] - 2026-09-20 + +### Added +- `list_email_suppressions` for `GET /email-suppressions` with optional `email`, `cursor`, and `limit` query parameters. Pass `email` to check a single address; an empty collection means it is not suppressed. +- `delete_email_suppression` for `DELETE /email-suppressions/{id}` +- `Pylon::Models::EmailSuppression` model (`id`, `email`, `reason`, `created_at`, `bounce_details`) + +### Changed +- Documentation only: README examples now use `body_html` when creating issues and note the 365-day window for `list_issues`. No existing method signatures, paths, or return types changed. + +## [1.1.1] - 2025-04-18 + +### Fixed +- Fixed URL-based file attachments by ensuring they use proper multipart form encoding + ## [1.1.0] - 2024-04-16 ### Added @@ -51,6 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - YARD documentation for all methods - MIT License +[1.2.0]: https://github.com/benjodo/pylon-api/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/benjodo/pylon-api/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/benjodo/pylon-api/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/benjodo/pylon-api/compare/v0.2.0...v1.0.0 [0.2.0]: https://github.com/benjodo/pylon-api/compare/v0.1.0...v0.2.0 diff --git a/README.md b/README.md index e01739a..ff9b221 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ response = account._response #### Issues ```ruby -# List issues (requires time range, max 30 days) +# List issues (requires time range, max 365 days) start_time = Time.now.utc - 86400 # 24 hours ago end_time = Time.now.utc @@ -89,19 +89,19 @@ issues = client.list_issues( start_time: start_time.iso8601, end_time: end_time.iso8601, page: 1, - per_page: 20, - status: 'open' # optional filter + per_page: 20 ) # Iterate through issues issues.each do |issue| - puts "#{issue.id}: #{issue.title} (#{issue.status})" + puts "#{issue.id}: #{issue.title} (#{issue.state})" end -# Create an issue +# Create an issue (title and body_html are required by the API) issue = client.create_issue( title: 'New Issue', - description: 'Issue description' + body_html: '
Issue description
', + requester_email: 'customer@example.com' ) # Access issue properties directly @@ -185,6 +185,31 @@ attachment = client.create_attachment(nil, ) ``` +#### Email Suppressions + +Pylon stops sending email to an address once it hard bounces or is suppressed manually. Use these methods to check and clear suppressions. + +```ruby +# Check whether a single address is suppressed +suppressions = client.list_email_suppressions(email: 'user@example.com') + +if suppressions.size.zero? + puts 'not suppressed' +else + suppression = suppressions.first + puts "#{suppression.email} suppressed (#{suppression.reason}) at #{suppression.created_at}" + puts suppression.bounce_details if suppression.bounce_details +end + +# List suppressions, most recent first, using the API's cursor pagination +page = client.list_email_suppressions(limit: 100) +pagination = page._response.body['pagination'] +next_page = client.list_email_suppressions(cursor: pagination['cursor']) if pagination['has_next_page'] + +# Remove a suppression so Pylon resumes sending to the address +client.delete_email_suppression(suppression.id) +``` + ## Error Handling The client will raise different types of errors based on the API response: diff --git a/pylon-api/.rubocop.yml b/pylon-api/.rubocop.yml index 61389f5..42095fc 100644 --- a/pylon-api/.rubocop.yml +++ b/pylon-api/.rubocop.yml @@ -1,4 +1,4 @@ -require: +plugins: - rubocop-rake - rubocop-rspec @@ -73,12 +73,6 @@ RSpec/NestedGroups: RSpec/PredicateMatcher: Enabled: false -RSpec/Capybara: - Enabled: false - -RSpec/FactoryBot: - Enabled: false - RSpec/MultipleExpectations: Enabled: false diff --git a/pylon-api/CHANGELOG.md b/pylon-api/CHANGELOG.md index 7712cc6..ac3be88 100644 --- a/pylon-api/CHANGELOG.md +++ b/pylon-api/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [1.2.0] - 2026-09-20 + +### Added +- `list_email_suppressions` for `GET /email-suppressions` with optional `email`, `cursor`, and `limit` query parameters. Pass `email` to check a single address; an empty collection means it is not suppressed. +- `delete_email_suppression` for `DELETE /email-suppressions/{id}` +- `Pylon::Models::EmailSuppression` model (`id`, `email`, `reason`, `created_at`, `bounce_details`) + +### Changed +- Documentation only: README examples now use `body_html` when creating issues and note the 365-day window for `list_issues`. No existing method signatures, paths, or return types changed. + ## [1.1.1] - 2025-04-18 ### Fixed diff --git a/pylon-api/Gemfile b/pylon-api/Gemfile index 82c3347..3211e97 100644 --- a/pylon-api/Gemfile +++ b/pylon-api/Gemfile @@ -7,6 +7,6 @@ gem "faraday", "~> 1.10.4" gem "json", "~> 2.0" group :development do - gem "rubocop-rake", "~> 0.6.0" - gem "rubocop-rspec", "~> 2.26.1" + gem "rubocop-rake", "~> 0.7" + gem "rubocop-rspec", "~> 3.0" end diff --git a/pylon-api/README.md b/pylon-api/README.md index 740b895..f57b256 100644 --- a/pylon-api/README.md +++ b/pylon-api/README.md @@ -55,25 +55,35 @@ account, response = client.get_account('account_id') #### Issues ```ruby -# List issues (requires time range, max 30 days) +# List issues (requires time range, max 365 days) start_time = Time.now.utc - 86400 # 24 hours ago end_time = Time.now.utc -issues, response = client.list_issues( +issues = client.list_issues( start_time: start_time.strftime("%Y-%m-%dT%H:%M:%SZ"), end_time: end_time.strftime("%Y-%m-%dT%H:%M:%SZ"), page: 1, - per_page: 20, - status: 'open' # optional filter + per_page: 20 ) -# Create an issue -issue, response = client.create_issue( +# Create an issue (title and body_html are required by the API) +issue = client.create_issue( title: 'New Issue', - description: 'Issue description' + body_html: 'Issue description
', + requester_email: 'customer@example.com' ) ``` +#### Email Suppressions + +```ruby +# Check whether a single address is suppressed (empty collection means not suppressed) +suppressions = client.list_email_suppressions(email: 'user@example.com') + +# Remove a suppression +client.delete_email_suppression(suppressions.first.id) unless suppressions.size.zero? +``` + #### Teams ```ruby diff --git a/pylon-api/lib/pylon.rb b/pylon-api/lib/pylon.rb index ac47cff..ae7846f 100644 --- a/pylon-api/lib/pylon.rb +++ b/pylon-api/lib/pylon.rb @@ -17,6 +17,7 @@ require_relative "pylon/models/contact" require_relative "pylon/models/ticket_form" require_relative "pylon/models/article" +require_relative "pylon/models/email_suppression" require_relative "pylon/client" module Pylon diff --git a/pylon-api/lib/pylon/client.rb b/pylon-api/lib/pylon/client.rb index 056a538..6bf0779 100644 --- a/pylon-api/lib/pylon/client.rb +++ b/pylon-api/lib/pylon/client.rb @@ -152,7 +152,30 @@ def create_custom_field(params) post("/custom_fields", body: params) end - # Lists issues within a specified time range (max 30 days) + # List email suppressions, most recently suppressed first + # + # Pass +email+ to check a single address. An empty collection means the + # address is not suppressed. Uses the API's cursor-based pagination. + # + # @param email [String, nil] Only return the suppression for this address, if one exists + # @param cursor [String, nil] Cursor for the next page of results + # @param limit [Integer, nil] Number of suppressions to fetch (API default 100, max 1000) + # @return [Models::Collection