keplars 2.1.0 → 2.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cf4cf268e788b45d71a89a6fdeebfc472a8a051e4ef361fc50704445829d784
4
- data.tar.gz: a91eb6cc0fd9769f9cc72c79877ea08f60a4711994a4b65bc3ea163e8dbb8a82
3
+ metadata.gz: 3c9f27c62fc254b42974354edc4e138d253a05b0367eb1276f9999125810e7e9
4
+ data.tar.gz: 1cc364a65c48a1676b3d0fc82a511cf671efe84aae50127b7017907917ef1bbd
5
5
  SHA512:
6
- metadata.gz: 43bc8dd44d4000e4faae494f29963016fa10851ae2a70ae71febad694b8fe31bba334af131644103fe8d36e6902ef721cdcc9f65e9755764ba69c8923bb59d71
7
- data.tar.gz: 0af7402b88f4a3bbcd13c253174c509d5bf3e5b433256b7851c50a8c9e4e75ef1fb91d3f66fd84e1d2e01fae2f187d7cfcf9df3d5e292237324a70f7b37d3dd1
6
+ metadata.gz: 2bb04146d158a84e30a28220d06c6ab4308c37aabc4391cd8e9ad3871198e40ea71a92254ef827b1c466237613eee519884217fbfc6f61b8a412aeb5411ddaa9
7
+ data.tar.gz: 9fd4acb85ed648d3bf8babd7a5f2d345599bcfa9db8091fb1da3e710cd5377e52c7f804d3708468475414de7e45de28e03d440d3fb509b9d08b9d8389518142c
data/README.md CHANGED
@@ -46,6 +46,26 @@ puts result[:data][:job_id]
46
46
  | Regular | `kms_<id>.live_<secret>` | Email sending |
47
47
  | Admin | `kms_<id>.adm_<secret>` | Contacts, audiences, automations, domains |
48
48
 
49
+ ### Using Both Send and Admin Operations
50
+
51
+ Pass `admin_key:` alongside a live `api_key:` to use a single client for both:
52
+
53
+ ```ruby
54
+ client = Keplars::Client.new(
55
+ api_key: 'kms_<id>.live_<secret>',
56
+ admin_key: 'kms_<id>.adm_<secret>'
57
+ )
58
+
59
+ client.emails.send_instant(...) # uses api_key
60
+ client.audiences.list # uses admin_key
61
+ ```
62
+
63
+ If you only have an admin key, pass it as `api_key:` directly:
64
+
65
+ ```ruby
66
+ client = Keplars::Client.new(api_key: 'kms_<id>.adm_<secret>')
67
+ ```
68
+
49
69
  ## Email Sending
50
70
 
51
71
  ### Priority Levels
@@ -4,24 +4,26 @@ module Keplars
4
4
  def create(name:, description: nil)
5
5
  body = { name: name }
6
6
  body[:description] = description if description
7
- @client.request('POST', '/api/v1/public/audiences/add-audience', body: body)[:data]
7
+ @client.request('POST', '/api/v1/public/audiences/add-audience', body: body, is_admin: true)[:data]
8
8
  end
9
9
 
10
- def list(page: nil, limit: nil)
10
+ def list(page: nil, limit: nil, sort_by: nil, sort_dir: nil)
11
11
  params = {}
12
12
  params[:page] = page if page
13
13
  params[:limit] = limit if limit
14
+ params[:sortBy] = sort_by if sort_by
15
+ params[:sortDir] = sort_dir if sort_dir
14
16
 
15
17
  query = @client.send(:build_query_string, params)
16
- @client.request('GET', "/api/v1/public/audiences/get-audiences#{query}")[:data]
18
+ @client.request('GET', "/api/v1/public/audiences/get-audiences#{query}", is_admin: true)[:data]
17
19
  end
18
20
 
19
21
  def get(id)
20
- @client.request('GET', "/api/v1/public/audiences/get-audience?id=#{URI.encode_www_form_component(id)}")[:data]
22
+ @client.request('GET', "/api/v1/public/audiences/get-audience?id=#{URI.encode_www_form_component(id)}", is_admin: true)[:data]
21
23
  end
22
24
 
23
25
  def delete(id)
24
- @client.request('DELETE', "/api/v1/public/audiences/delete-audience?id=#{URI.encode_www_form_component(id)}")[:data]
26
+ @client.request('DELETE', "/api/v1/public/audiences/delete-audience?id=#{URI.encode_www_form_component(id)}", is_admin: true)[:data]
25
27
  end
26
28
  end
27
29
  end
@@ -7,19 +7,27 @@ module Keplars
7
7
  params[:limit] = limit if limit
8
8
 
9
9
  query = @client.send(:build_query_string, params)
10
- @client.request('GET', "/api/v1/public/automations/get-all#{query}")[:data]
10
+ @client.request('GET', "/api/v1/public/automations/get-all#{query}", is_admin: true)[:data]
11
11
  end
12
12
 
13
13
  def get(id)
14
- @client.request('GET', "/api/v1/public/automations/get-automation/#{id}")[:data]
14
+ @client.request('GET', "/api/v1/public/automations/get-automation/#{id}", is_admin: true)[:data]
15
15
  end
16
16
 
17
- def enroll(id, email:)
18
- @client.request('POST', "/api/v1/public/automations/add-automation/#{id}/enroll", body: { email: email })[:data]
17
+ def enroll(id, email:, properties: nil)
18
+ body = { email: email }
19
+ body[:properties] = properties if properties
20
+ @client.request('POST', "/api/v1/public/automations/add-automation/#{id}/enroll", body: body, is_admin: true)[:data]
19
21
  end
20
22
 
21
23
  def unenroll(id, email:)
22
- @client.request('DELETE', "/api/v1/public/automations/delete-automation/#{id}/subscribers", body: { email: email })[:data]
24
+ @client.request('DELETE', "/api/v1/public/automations/delete-automation/#{id}/subscribers", body: { email: email }, is_admin: true)[:data]
25
+ end
26
+
27
+ def track_event(email, event_name, event_properties: nil)
28
+ body = { email: email, event_name: event_name }
29
+ body[:event_properties] = event_properties if event_properties
30
+ @client.request('POST', '/api/v1/public/automations/events/track', body: body, is_admin: true)[:data]
23
31
  end
24
32
  end
25
33
  end
@@ -11,7 +11,7 @@ module Keplars
11
11
 
12
12
  attr_reader :emails, :contacts, :audiences, :automations, :domains
13
13
 
14
- def initialize(api_key: nil, base_url: nil, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES, retry_delay: DEFAULT_RETRY_DELAY)
14
+ def initialize(api_key: nil, admin_key: nil, base_url: nil, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES, retry_delay: DEFAULT_RETRY_DELAY)
15
15
  @api_key = api_key || ENV['KEPLARS_API_KEY']
16
16
  raise ArgumentError, 'API key is required. Set KEPLARS_API_KEY or pass api_key parameter' if @api_key.nil? || @api_key.empty?
17
17
 
@@ -19,6 +19,11 @@ module Keplars
19
19
  raise ArgumentError, 'Invalid API key format. Expected: kms_<id>.live_<secret> or kms_<id>.adm_<secret>'
20
20
  end
21
21
 
22
+ if admin_key
23
+ raise ArgumentError, 'Invalid adminKey format. Expected: kms_<id>.adm_<secret>' unless admin_key.match?(/\Akms_[a-f0-9]+\.adm_[a-f0-9]+\z/)
24
+ @admin_key = admin_key
25
+ end
26
+
22
27
  resolved_base_url = base_url || ENV['KEPLARS_BASE_URL'] || DEFAULT_BASE_URL
23
28
  @base_url = resolved_base_url.sub(/\/$/, '')
24
29
  @timeout = timeout
@@ -32,7 +37,7 @@ module Keplars
32
37
  @domains = Resources::Domains.new(self)
33
38
  end
34
39
 
35
- def request(method, path, body: nil, retry_count: 0)
40
+ def request(method, path, body: nil, retry_count: 0, is_admin: false)
36
41
  uri = URI("#{@base_url}#{path}")
37
42
 
38
43
  http = Net::HTTP.new(uri.host, uri.port)
@@ -50,7 +55,7 @@ module Keplars
50
55
  end
51
56
 
52
57
  request = request_class.new(uri.request_uri)
53
- request['Authorization'] = "Bearer #{@api_key}"
58
+ request['Authorization'] = "Bearer #{get_auth_key(is_admin)}"
54
59
  request['Content-Type'] = 'application/json'
55
60
  request['User-Agent'] = "keplars-ruby/#{VERSION}"
56
61
 
@@ -66,11 +71,13 @@ module Keplars
66
71
 
67
72
  result = response.body && !response.body.empty? ? JSON.parse(response.body, symbolize_names: true) : nil
68
73
  { data: result, rate_limit_info: rate_limit_info }
74
+ rescue KeplarsError
75
+ raise
69
76
  rescue StandardError => e
70
77
  if retryable_error?(e) && retry_count < @max_retries
71
78
  delay = calculate_backoff(retry_count)
72
79
  sleep(delay)
73
- request(method, path, body: body, retry_count: retry_count + 1)
80
+ request(method, path, body: body, retry_count: retry_count + 1, is_admin: is_admin)
74
81
  else
75
82
  raise NetworkError.new("Request failed: #{e.message}", original_error: e)
76
83
  end
@@ -79,6 +86,14 @@ module Keplars
79
86
 
80
87
  private
81
88
 
89
+ def get_auth_key(is_admin)
90
+ return @api_key unless is_admin
91
+ return @admin_key if @admin_key
92
+ return @api_key if @api_key.match?(/\Akms_[a-f0-9]+\.adm_[a-f0-9]+\z/)
93
+
94
+ raise AuthorizationError.new('Admin key required for this operation. Provide admin_key in config.', request_id: nil)
95
+ end
96
+
82
97
  def extract_rate_limit_info(response)
83
98
  limit = response['X-RateLimit-Limit']
84
99
  remaining = response['X-RateLimit-Remaining']
@@ -2,29 +2,45 @@ module Keplars
2
2
  module Resources
3
3
  class Contacts < Base
4
4
  def add(**params)
5
- @client.request('POST', '/api/v1/public/contacts/add-contact', body: params.compact)[:data]
5
+ @client.request('POST', '/api/v1/public/contacts/add-contact', body: params.compact, is_admin: true)[:data]
6
6
  end
7
7
 
8
- def get(email)
9
- @client.request('GET', "/api/v1/public/contacts/get-contact?email=#{URI.encode_www_form_component(email)}")[:data]
8
+ def get(email_or_id)
9
+ param = email_or_id.include?('@') ? 'email' : 'id'
10
+ @client.request('GET', "/api/v1/public/contacts/get-contact?#{param}=#{URI.encode_www_form_component(email_or_id)}", is_admin: true)[:data]
10
11
  end
11
12
 
12
- def list(audience_id: nil, page: nil, limit: nil)
13
+ def list(audience_id: nil, page: nil, limit: nil, sort_by: nil, sort_dir: nil, subscribed_only: nil)
13
14
  params = {}
14
- params[:audience_id] = audience_id if audience_id
15
+ params[:audienceId] = audience_id if audience_id
15
16
  params[:page] = page if page
16
17
  params[:limit] = limit if limit
18
+ params[:sortBy] = sort_by if sort_by
19
+ params[:sortDir] = sort_dir if sort_dir
20
+ params[:subscribedOnly] = subscribed_only.to_s if !subscribed_only.nil?
17
21
 
18
22
  query = @client.send(:build_query_string, params)
19
- @client.request('GET', "/api/v1/public/contacts/get-contacts#{query}")[:data]
23
+ @client.request('GET', "/api/v1/public/contacts/get-contacts#{query}", is_admin: true)[:data]
20
24
  end
21
25
 
22
- def update(email, **params)
23
- @client.request('PATCH', "/api/v1/public/contacts/update-contact?email=#{URI.encode_www_form_component(email)}", body: params.compact)[:data]
26
+ def update(id_or_email, **params)
27
+ if id_or_email.include?('@')
28
+ warn '[Keplars] Deprecated: passing email to update() is deprecated since v2.7.0. Use contact ID instead.'
29
+ param = 'email'
30
+ else
31
+ param = 'id'
32
+ end
33
+ @client.request('PATCH', "/api/v1/public/contacts/update-contact?#{param}=#{URI.encode_www_form_component(id_or_email)}", body: params.compact, is_admin: true)[:data]
24
34
  end
25
35
 
26
- def delete(email)
27
- @client.request('DELETE', "/api/v1/public/contacts/delete-contact?email=#{URI.encode_www_form_component(email)}")[:data]
36
+ def delete(id_or_email)
37
+ if id_or_email.include?('@')
38
+ warn '[Keplars] Deprecated: passing email to delete() is deprecated since v2.7.0. Use contact ID instead.'
39
+ param = 'email'
40
+ else
41
+ param = 'id'
42
+ end
43
+ @client.request('DELETE', "/api/v1/public/contacts/delete-contact?#{param}=#{URI.encode_www_form_component(id_or_email)}", is_admin: true)[:data]
28
44
  end
29
45
  end
30
46
  end
@@ -1,28 +1,35 @@
1
1
  module Keplars
2
2
  module Resources
3
3
  class Domains < Base
4
- def add(domain)
5
- @client.request('POST', '/api/v1/public/domains/add-domain', body: { domain: domain })[:data]
4
+ def add(domain, region: nil)
5
+ body = { domain: domain }
6
+ body[:region] = region if region
7
+ @client.request('POST', '/api/v1/public/domains/add-domain', body: body, is_admin: true)[:data]
6
8
  end
7
9
 
8
10
  def list
9
- @client.request('GET', '/api/v1/public/domains/get-domains')[:data]
11
+ @client.request('GET', '/api/v1/public/domains/get-domains', is_admin: true)[:data]
10
12
  end
11
13
 
12
14
  def get_status(domain_id)
13
- @client.request('GET', "/api/v1/public/domains/domain-status/#{domain_id}")[:data]
15
+ @client.request('GET', "/api/v1/public/domains/domain-status/#{domain_id}", is_admin: true)[:data]
14
16
  end
15
17
 
16
18
  def verify(domain_id)
17
- @client.request('POST', "/api/v1/public/domains/verify-domain/#{domain_id}")[:data]
19
+ @client.request('POST', "/api/v1/public/domains/verify-domain/#{domain_id}", is_admin: true)[:data]
18
20
  end
19
21
 
20
22
  def delete(domain_id)
21
- @client.request('DELETE', "/api/v1/public/domains/delete-domain/#{domain_id}")[:data]
23
+ @client.request('DELETE', "/api/v1/public/domains/delete-domain/#{domain_id}", is_admin: true)[:data]
22
24
  end
23
25
 
24
26
  def create_api_key(**params)
25
- @client.request('POST', '/api/v1/public/domains/api-keys/create', body: params.compact)[:data]
27
+ body = params.dup
28
+ if body.key?(:domain_id) && !body.key?(:custom_domain_id)
29
+ warn '[Keplars] Deprecated: :domain_id is deprecated since v2.7.0. Use :custom_domain_id instead.'
30
+ body[:custom_domain_id] = body.delete(:domain_id)
31
+ end
32
+ @client.request('POST', '/api/v1/public/domains/api-keys/create', body: body.compact, is_admin: true)[:data]
26
33
  end
27
34
  end
28
35
  end
@@ -8,22 +8,22 @@ module Keplars
8
8
 
9
9
  class Emails < Base
10
10
  def send_instant(**args)
11
- body = args.compact
11
+ body = normalize_email_args(args)
12
12
  @client.request('POST', '/api/v1/send-email/instant', body: body)[:data]
13
13
  end
14
14
 
15
15
  def send_high(**args)
16
- body = args.compact
16
+ body = normalize_email_args(args)
17
17
  @client.request('POST', '/api/v1/send-email/high', body: body)[:data]
18
18
  end
19
19
 
20
20
  def send_async(**args)
21
- body = args.compact
21
+ body = normalize_email_args(args)
22
22
  @client.request('POST', '/api/v1/send-email/async', body: body)[:data]
23
23
  end
24
24
 
25
25
  def send_bulk(**args)
26
- body = args.compact
26
+ body = normalize_email_args(args)
27
27
  @client.request('POST', '/api/v1/send-email/bulk', body: body)[:data]
28
28
  end
29
29
 
@@ -32,9 +32,56 @@ module Keplars
32
32
  end
33
33
 
34
34
  def schedule(**args)
35
- body = args.compact
36
- body[:priority] ||= 'async'
37
- @client.request('POST', '/api/v1/send-email/schedule', body: body)[:data]
35
+ body = args.dup
36
+
37
+ if body.key?(:scheduled_for) && !body.key?(:scheduled_at)
38
+ warn '[Keplars] Deprecated: :scheduled_for is deprecated since v2.7.0. Use :scheduled_at instead.'
39
+ body[:scheduled_at] = body.delete(:scheduled_for)
40
+ end
41
+
42
+ scheduled_at = body.delete(:scheduled_at)
43
+ timezone = body.delete(:timezone)
44
+ body.delete(:priority)
45
+
46
+ raise ArgumentError, 'scheduled_at is required for scheduling emails' unless scheduled_at
47
+
48
+ email_body = normalize_email_args(body).compact
49
+
50
+ payload = { scheduled_at: scheduled_at, email: email_body }
51
+ payload[:timezone] = timezone if timezone
52
+
53
+ @client.request('POST', '/api/v1/send-email/schedule', body: payload)[:data]
54
+ end
55
+
56
+ private
57
+
58
+ def normalize_email_args(args)
59
+ body = args.dup
60
+
61
+ if body.key?(:to) && !body[:to].is_a?(Array)
62
+ raise ArgumentError, 'to must be an array of email addresses, e.g. to: ["user@example.com"]'
63
+ end
64
+
65
+ if body.key?(:html)
66
+ warn '[Keplars] Deprecated: :html is deprecated since v2.7.0. Use :body + is_html: true instead.'
67
+ body[:body] ||= body.delete(:html)
68
+ body[:is_html] = true unless body.key?(:is_html)
69
+ body.delete(:html)
70
+ end
71
+
72
+ if body.key?(:text)
73
+ warn '[Keplars] Deprecated: :text is deprecated since v2.7.0. Use :body instead.'
74
+ body[:body] ||= body.delete(:text)
75
+ body.delete(:text)
76
+ end
77
+
78
+ if body.key?(:template_data)
79
+ warn '[Keplars] Deprecated: :template_data is deprecated since v2.7.0. Use :params instead.'
80
+ body[:params] ||= body.delete(:template_data)
81
+ body.delete(:template_data)
82
+ end
83
+
84
+ body.compact
38
85
  end
39
86
  end
40
87
  end
@@ -1,3 +1,3 @@
1
1
  module Keplars
2
- VERSION = "2.1.0"
2
+ VERSION = "2.9.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keplars
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keplars
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-20 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday