sidemail 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 10bbf092935efd7fce7c872480e85a75d50193f2c40d167c3c091b6f2e476061
4
+ data.tar.gz: 5ec58344812bf3582f84426ecaeb009a47019bed904a08f635b33886fcbee513
5
+ SHA512:
6
+ metadata.gz: 4366cbc8907914d820ec5b2e9f80dcac57a409109c92861efa479921b4c015467e6c73bfac1b76f80a05ddfbd8e967794f4f4aee8e479c325bcaed22676a6b5e
7
+ data.tar.gz: 1f0c2492beadd451a6971f3b82e9d1c87208082f5908ef926d0cbe2d75b22a7a4a9a85910c946f51e5981f469ae5d438597900aae660ed964db54c10aa28a7b2
data/README.md ADDED
@@ -0,0 +1,395 @@
1
+ # Sidemail Ruby Library
2
+
3
+ Official Sidemail.io Ruby library providing convenient access to the Sidemail API from Ruby applications.
4
+
5
+ ## Requirements
6
+
7
+ - Ruby 2.6+
8
+
9
+ ## Installation
10
+
11
+ Using RubyGems:
12
+
13
+ ```bash
14
+ gem install sidemail
15
+ ```
16
+
17
+ Or using Bundler:
18
+
19
+ ```bash
20
+ bundle add sidemail
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ First, the client needs to be configured with your project's API key, which you can find in the Sidemail Dashboard after you sign up.
26
+
27
+ ```ruby
28
+ require "sidemail"
29
+
30
+ # Initialize with API key
31
+ sm = Sidemail.new(api_key: "your-api-key")
32
+
33
+ # Or set environment variable SIDEMAIL_API_KEY
34
+ # sm = Sidemail.new
35
+ ```
36
+
37
+ ```ruby
38
+ response = sm.send_email(
39
+ toAddress: "user@email.com",
40
+ fromAddress: "you@example.com",
41
+ fromName: "Your App",
42
+ templateName: "Welcome",
43
+ templateProps: { foo: "bar" }
44
+ )
45
+
46
+ puts "Email sent! ID: #{response.id}"
47
+ ```
48
+
49
+ The response looks like:
50
+
51
+ ```json
52
+ {
53
+ "id": "5e858953daf20f3aac50a3da",
54
+ "status": "queued"
55
+ }
56
+ ```
57
+
58
+ Shortcut `sm.send_email(...)` calls `sm.email.send(...)` under the hood.
59
+
60
+ ### Authentication
61
+
62
+ Explicit key:
63
+
64
+ ```ruby
65
+ require "sidemail"
66
+ sm = Sidemail.new(api_key: "your-api-key")
67
+ ```
68
+
69
+ Or if you set environment variable `SIDEMAIL_API_KEY`, then simply:
70
+
71
+ ```ruby
72
+ require "sidemail"
73
+ sm = Sidemail.new # reads SIDEMAIL_API_KEY
74
+ ```
75
+
76
+ ### Client configuration
77
+
78
+ ```ruby
79
+ require "sidemail"
80
+
81
+ sm = Sidemail.new(
82
+ api_key: "your-api-key",
83
+ base_url: "https://api.sidemail.io/v1", # override for testing/mocking
84
+ timeout: 10.0, # per-request timeout (seconds)
85
+ http_client: custom_http_client, # custom Net::HTTP client (proxies, retries, etc.)
86
+ )
87
+ ```
88
+
89
+ ## Email Sending Examples
90
+
91
+ ### Password reset template
92
+
93
+ ```ruby
94
+ sm.send_email(
95
+ toAddress: "user@email.com",
96
+ fromAddress: "you@example.com",
97
+ fromName: "Your App",
98
+ templateName: "Password reset",
99
+ templateProps: { resetUrl: "https://your.app/reset?token=123" }
100
+ )
101
+ ```
102
+
103
+ ### Schedule email delivery
104
+
105
+ ```ruby
106
+ require "date"
107
+
108
+ # Schedule for 60 minutes from now
109
+ scheduled_iso = (Time.now + 60 * 60).utc.iso8601
110
+
111
+ sm.send_email(
112
+ toAddress: "user@email.com",
113
+ fromAddress: "your@startup.com",
114
+ fromName: "Startup name",
115
+ templateName: "Welcome",
116
+ templateProps: { firstName: "Patrik" },
117
+ scheduledAt: scheduled_iso
118
+ )
119
+ ```
120
+
121
+ ### Template with dynamic list
122
+
123
+ ```ruby
124
+ sm.send_email(
125
+ toAddress: "user@email.com",
126
+ fromAddress: "your@startup.com",
127
+ fromName: "Startup name",
128
+ templateName: "Template with dynamic list",
129
+ templateProps: {
130
+ list: [
131
+ { text: "Dynamic list" },
132
+ { text: "allows you to generate email template content" },
133
+ { text: "based on template props." }
134
+ ]
135
+ }
136
+ )
137
+ ```
138
+
139
+ ### Custom HTML email
140
+
141
+ ```ruby
142
+ sm.send_email(
143
+ toAddress: "user@email.com",
144
+ fromAddress: "your@startup.com",
145
+ fromName: "Startup name",
146
+ subject: "Testing html only custom emails :)",
147
+ html: "<html><body><h1>Hello world! 👋</h1></body></html>"
148
+ )
149
+ ```
150
+
151
+ ### Custom plain text email
152
+
153
+ ```ruby
154
+ sm.send_email(
155
+ toAddress: "user@email.com",
156
+ fromAddress: "your@startup.com",
157
+ fromName: "Startup name",
158
+ subject: "Testing plain-text only custom emails :)",
159
+ text: "Hello world! 👋"
160
+ )
161
+ ```
162
+
163
+ ## Error handling
164
+
165
+ The SDK raises `Sidemail::Error` for all errors. API errors include `message`, `http_status`, `error_code`, and `more_info`.
166
+
167
+ ```ruby
168
+ require "sidemail"
169
+
170
+ sm = Sidemail.new(api_key: "your-api-key")
171
+
172
+ begin
173
+ sm.send_email(
174
+ toAddress: "user@example.com",
175
+ fromAddress: "you@example.com",
176
+ subject: "Hello",
177
+ text: "Hello"
178
+ )
179
+ rescue Sidemail::Error => e
180
+ puts e.message
181
+ if e.http_status # API error
182
+ puts "#{e.http_status} #{e.error_code} #{e.more_info}"
183
+ end
184
+ end
185
+ ```
186
+
187
+ ## Response objects
188
+
189
+ Most responses are wrapped in a `Resource` enabling attribute access while remaining hash-like.
190
+
191
+ - Methods return `Resource` wrappers (attribute + hash access); unwrap via `.to_h`.
192
+ - Original JSON available via `.raw`.
193
+
194
+ ```ruby
195
+ response = sm.email.get("email-id")
196
+ puts "#{response.email.id} #{response.email.status}"
197
+ puts response.email["id"] # hash-style
198
+ raw_json = response.raw # original JSON mapping
199
+ flat_hash = response.to_h # fully unwrapped hash
200
+ ```
201
+
202
+ ## Attachments helper
203
+
204
+ ```ruby
205
+ require "sidemail"
206
+
207
+ file_content = File.read("invoice.pdf")
208
+ attachment = Sidemail.file_to_attachment("invoice.pdf", file_content)
209
+
210
+ sm.send_email(
211
+ toAddress: "user@email.com",
212
+ fromAddress: "you@example.com",
213
+ subject: "Invoice",
214
+ text: "Invoice attached.",
215
+ attachments: [attachment]
216
+ )
217
+ ```
218
+
219
+ ## Auto-pagination
220
+
221
+ List/search methods return a `PaginatedResponse` containing the first page in `result.data`. Iterate across all pages with `auto_paginate`.
222
+
223
+ ```ruby
224
+ result = sm.contacts.list(limit: 50)
225
+
226
+ result.auto_paginate.each do |contact|
227
+ puts contact.emailAddress
228
+ end
229
+ ```
230
+
231
+ Supported auto-paging methods:
232
+
233
+ - `sm.contacts.list`
234
+ - `sm.contacts.query`
235
+ - `sm.email.search`
236
+ - `sm.messenger.list`
237
+
238
+ ## Email Methods
239
+
240
+ ### Search emails
241
+
242
+ Paginated (supports auto-pagination).
243
+
244
+ ```ruby
245
+ result = sm.email.search(
246
+ query: {
247
+ toAddress: "john.doe@example.com",
248
+ status: "delivered",
249
+ templateProps: { foo: "bar" }
250
+ },
251
+ limit: 50
252
+ )
253
+
254
+ puts "First page count: #{result.items.count}"
255
+ result.auto_paginate.each do |email|
256
+ puts "#{email.id} #{email.status}"
257
+ end
258
+ ```
259
+
260
+ ### Retrieve a specific email
261
+
262
+ ```ruby
263
+ resp = sm.email.get("SIDEMAIL_EMAIL_ID")
264
+ puts resp.email
265
+ ```
266
+
267
+ ### Delete a scheduled email
268
+
269
+ Only scheduled (future) emails can be deleted.
270
+
271
+ ```ruby
272
+ resp = sm.email.delete("SIDEMAIL_EMAIL_ID")
273
+ puts "Deleted: #{resp.deleted}"
274
+ ```
275
+
276
+ ## Contact Methods
277
+
278
+ ### Create or update a contact
279
+
280
+ ```ruby
281
+ resp = sm.contacts.create_or_update(
282
+ emailAddress: "marry@lightning.com",
283
+ identifier: "123",
284
+ customProps: {
285
+ name: "Marry Lightning"
286
+ # ... more props ...
287
+ }
288
+ )
289
+ puts "Contact status: #{resp.status}"
290
+ ```
291
+
292
+ ### Find a contact
293
+
294
+ ```ruby
295
+ resp = sm.contacts.find("marry@lightning.com")
296
+ if resp.contact
297
+ puts "Found contact: #{resp.contact.emailAddress}"
298
+ end
299
+ ```
300
+
301
+ ### List all contacts
302
+
303
+ ```ruby
304
+ result = sm.contacts.list(limit: 50)
305
+ puts "Has more: #{result.has_more}"
306
+ result.auto_paginate.each do |c|
307
+ puts c.emailAddress
308
+ end
309
+ ```
310
+
311
+ ### Query contacts (filtering)
312
+
313
+ ```ruby
314
+ result = sm.contacts.query(limit: 100, query: { "customProps.plan" => "pro" })
315
+ result.auto_paginate.each do |c|
316
+ puts c.emailAddress
317
+ end
318
+ ```
319
+
320
+ ### Delete a contact
321
+
322
+ ```ruby
323
+ resp = sm.contacts.delete("marry@lightning.com")
324
+ puts resp
325
+ ```
326
+
327
+ ## Project Methods
328
+
329
+ Linked projects are associated with the parent project of the API key used to initialize Sidemail. After creation, update the design to personalize templates.
330
+
331
+ ### Create a linked project
332
+
333
+ ```ruby
334
+ project = sm.project.create(name: "Customer X linked project")
335
+ # Important! Save project.apiKey for later use
336
+ ```
337
+
338
+ ### Update a linked project
339
+
340
+ ```ruby
341
+ updated = sm.project.update(
342
+ name: "New name",
343
+ emailTemplateDesign: {
344
+ logo: {
345
+ sizeWidth: 50,
346
+ href: "https://example.com",
347
+ file: "PHN2ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTIgNS43MmMtMi42MjQtNC41MTctMTAtMy4xOTgtMTAgMi40NjEgMCAzLjcyNSA0LjM0NSA3LjcyNyA5LjMwMyAxMi41NC4xOTQuMTg5LjQ0Ni4yODMuNjk3LjI4M3MuNTAzLS4wOTQuNjk3LS4yODNjNC45NzctNC44MzEgOS4zMDMtOC44MTQgOS4zMDMtMTIuNTQgMC01LjY3OC03LjM5Ni02Ljk0NC0xMC0yLjQ2MXoiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==",
348
+ },
349
+ font: { name: "Acme" },
350
+ colors: { highlight: "#0000FF", isDarkModeEnabled: true },
351
+ unsubscribeText: "Darse de baja",
352
+ footerTextTransactional: "You're receiving these emails because you registered for Acme Inc."
353
+ }
354
+ )
355
+ ```
356
+
357
+ ### Get a project
358
+
359
+ ```ruby
360
+ project = sm.project.get
361
+ puts "#{project.id} #{project.name}"
362
+ ```
363
+
364
+ ### Delete a linked project
365
+
366
+ ```ruby
367
+ resp = sm.project.delete
368
+ puts resp
369
+ ```
370
+
371
+ ## Messenger API (newsletters)
372
+
373
+ ```ruby
374
+ result = sm.messenger.list(limit: 20)
375
+ result.auto_paginate.each do |m|
376
+ puts "#{m.id} #{m.name}"
377
+ end
378
+
379
+ messenger = sm.messenger.get("messenger-id")
380
+ created = sm.messenger.create(subject: "My Messenger", markdown: "Broadcast message...")
381
+ updated = sm.messenger.update("messenger-id", name: "Updated name")
382
+ deleted = sm.messenger.delete("messenger-id")
383
+ ```
384
+
385
+ ## Sending domains API
386
+
387
+ ```ruby
388
+ domains = sm.domains.list
389
+ domain = sm.domains.create(name: "example.com")
390
+ deleted = sm.domains.delete("domain-id")
391
+ ```
392
+
393
+ ## More Info
394
+
395
+ Visit [Sidemail docs](https://sidemail.io/docs) for more information.
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+ require "base64"
7
+ require_relative "paginated_response"
8
+ require_relative "resources/email"
9
+ require_relative "resources/contact"
10
+ require_relative "resources/project"
11
+ require_relative "resources/messenger"
12
+ require_relative "resources/domain"
13
+
14
+ module Sidemail
15
+ class Client
16
+ DEFAULT_BASE_URL = "https://api.sidemail.io/v1"
17
+
18
+ attr_reader :api_key, :base_url, :timeout, :http_client
19
+
20
+ def initialize(api_key: nil, base_url: DEFAULT_BASE_URL, timeout: nil, http_client: nil)
21
+ @api_key = api_key || ENV["SIDEMAIL_API_KEY"]
22
+ raise Sidemail::Error.new("apiKey missing. Provide it as an option or set SIDEMAIL_API_KEY environment variable.") unless @api_key
23
+ @base_url = base_url
24
+ @timeout = timeout
25
+ @http_client = http_client
26
+ end
27
+
28
+ def email
29
+ @email ||= Resources::Email.new(self)
30
+ end
31
+
32
+ def contacts
33
+ @contacts ||= Resources::Contact.new(self)
34
+ end
35
+
36
+ def project
37
+ @project ||= Resources::Project.new(self)
38
+ end
39
+
40
+ def messenger
41
+ @messenger ||= Resources::Messenger.new(self)
42
+ end
43
+
44
+ def domains
45
+ @domains ||= Resources::Domain.new(self)
46
+ end
47
+
48
+ def send_email(params)
49
+ email.send(params)
50
+ end
51
+
52
+ def perform_request(path, params: nil, method: :get)
53
+ base = @base_url.end_with?("/") ? @base_url : "#{@base_url}/"
54
+ uri = URI.parse("#{base}#{path}")
55
+
56
+ if method == :get && params
57
+ # Add query params
58
+ existing_query = URI.decode_www_form(uri.query || "")
59
+ new_query = existing_query + params.map { |k, v| [k.to_s, v] }
60
+ uri.query = URI.encode_www_form(new_query)
61
+ end
62
+
63
+ http = if @http_client
64
+ @http_client
65
+ else
66
+ client = Net::HTTP.new(uri.host, uri.port)
67
+ client.use_ssl = (uri.scheme == "https")
68
+ if @timeout
69
+ client.read_timeout = @timeout
70
+ client.open_timeout = @timeout
71
+ end
72
+ client
73
+ end
74
+
75
+ request = case method
76
+ when :get
77
+ Net::HTTP::Get.new(uri)
78
+ when :post
79
+ Net::HTTP::Post.new(uri)
80
+ when :delete
81
+ Net::HTTP::Delete.new(uri)
82
+ when :patch
83
+ Net::HTTP::Patch.new(uri)
84
+ else
85
+ raise ArgumentError, "Unknown method: #{method}"
86
+ end
87
+
88
+ request["Authorization"] = "Bearer #{@api_key}"
89
+ request["Content-Type"] = "application/json"
90
+ request["User-Agent"] = "sidemail-sdk-ruby/#{Sidemail::VERSION}"
91
+
92
+ if [:post, :patch].include?(method) && params
93
+ request.body = params.to_json
94
+ end
95
+
96
+ response = http.request(request)
97
+
98
+ content_type = response["content-type"]
99
+
100
+ if content_type && content_type.include?("application/json")
101
+ body = JSON.parse(response.body)
102
+ else
103
+ body = response.body
104
+ end
105
+
106
+ unless response.is_a?(Net::HTTPSuccess)
107
+ message = if body.is_a?(Hash)
108
+ body["developerMessage"]
109
+ else
110
+ body.to_s.empty? ? response.message : body
111
+ end
112
+ error_code = body.is_a?(Hash) ? body["errorCode"] : nil
113
+ more_info = body.is_a?(Hash) ? body["moreInfo"] : nil
114
+
115
+ raise Sidemail::Error.new(
116
+ message,
117
+ http_status: response.code.to_i,
118
+ error_code: error_code,
119
+ more_info: more_info
120
+ )
121
+ end
122
+
123
+ body
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ class Error < StandardError
5
+ attr_reader :http_status, :error_code, :more_info
6
+
7
+ def initialize(message, http_status: nil, error_code: nil, more_info: nil)
8
+ super(message)
9
+ @http_status = http_status
10
+ @error_code = error_code
11
+ @more_info = more_info
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+
5
+ module Sidemail
6
+ class PaginatedResponse
7
+ include Enumerable
8
+
9
+ attr_reader :has_more, :pagination_cursor_next, :pagination_cursor_prev, :raw
10
+
11
+ def initialize(response_body, client, path, params, method)
12
+ @raw = response_body
13
+ @items = (response_body["data"] || []).map { |item| Resource.new(item) }
14
+ @has_more = response_body["hasMore"]
15
+ @pagination_cursor_next = response_body["paginationCursorNext"]
16
+ @pagination_cursor_prev = response_body["paginationCursorPrev"]
17
+ @client = client
18
+ @path = path
19
+ @params = params
20
+ @method = method
21
+ end
22
+
23
+ def inspect
24
+ @raw.inspect
25
+ end
26
+
27
+ def to_s
28
+ @raw.to_s
29
+ end
30
+
31
+ def [](key)
32
+ @raw[key.to_s]
33
+ end
34
+
35
+ def method_missing(method_name, *args, &block)
36
+ if @raw.key?(method_name.to_s)
37
+ @raw[method_name.to_s]
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def respond_to_missing?(method_name, include_private = false)
44
+ @raw.key?(method_name.to_s) || super
45
+ end
46
+
47
+ def items
48
+ @items
49
+ end
50
+
51
+ def data
52
+ @items
53
+ end
54
+
55
+ def each(&block)
56
+ return to_enum(:each) unless block_given?
57
+
58
+ @items.each do |item|
59
+ yield item
60
+ end
61
+ end
62
+
63
+ def auto_paginate
64
+ return to_enum(:auto_paginate) unless block_given?
65
+
66
+ # Yield current page items
67
+ each { |item| yield item }
68
+
69
+ current_cursor = @pagination_cursor_next
70
+
71
+ while current_cursor
72
+ # Fetch next page
73
+ next_params = @params.dup
74
+ if @method == :get
75
+ next_params[:paginationCursorNext] = current_cursor
76
+ else
77
+ next_params["paginationCursorNext"] = current_cursor
78
+ end
79
+
80
+ response = @client.perform_request(@path, params: next_params, method: @method)
81
+
82
+ # Yield items from the new page
83
+ if response["data"]
84
+ response["data"].each { |item| yield Resource.new(item) }
85
+ end
86
+
87
+ current_cursor = response["paginationCursorNext"]
88
+ break unless response["hasMore"]
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ class Resource
5
+ def initialize(data)
6
+ @data = data
7
+ end
8
+
9
+ def [](key)
10
+ value = @data[key.to_s]
11
+ wrap(value)
12
+ end
13
+
14
+ def to_h
15
+ @data
16
+ end
17
+
18
+ def raw
19
+ @data
20
+ end
21
+
22
+ def inspect
23
+ @data.inspect
24
+ end
25
+
26
+ def to_s
27
+ @data.to_s
28
+ end
29
+
30
+ def method_missing(method_name, *args, &block)
31
+ key = method_name.to_s
32
+ if @data.key?(key)
33
+ wrap(@data[key])
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def respond_to_missing?(method_name, include_private = false)
40
+ @data.key?(method_name.to_s) || super
41
+ end
42
+
43
+ private
44
+
45
+ def wrap(value)
46
+ if value.is_a?(Hash)
47
+ Resource.new(value)
48
+ elsif value.is_a?(Array)
49
+ value.map { |v| wrap(v) }
50
+ else
51
+ value
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ module Resources
5
+ class Contact
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def create_or_update(params)
11
+ raise Sidemail::Error.new("Missing contact data") unless params
12
+ response = @client.perform_request("contacts", params: params, method: :post)
13
+ Resource.new(response)
14
+ end
15
+
16
+ def find(email_address)
17
+ raise Sidemail::Error.new("Missing emailAddress") unless email_address
18
+ response = @client.perform_request("contacts/#{email_address}", method: :get)
19
+ Resource.new(response)
20
+ end
21
+
22
+ def list(params = {})
23
+ response = @client.perform_request("contacts", params: params, method: :get)
24
+ PaginatedResponse.new(response, @client, "contacts", params, :get)
25
+ end
26
+
27
+ def query(params = {})
28
+ list(params)
29
+ end
30
+
31
+ def delete(email_address)
32
+ response = @client.perform_request("contacts/#{email_address}", method: :delete)
33
+ Resource.new(response)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ module Resources
5
+ class Domain
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def list(params = {})
11
+ response = @client.perform_request("domains", params: params, method: :get)
12
+ PaginatedResponse.new(response, @client, "domains", params, :get)
13
+ end
14
+
15
+ def create(params)
16
+ response = @client.perform_request("domains", params: params, method: :post)
17
+ Resource.new(response)
18
+ end
19
+
20
+ def delete(id)
21
+ response = @client.perform_request("domains/#{id}", method: :delete)
22
+ Resource.new(response)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ module Resources
5
+ class Email
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def send(params)
11
+ response = @client.perform_request("email/send", params: params, method: :post)
12
+ Resource.new(response)
13
+ end
14
+
15
+ def search(params = {})
16
+ response = @client.perform_request("email/search", params: params, method: :post)
17
+ PaginatedResponse.new(response, @client, "email/search", params, :post)
18
+ end
19
+
20
+ def get(id)
21
+ response = @client.perform_request("email/#{id}", method: :get)
22
+ Resource.new(response)
23
+ end
24
+
25
+ def delete(id)
26
+ response = @client.perform_request("email/#{id}", method: :delete)
27
+ Resource.new(response)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ module Resources
5
+ class Messenger
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def list(params = {})
11
+ response = @client.perform_request("messenger", params: params, method: :get)
12
+ PaginatedResponse.new(response, @client, "messenger", params, :get)
13
+ end
14
+
15
+ def get(id)
16
+ response = @client.perform_request("messenger/#{id}", method: :get)
17
+ Resource.new(response)
18
+ end
19
+
20
+ def create(params)
21
+ response = @client.perform_request("messenger", params: params, method: :post)
22
+ Resource.new(response)
23
+ end
24
+
25
+ def update(id, params)
26
+ response = @client.perform_request("messenger/#{id}", params: params, method: :patch)
27
+ Resource.new(response)
28
+ end
29
+
30
+ def delete(id)
31
+ response = @client.perform_request("messenger/#{id}", method: :delete)
32
+ Resource.new(response)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ module Resources
5
+ class Project
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def create(params)
11
+ response = @client.perform_request("project", params: params, method: :post)
12
+ Resource.new(response)
13
+ end
14
+
15
+ def get
16
+ response = @client.perform_request("project", method: :get)
17
+ Resource.new(response)
18
+ end
19
+
20
+ def update(params)
21
+ response = @client.perform_request("project", params: params, method: :patch)
22
+ Resource.new(response)
23
+ end
24
+
25
+ def delete
26
+ response = @client.perform_request("project", method: :delete)
27
+ Resource.new(response)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidemail
4
+ VERSION = "0.1.1"
5
+ end
data/lib/sidemail.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sidemail/version"
4
+ require_relative "sidemail/error"
5
+ require_relative "sidemail/client"
6
+
7
+ module Sidemail
8
+ class << self
9
+ attr_accessor :api_key
10
+
11
+ def new(api_key: nil, **options)
12
+ Client.new(api_key: api_key || self.api_key, **options)
13
+ end
14
+
15
+ def file_to_attachment(name, content)
16
+ {
17
+ name: name,
18
+ content: Base64.strict_encode64(content)
19
+ }
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidemail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Sidemail
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-12-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base64
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fiddle
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.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.18'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.18'
83
+ description: Official Sidemail.io Ruby library providing convenient access to the
84
+ Sidemail API.
85
+ email:
86
+ - support@sidemail.io
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - README.md
92
+ - lib/sidemail.rb
93
+ - lib/sidemail/client.rb
94
+ - lib/sidemail/error.rb
95
+ - lib/sidemail/paginated_response.rb
96
+ - lib/sidemail/resource.rb
97
+ - lib/sidemail/resources/contact.rb
98
+ - lib/sidemail/resources/domain.rb
99
+ - lib/sidemail/resources/email.rb
100
+ - lib/sidemail/resources/messenger.rb
101
+ - lib/sidemail/resources/project.rb
102
+ - lib/sidemail/version.rb
103
+ homepage: https://sidemail.io
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 2.6.0
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.4.19
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Official Sidemail.io Ruby SDK
126
+ test_files: []