Class: Arafa::Data::AfricasTalking

Inherits:
Object
  • Object
show all
Extended by:
Dry::Initializer
Defined in:
lib/arafa/data/africas_talking.rb

Overview

Africa's Talking Mobile Data adapter (§1.12), AT-only — Wasiliana has no equivalent. Doesn't extend Airtime::Base/Providers::Base: each recipient carries its own quantity/unit/validity/metadata rather than one shape applied uniformly, so there's nothing to share. Still follows the same ctor/contract/HTTP/Result conventions, reusing Providers::Base's Faraday connection.

Constant Summary collapse

CONTRACT =
Contracts::MobileDataContract
RECIPIENTS_TYPE =

Symbolizes recipient hash keys and normalizes phone_number (local/international -> 254...) up front, so #build_payload and the contract both see a consistent shape.

Types::Array.of(Types::Hash).constructor do |value|
  Array(value).map do |recipient|
    normalized = recipient.each_with_object({}) { |(k, v), acc| acc[k.to_sym] = v }
    phone = normalized[:phone_number]
    normalized[:phone_number] = Arafa::PhoneNumber.normalize(phone) if phone
    normalized
  end
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.connectionObject

Reuses Providers::Base's connection (retry, JSON, apiKey-redacting logger) rather than building a second one.



53
54
55
# File 'lib/arafa/data/africas_talking.rb', line 53

def connection
  Providers::Base.connection
end

.send(*args, **kwargs) ⇒ Object



45
46
47
48
49
# File 'lib/arafa/data/africas_talking.rb', line 45

def send(*args, **kwargs)
  return super if args.any? || kwargs.empty?

  new(**kwargs).send
end

Instance Method Details

#build_payloadObject



87
88
89
# File 'lib/arafa/data/africas_talking.rb', line 87

def build_payload
  { username: username, productName: product_name, recipients: recipients.map { |r| build_recipient_payload(r) } }
end

#connectionObject



73
74
75
# File 'lib/arafa/data/africas_talking.rb', line 73

def connection
  self.class.connection
end

#endpointObject



77
78
79
# File 'lib/arafa/data/africas_talking.rb', line 77

def endpoint
  "https://bundles.africastalking.com/mobile/data/request"
end

#headersObject



81
82
83
84
85
# File 'lib/arafa/data/africas_talking.rb', line 81

def headers
  base = { "apiKey" => Arafa.config.africas_talking.api_key,
           "Content-Type" => "application/json", "Accept" => "application/json" }
  idempotency_key.nil? ? base : base.merge("Idempotency-Key" => idempotency_key)
end

#parse_response(response) ⇒ Object



91
92
93
94
95
96
97
98
99
# File 'lib/arafa/data/africas_talking.rb', line 91

def parse_response(response)
  return build_failure(response) unless response.status.between?(200, 299)

  body = response.body
  entries = Array(body["entries"])
  return Failure(Arafa::InvalidRequestError.new(body["errorMessage"] || body.to_s)) if entries.empty?

  build_success(body, entries)
end

#send(*args, **kwargs) ⇒ Object

Contract → HTTP call → response parsing. Returns a Dry::Monads::Result: Success(SendResult) or Failure(Arafa::Error subclass).



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/arafa/data/africas_talking.rb', line 61

def send(*args, **kwargs)
  return super if args.any? || kwargs.any?

  validation = validate_contract
  return validation if validation.failure?

  response = connection.post(endpoint, build_payload, headers)
  parse_response(response)
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
  Failure(Arafa::NetworkError.new(e.message))
end