emailit 2.0.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +597 -0
  4. data/lib/emailit/api_resource.rb +22 -0
  5. data/lib/emailit/api_response.rb +18 -0
  6. data/lib/emailit/base_client.rb +115 -0
  7. data/lib/emailit/collection.rb +25 -0
  8. data/lib/emailit/delivery_method.rb +74 -0
  9. data/lib/emailit/emailit_client.rb +53 -0
  10. data/lib/emailit/emailit_object.rb +57 -0
  11. data/lib/emailit/errors.rb +36 -0
  12. data/lib/emailit/events/webhook_event.rb +215 -0
  13. data/lib/emailit/railtie.rb +13 -0
  14. data/lib/emailit/resources/api_key.rb +7 -0
  15. data/lib/emailit/resources/audience.rb +7 -0
  16. data/lib/emailit/resources/contact.rb +7 -0
  17. data/lib/emailit/resources/domain.rb +7 -0
  18. data/lib/emailit/resources/email.rb +7 -0
  19. data/lib/emailit/resources/email_verification.rb +7 -0
  20. data/lib/emailit/resources/email_verification_list.rb +7 -0
  21. data/lib/emailit/resources/event.rb +7 -0
  22. data/lib/emailit/resources/subscriber.rb +7 -0
  23. data/lib/emailit/resources/suppression.rb +7 -0
  24. data/lib/emailit/resources/template.rb +7 -0
  25. data/lib/emailit/resources/webhook.rb +7 -0
  26. data/lib/emailit/services/api_key_service.rb +27 -0
  27. data/lib/emailit/services/audience_service.rb +27 -0
  28. data/lib/emailit/services/base_service.rb +50 -0
  29. data/lib/emailit/services/contact_service.rb +27 -0
  30. data/lib/emailit/services/domain_service.rb +31 -0
  31. data/lib/emailit/services/email_service.rb +47 -0
  32. data/lib/emailit/services/email_verification_list_service.rb +27 -0
  33. data/lib/emailit/services/email_verification_service.rb +11 -0
  34. data/lib/emailit/services/event_service.rb +15 -0
  35. data/lib/emailit/services/subscriber_service.rb +27 -0
  36. data/lib/emailit/services/suppression_service.rb +27 -0
  37. data/lib/emailit/services/template_service.rb +31 -0
  38. data/lib/emailit/services/webhook_service.rb +27 -0
  39. data/lib/emailit/util.rb +35 -0
  40. data/lib/emailit/version.rb +5 -0
  41. data/lib/emailit/webhook_signature.rb +59 -0
  42. data/lib/emailit.rb +66 -0
  43. metadata +144 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class ApiKeyService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/api-keys", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/api-keys/%s", id))
12
+ end
13
+
14
+ def list(params = {})
15
+ request_collection(:get, "/v2/api-keys", params.empty? ? nil : params)
16
+ end
17
+
18
+ def update(id, params)
19
+ request(:post, build_path("/v2/api-keys/%s", id), params)
20
+ end
21
+
22
+ def delete(id)
23
+ request(:delete, build_path("/v2/api-keys/%s", id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class AudienceService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/audiences", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/audiences/%s", id))
12
+ end
13
+
14
+ def update(id, params)
15
+ request(:post, build_path("/v2/audiences/%s", id), params)
16
+ end
17
+
18
+ def list(params = {})
19
+ request_collection(:get, "/v2/audiences", params.empty? ? nil : params)
20
+ end
21
+
22
+ def delete(id)
23
+ request(:delete, build_path("/v2/audiences/%s", id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+
5
+ module Emailit
6
+ module Services
7
+ class BaseService
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ private
13
+
14
+ def request(method, path, params = nil)
15
+ response = @client.request(method, path, params)
16
+ obj = Util.convert_to_emailit_object(response.json)
17
+
18
+ if obj
19
+ obj.last_response = response
20
+ else
21
+ obj = EmailitObject.new
22
+ end
23
+
24
+ obj
25
+ end
26
+
27
+ def request_collection(method, path, params = nil)
28
+ response = @client.request(method, path, params)
29
+ obj = Util.convert_to_emailit_object(response.json)
30
+
31
+ if obj.is_a?(Collection)
32
+ obj.last_response = response
33
+ return obj
34
+ end
35
+
36
+ collection = Collection.new(response.json || {})
37
+ collection.last_response = response
38
+ collection
39
+ end
40
+
41
+ def request_raw(method, path, params = nil)
42
+ @client.request(method, path, params)
43
+ end
44
+
45
+ def build_path(pattern, *args)
46
+ format(pattern, *args.map { |a| CGI.escape(a.to_s) })
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class ContactService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/contacts", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/contacts/%s", id))
12
+ end
13
+
14
+ def update(id, params)
15
+ request(:post, build_path("/v2/contacts/%s", id), params)
16
+ end
17
+
18
+ def list(params = {})
19
+ request_collection(:get, "/v2/contacts", params.empty? ? nil : params)
20
+ end
21
+
22
+ def delete(id)
23
+ request(:delete, build_path("/v2/contacts/%s", id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class DomainService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/domains", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/domains/%s", id))
12
+ end
13
+
14
+ def verify(id)
15
+ request(:post, build_path("/v2/domains/%s/verify", id))
16
+ end
17
+
18
+ def update(id, params)
19
+ request(:patch, build_path("/v2/domains/%s", id), params)
20
+ end
21
+
22
+ def list(params = {})
23
+ request_collection(:get, "/v2/domains", params.empty? ? nil : params)
24
+ end
25
+
26
+ def delete(id)
27
+ request(:delete, build_path("/v2/domains/%s", id))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class EmailService < BaseService
6
+ def send(params)
7
+ request(:post, "/v2/emails", params)
8
+ end
9
+
10
+ def list(params = {})
11
+ request_collection(:get, "/v2/emails", params.empty? ? nil : params)
12
+ end
13
+
14
+ def get(id)
15
+ request(:get, build_path("/v2/emails/%s", id))
16
+ end
17
+
18
+ def get_raw(id)
19
+ request(:get, build_path("/v2/emails/%s/raw", id))
20
+ end
21
+
22
+ def get_attachments(id)
23
+ request_collection(:get, build_path("/v2/emails/%s/attachments", id))
24
+ end
25
+
26
+ def get_body(id)
27
+ request(:get, build_path("/v2/emails/%s/body", id))
28
+ end
29
+
30
+ def get_meta(id)
31
+ request(:get, build_path("/v2/emails/%s/meta", id))
32
+ end
33
+
34
+ def update(id, params)
35
+ request(:post, build_path("/v2/emails/%s", id), params)
36
+ end
37
+
38
+ def cancel(id)
39
+ request(:post, build_path("/v2/emails/%s/cancel", id))
40
+ end
41
+
42
+ def retry(id)
43
+ request(:post, build_path("/v2/emails/%s/retry", id))
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class EmailVerificationListService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/email-verification-lists", params)
8
+ end
9
+
10
+ def list(params = {})
11
+ request_collection(:get, "/v2/email-verification-lists", params.empty? ? nil : params)
12
+ end
13
+
14
+ def get(id)
15
+ request(:get, build_path("/v2/email-verification-lists/%s", id))
16
+ end
17
+
18
+ def results(id, params = {})
19
+ request_collection(:get, build_path("/v2/email-verification-lists/%s/results", id), params.empty? ? nil : params)
20
+ end
21
+
22
+ def export(id)
23
+ request_raw(:get, build_path("/v2/email-verification-lists/%s/export", id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class EmailVerificationService < BaseService
6
+ def verify(params)
7
+ request(:post, "/v2/email-verifications", params)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class EventService < BaseService
6
+ def list(params = {})
7
+ request_collection(:get, "/v2/events", params.empty? ? nil : params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/events/%s", id))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class SubscriberService < BaseService
6
+ def create(audience_id, params)
7
+ request(:post, build_path("/v2/audiences/%s/subscribers", audience_id), params)
8
+ end
9
+
10
+ def get(audience_id, subscriber_id)
11
+ request(:get, build_path("/v2/audiences/%s/subscribers/%s", audience_id, subscriber_id))
12
+ end
13
+
14
+ def update(audience_id, subscriber_id, params)
15
+ request(:post, build_path("/v2/audiences/%s/subscribers/%s", audience_id, subscriber_id), params)
16
+ end
17
+
18
+ def list(audience_id, params = {})
19
+ request_collection(:get, build_path("/v2/audiences/%s/subscribers", audience_id), params.empty? ? nil : params)
20
+ end
21
+
22
+ def delete(audience_id, subscriber_id)
23
+ request(:delete, build_path("/v2/audiences/%s/subscribers/%s", audience_id, subscriber_id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class SuppressionService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/suppressions", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/suppressions/%s", id))
12
+ end
13
+
14
+ def update(id, params)
15
+ request(:post, build_path("/v2/suppressions/%s", id), params)
16
+ end
17
+
18
+ def list(params = {})
19
+ request_collection(:get, "/v2/suppressions", params.empty? ? nil : params)
20
+ end
21
+
22
+ def delete(id)
23
+ request(:delete, build_path("/v2/suppressions/%s", id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class TemplateService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/templates", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/templates/%s", id))
12
+ end
13
+
14
+ def update(id, params)
15
+ request(:post, build_path("/v2/templates/%s", id), params)
16
+ end
17
+
18
+ def list(params = {})
19
+ request_collection(:get, "/v2/templates", params.empty? ? nil : params)
20
+ end
21
+
22
+ def delete(id)
23
+ request(:delete, build_path("/v2/templates/%s", id))
24
+ end
25
+
26
+ def publish(id)
27
+ request(:post, build_path("/v2/templates/%s/publish", id))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Services
5
+ class WebhookService < BaseService
6
+ def create(params)
7
+ request(:post, "/v2/webhooks", params)
8
+ end
9
+
10
+ def get(id)
11
+ request(:get, build_path("/v2/webhooks/%s", id))
12
+ end
13
+
14
+ def update(id, params)
15
+ request(:post, build_path("/v2/webhooks/%s", id), params)
16
+ end
17
+
18
+ def list(params = {})
19
+ request_collection(:get, "/v2/webhooks", params.empty? ? nil : params)
20
+ end
21
+
22
+ def delete(id)
23
+ request(:delete, build_path("/v2/webhooks/%s", id))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ module Util
5
+ OBJECT_TYPES = {}.freeze
6
+
7
+ def self.register_object_type(name, klass)
8
+ @object_types ||= {}
9
+ @object_types[name] = klass
10
+ end
11
+
12
+ def self.object_types
13
+ @object_types || {}
14
+ end
15
+
16
+ def self.convert_to_emailit_object(data)
17
+ return nil if data.nil?
18
+
19
+ if data.is_a?(Hash) && data["data"].is_a?(Array)
20
+ data["data"] = data["data"].map { |item| convert_to_emailit_object(item) }
21
+ collection = Collection.new(data)
22
+ return collection
23
+ end
24
+
25
+ object_type = data["object"] if data.is_a?(Hash)
26
+ mapping = object_types
27
+
28
+ if object_type && mapping[object_type]
29
+ mapping[object_type].new(data)
30
+ else
31
+ EmailitObject.new(data)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emailit
4
+ VERSION = "2.0.1"
5
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module Emailit
6
+ class WebhookSignature
7
+ HEADER_SIGNATURE = "x-emailit-signature"
8
+ HEADER_TIMESTAMP = "x-emailit-timestamp"
9
+ DEFAULT_TOLERANCE = 300
10
+
11
+ def self.verify(raw_body, signature, timestamp, secret, tolerance: DEFAULT_TOLERANCE)
12
+ if tolerance
13
+ age = Time.now.to_i - timestamp.to_i
14
+ if age > tolerance
15
+ raise ApiError.new(
16
+ "Webhook timestamp is too old. The request may be a replay attack.",
17
+ http_status: 401
18
+ )
19
+ end
20
+ end
21
+
22
+ computed = compute_signature(raw_body, timestamp, secret)
23
+
24
+ unless secure_compare(computed, signature)
25
+ raise ApiError.new(
26
+ "Webhook signature verification failed.",
27
+ http_status: 401
28
+ )
29
+ end
30
+
31
+ payload = begin
32
+ JSON.parse(raw_body)
33
+ rescue JSON::ParserError
34
+ raise ApiError.new(
35
+ "Invalid webhook payload: unable to decode JSON.",
36
+ http_status: 400
37
+ )
38
+ end
39
+
40
+ Events::WebhookEvent.construct_from(payload)
41
+ end
42
+
43
+ def self.compute_signature(raw_body, timestamp, secret)
44
+ signed_payload = "#{timestamp}.#{raw_body}"
45
+ OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload)
46
+ end
47
+
48
+ def self.secure_compare(a, b)
49
+ return false unless a.bytesize == b.bytesize
50
+
51
+ l = a.unpack("C*")
52
+ r = b.unpack("C*")
53
+ result = 0
54
+ l.zip(r) { |x, y| result |= x ^ y }
55
+ result.zero?
56
+ end
57
+ private_class_method :secure_compare
58
+ end
59
+ end
data/lib/emailit.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "cgi"
5
+
6
+ require_relative "emailit/version"
7
+ require_relative "emailit/errors"
8
+ require_relative "emailit/api_response"
9
+ require_relative "emailit/emailit_object"
10
+ require_relative "emailit/api_resource"
11
+ require_relative "emailit/collection"
12
+ require_relative "emailit/util"
13
+
14
+ require_relative "emailit/resources/email"
15
+ require_relative "emailit/resources/domain"
16
+ require_relative "emailit/resources/api_key"
17
+ require_relative "emailit/resources/audience"
18
+ require_relative "emailit/resources/subscriber"
19
+ require_relative "emailit/resources/template"
20
+ require_relative "emailit/resources/suppression"
21
+ require_relative "emailit/resources/email_verification"
22
+ require_relative "emailit/resources/email_verification_list"
23
+ require_relative "emailit/resources/webhook"
24
+ require_relative "emailit/resources/contact"
25
+ require_relative "emailit/resources/event"
26
+
27
+ require_relative "emailit/services/base_service"
28
+ require_relative "emailit/services/email_service"
29
+ require_relative "emailit/services/domain_service"
30
+ require_relative "emailit/services/api_key_service"
31
+ require_relative "emailit/services/audience_service"
32
+ require_relative "emailit/services/subscriber_service"
33
+ require_relative "emailit/services/template_service"
34
+ require_relative "emailit/services/suppression_service"
35
+ require_relative "emailit/services/email_verification_service"
36
+ require_relative "emailit/services/email_verification_list_service"
37
+ require_relative "emailit/services/webhook_service"
38
+ require_relative "emailit/services/contact_service"
39
+ require_relative "emailit/services/event_service"
40
+
41
+ require_relative "emailit/events/webhook_event"
42
+
43
+ require_relative "emailit/webhook_signature"
44
+ require_relative "emailit/base_client"
45
+ require_relative "emailit/emailit_client"
46
+
47
+ module Emailit
48
+ def self.client(api_key)
49
+ EmailitClient.new(api_key)
50
+ end
51
+
52
+ Util.register_object_type(Email::OBJECT_NAME, Email)
53
+ Util.register_object_type(Domain::OBJECT_NAME, Domain)
54
+ Util.register_object_type(ApiKey::OBJECT_NAME, ApiKey)
55
+ Util.register_object_type(Audience::OBJECT_NAME, Audience)
56
+ Util.register_object_type(Subscriber::OBJECT_NAME, Subscriber)
57
+ Util.register_object_type(Template::OBJECT_NAME, Template)
58
+ Util.register_object_type(Suppression::OBJECT_NAME, Suppression)
59
+ Util.register_object_type(EmailVerification::OBJECT_NAME, EmailVerification)
60
+ Util.register_object_type(EmailVerificationList::OBJECT_NAME, EmailVerificationList)
61
+ Util.register_object_type(Webhook::OBJECT_NAME, Webhook)
62
+ Util.register_object_type(Contact::OBJECT_NAME, Contact)
63
+ Util.register_object_type(Event::OBJECT_NAME, Event)
64
+ end
65
+
66
+ require_relative "emailit/railtie" if defined?(Rails::Railtie)