purelymail 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5e4bdda524618b9f54fa2db24e50c78720d0e37ed092dd2a90943095a65ef1e1
4
+ data.tar.gz: e4167c8d6e8ff29be55e479bb45acdaf159e97edd30a8ce901a2f643b2902b48
5
+ SHA512:
6
+ metadata.gz: f3ea3625f9066135040dfc9981b84a9f7f465ea75b4bd634a63be1c8b6da07eeaeae037c9477d80b2bb87cc582f39ad0e8c694d052031dac9fa9de051458b8d2
7
+ data.tar.gz: 56076ffc74848db74ac31a3c8f8b0c1c54f0960e6db801260ccf3c3747ad2026f7b3901e9ac8fd0cb189aae5144e85b61f1cb7dd0c7594f6dcc4a8b44ae19716
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # Purelymail
2
+
3
+ A standalone Ruby client for the [Purelymail API](https://purelymail.com/).
4
+
5
+ ## Installation
6
+
7
+ Add this to your `Gemfile`:
8
+
9
+ ```ruby
10
+ gem "purelymail"
11
+ ```
12
+
13
+ Then run `bundle install`.
14
+
15
+ Or install it yourself:
16
+
17
+ ```bash
18
+ gem install purelymail
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Per-Instance Configuration
24
+
25
+ ```ruby
26
+ client = Purelymail::Client.new(api_token: "pm-live-xxxxxxxx")
27
+ client.create_domain(name: "example.com")
28
+ ```
29
+
30
+ ### Global Configuration
31
+
32
+ ```ruby
33
+ Purelymail.configure do |config|
34
+ config.api_token = "pm-live-xxxxxxxx"
35
+ end
36
+
37
+ client = Purelymail::Client.new
38
+ client.create_user(name: "alice", domain: "example.com", password: "s3cret")
39
+ ```
40
+
41
+ You can mix both: pass an `api_token` to `Client.new` to override the global config for that instance.
42
+
43
+ ### API Methods
44
+
45
+ ```ruby
46
+ client = Purelymail::Client.new(api_token: "pm-live-xxxxxxxx")
47
+
48
+ client.create_domain(name: "example.com")
49
+
50
+ client.create_user(name: "alice", domain: "example.com", password: "s3cret")
51
+
52
+ client.change_password(name: "alice", domain: "example.com", password: "newpass")
53
+
54
+ client.create_routing_rule(
55
+ domain_name: "example.com",
56
+ match_user: "alice",
57
+ target_addresses: ["alice@destination.com"],
58
+ prefix: false,
59
+ catchall: false
60
+ )
61
+
62
+ client.configured?
63
+ # => true
64
+ ```
65
+
66
+ ### Error Handling
67
+
68
+ All API errors raise `Purelymail::ApiError`:
69
+
70
+ ```ruby
71
+ begin
72
+ client.create_domain(name: "invalid@domain")
73
+ rescue Purelymail::ApiError => e
74
+ puts e.message # => "[Purelymail] addDomain failed: ..."
75
+ puts e.status # => 400
76
+ puts e.response # => {"type" => "error", "message" => "..."}
77
+ end
78
+ ```
79
+
80
+ ## Rails Integration
81
+
82
+ Add the gem to your `Gemfile`:
83
+
84
+ ```ruby
85
+ gem "purelymail"
86
+ ```
87
+
88
+ Configure via Rails credentials:
89
+
90
+ ```bash
91
+ bin/rails credentials:edit
92
+ ```
93
+
94
+ Add:
95
+
96
+ ```yaml
97
+ purelymail:
98
+ api_token: pm-live-xxxxxxxx
99
+ ```
100
+
101
+ The gem automatically picks up `Rails.application.credentials.dig(:purelymail, :api_token)` as a fallback, so you can use the client without any explicit configuration:
102
+
103
+ ```ruby
104
+ # config/initializers/purelymail.rb
105
+ Purelymail.configure do |config|
106
+ # config.api_token is optional here —
107
+ # it will fall back to Rails.application.credentials.purelymail.api_token
108
+ end
109
+ ```
110
+
111
+ Then anywhere in your app:
112
+
113
+ ```ruby
114
+ Purelymail::Client.new.create_domain(name: "example.com")
115
+ ```
116
+
117
+ If you need to override the token at the call site, pass it directly:
118
+
119
+ ```ruby
120
+ Purelymail::Client.new(api_token: "pm-live-yyyyyyyy")
121
+ ```
122
+
123
+ ## Development
124
+
125
+ After checking out the repo, run:
126
+
127
+ ```bash
128
+ bundle install
129
+ bundle exec rspec
130
+ ```
131
+
132
+ ## License
133
+
134
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/retry"
5
+
6
+ module Purelymail
7
+ class Client
8
+ BASE_URL = "https://purelymail.com/api/v0/"
9
+
10
+ def initialize(api_token: nil)
11
+ @api_token = api_token || Purelymail.configuration.api_token
12
+ end
13
+
14
+ def create_domain(name:)
15
+ post("addDomain", { domainName: name })
16
+ end
17
+
18
+ def create_user(name:, domain:, password:)
19
+ post("createUser", { userName: name, domainName: domain, password: password })
20
+ end
21
+
22
+ def change_password(name:, domain:, password:)
23
+ post("changePassword", { userName: name, domainName: domain, password: password })
24
+ end
25
+
26
+ def create_routing_rule(domain_name:, match_user:, target_addresses:, prefix: false, catchall: false)
27
+ post("createRoutingRule", {
28
+ domainName: domain_name,
29
+ matchUser: match_user,
30
+ targetAddresses: Array(target_addresses),
31
+ prefix: prefix,
32
+ catchall: catchall
33
+ })
34
+ end
35
+
36
+ def configured?
37
+ !api_token.nil? && api_token.to_s.strip != ""
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :api_token
43
+
44
+ def connection
45
+ @connection ||= Faraday.new(url: BASE_URL) do |f|
46
+ f.options.timeout = 5
47
+ f.options.open_timeout = 3
48
+ f.request :json
49
+ f.response :json
50
+ f.request :retry, max: 3, interval: 0.5, interval_randomness: 0.5,
51
+ backoff_factor: 2,
52
+ exceptions: [Faraday::ServerError, Faraday::TimeoutError, Faraday::ConnectionFailed]
53
+ f.adapter Faraday.default_adapter
54
+ f.headers["Purelymail-Api-Token"] = api_token
55
+ end
56
+ end
57
+
58
+ def post(endpoint, body)
59
+ response = connection.post(endpoint, body)
60
+ handle_response(response, endpoint)
61
+ end
62
+
63
+ def handle_response(response, endpoint)
64
+ body = response.body
65
+
66
+ if response.success? && body.is_a?(Hash) && body["type"] != "error"
67
+ body
68
+ else
69
+ error_msg = if body.is_a?(Hash)
70
+ body["message"] || body["code"]
71
+ else
72
+ "HTTP Status #{response.status}"
73
+ end
74
+
75
+ raise ApiError.new(
76
+ "[Purelymail] #{endpoint} failed: #{error_msg}",
77
+ status: response.status,
78
+ response: body
79
+ )
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Purelymail
4
+ class Configuration
5
+ attr_writer :api_token
6
+
7
+ def api_token
8
+ @api_token || rails_credentials_token
9
+ end
10
+
11
+ private
12
+
13
+ def rails_credentials_token
14
+ if defined?(Rails) && Rails.respond_to?(:application) && Rails.application.respond_to?(:credentials)
15
+ Rails.application.credentials.dig(:purelymail, :api_token)
16
+ end
17
+ end
18
+ end
19
+
20
+ class << self
21
+ def configuration
22
+ @configuration ||= Configuration.new
23
+ end
24
+
25
+ def configure
26
+ yield(configuration)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Purelymail
4
+ class ApiError < StandardError
5
+ attr_reader :status, :response
6
+
7
+ def initialize(message = nil, status: nil, response: nil)
8
+ super(message)
9
+ @status = status
10
+ @response = response
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Purelymail
4
+ VERSION = "0.1.0"
5
+ end
data/lib/purelymail.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "purelymail/version"
4
+ require "purelymail/error"
5
+ require "purelymail/configuration"
6
+ require "purelymail/client"
7
+
8
+ module Purelymail
9
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: purelymail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ben D'Angelo
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 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.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
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.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ description: A standalone Ruby gem for interacting with the Purelymail API. Manage
41
+ domains, users, routing rules, and more.
42
+ email:
43
+ - ben@bendangelo.me
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - lib/purelymail.rb
50
+ - lib/purelymail/client.rb
51
+ - lib/purelymail/configuration.rb
52
+ - lib/purelymail/error.rb
53
+ - lib/purelymail/version.rb
54
+ homepage: https://github.com/bendangelo/purelymail-rb
55
+ licenses:
56
+ - MIT
57
+ metadata:
58
+ homepage_uri: https://github.com/bendangelo/purelymail-rb
59
+ source_code_uri: https://github.com/bendangelo/purelymail-rb
60
+ changelog_uri: https://github.com/bendangelo/purelymail-rb/blob/main/CHANGELOG.md
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.7.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 4.0.10
76
+ specification_version: 4
77
+ summary: Ruby client for the Purelymail API
78
+ test_files: []