Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V0.1.0 #3

Merged
merged 12 commits into from
Aug 22, 2024
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
tests:
runs-on: ubuntu-latest
container:
image: ruby:2.7.6
image: ruby:2.6.6
env:
BUNDLE_JOBS: 3
BUNDLE_RETRY: 3
Expand Down
32 changes: 14 additions & 18 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
PATH
remote: .
specs:
lightspark-client (0.0.1)
lightspark-client (0.1.0)
base64 (~> 0.2)
ffi (~> 1.9.10)
typhoeus (~> 1.3)

GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.4)
base64
bigdecimal
activesupport (6.1.7.8)
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.2)
Expand All @@ -25,22 +23,19 @@ GEM
byebug (10.0.2)
coderay (1.1.3)
concurrent-ruby (1.3.4)
connection_pool (2.4.1)
crack (1.0.0)
bigdecimal
rexml
diff-lcs (1.5.1)
drb (2.2.1)
ethon (0.16.0)
ffi (>= 1.15.0)
ffi (1.16.3)
ethon (0.12.0)
ffi (>= 1.3.0)
ffi (1.9.25)
hashdiff (1.1.1)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
method_source (1.1.0)
minitest (5.25.1)
mutex_m (0.2.0)
parallel (1.26.3)
parallel (1.24.0)
parser (3.3.4.2)
ast (~> 2.4.1)
racc
Expand All @@ -56,7 +51,7 @@ GEM
rainbow (3.1.1)
rake (12.3.3)
regexp_parser (2.9.2)
rexml (3.3.5)
rexml (3.3.6)
strscan
rspec (3.13.0)
rspec-core (~> 3.13.0)
Expand All @@ -80,8 +75,8 @@ GEM
rubocop-ast (>= 0.6.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (1.32.1)
parser (>= 3.3.1.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-github (0.17.0)
rubocop
rubocop-performance
Expand All @@ -107,6 +102,7 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
zeitwerk (2.6.17)

PLATFORMS
ruby
Expand All @@ -124,4 +120,4 @@ DEPENDENCIES
webmock (~> 3.5)

BUNDLED WITH
2.1.4
1.17.3
8 changes: 8 additions & 0 deletions lib/lightspark-client.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# frozen_string_literal: true

# Dependencies
require "base64"
require "json"
require "typhoeus"

# Source
require "lightspark-client/version"
require "lightspark-client/errors/client_error"
require "lightspark-client/mutations/invoices"
require "lightspark-client/queries/invoices"
require "lightspark-client/queries/transactions"
require "lightspark-client/client"
100 changes: 100 additions & 0 deletions lib/lightspark-client/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

module LightsparkClient
class Client
include LightsparkClient::Mutations::Invoices
include LightsparkClient::Queries::Invoices
include LightsparkClient::Queries::Transactions

DEFAULT_API_URL = "https://api.lightspark.com/graphql/server/2023-09-13"
DEFAULT_ERROR_MESSAGE = "An error occurred while processing the request"
LIGHTSPARK_ERRORS = {
"400" => "You may have malformed your GraphQL request (for example, forgot to include the query field in the payload)",
"401" => "The token/token_id pair is not valid and we cannot authenticate your account in the request.",
"402" => "Your account might be on hold because of billing issues, or you are trying to use a feature that is not in your plan.",
"403" => "Your account might be on hold because of suspicious activity.",
"429" => "Your account sent too many requests in a short period of time and was rate limited.",
"5xx" => "The server is experiencing a problem. Please try again later."
}
LOGGER_TAG = "LightsparkClient"

def initialize(client_id:, client_secret:, api_url: DEFAULT_API_URL, logger: nil)
@client_id = client_id
@client_secret = client_secret
@api_url = api_url
@logger = logger
end

private

def request(payload)
log("Requesting Lightspark API")
log("Payload: #{payload}")

response = Typhoeus.post(
@api_url,
body: payload.to_json,
headers: request_headers
)

handle_response(response)
end

def request_headers
token = Base64.encode64("#{@client_id}:#{@client_secret}").gsub("\n", "")

{
"Content-Type" => "application/json",
"Authorization" => "Basic #{token}",
}
end

def log(message, level = :info)
return unless @logger && looger.respond_to?(:tagged) && @logger.respond_to?(level)

@logger.send(:tagged, LOGGER_TAG) { @logger.send(level, message) }
end

def handle_response(response)
handle_status(response)

response_body = parse_response_body(response)

handle_errors(response_body)

log("Request to Lightspark API was successful")

response_body["data"]
end

def handle_status(response)
return response if response.success?

status = response.code
status = "5xx" if status >= 500

message = LIGHTSPARK_ERRORS[status.to_s]
message ||= DEFAULT_ERROR_MESSAGE

log("Request failed with status: #{response.code}. Message: #{message}", :error)

raise LightsparkClient::Errors::ClientError, message
end

def handle_errors(body)
return if body["errors"].nil? || body["errors"].empty?

message = body["errors"].map { |error| error["message"] }.join(", ")

log("Request failed with errors: #{message}", :error)

raise LightsparkClient::Errors::ClientError, message
end

def parse_response_body(response)
JSON.parse(response.body)
rescue StandardError
raise LightsparkClient::Errors::ClientError, "An error occurred while parsing the response"
end
end
end
7 changes: 7 additions & 0 deletions lib/lightspark-client/errors/client_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module LightsparkClient
module Errors
class ClientError < StandardError; end
end
end
51 changes: 51 additions & 0 deletions lib/lightspark-client/mutations/invoices.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module LightsparkClient
module Mutations
module Invoices
def create_invoice(node_id:, amount_msats:, memo: nil, type: nil, expiry_secs: nil)
mutation = <<~GQL
mutation CreateInvoice(
$node_id: ID!
$amount_msats: Long!
$memo: String
$type: InvoiceType = null
$expiry_secs: Int = null
) {
create_invoice(
input: {
node_id: $node_id,
amount_msats: $amount_msats,
memo: $memo,
invoice_type: $type,
expiry_secs: $expiry_secs
}
) {
invoice {
id
data {
encoded_payment_request
payment_hash
}
}
}
}
GQL

variables = {
node_id: node_id,
amount_msats: amount_msats,
memo: memo,
type: type,
expiry_secs: expiry_secs
}.compact

response = request({ query: mutation, variables: variables })

raise LightsparkClient::Errors::ClientError, "Invoice not created" if response.dig("create_invoice", "invoice").nil? || response.dig("create_invoice", "invoice").empty?

response.dig("create_invoice", "invoice")
end
end
end
end
70 changes: 70 additions & 0 deletions lib/lightspark-client/queries/invoices.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

module LightsparkClient
module Queries
module Invoices
def get_invoice(id: nil)
query = <<~GQL
query GetPaymentRequest($id: ID!) {
entity(id: $id) {
... on PaymentRequest {
...PaymentRequestFragment
}
}
}

fragment PaymentRequestFragment on PaymentRequest {
__typename
... on Invoice {
__typename
invoice_id: id
invoice_created_at: created_at
invoice_updated_at: updated_at
invoice_data: data {
__typename
invoice_data_encoded_payment_request: encoded_payment_request
invoice_data_bitcoin_network: bitcoin_network
invoice_data_payment_hash: payment_hash
invoice_data_amount: amount {
__typename
currency_amount_original_value: original_value
currency_amount_original_unit: original_unit
currency_amount_preferred_currency_unit: preferred_currency_unit
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
}
invoice_data_created_at: created_at
invoice_data_expires_at: expires_at
invoice_data_memo: memo
}
invoice_status: status
invoice_amount_paid: amount_paid {
__typename
currency_amount_original_value: original_value
currency_amount_original_unit: original_unit
currency_amount_preferred_currency_unit: preferred_currency_unit
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
}
invoice_is_uma: is_uma
invoice_is_lnurl: is_lnurl
}
}
GQL

variables = { id: id }.compact

response = request({ query: query, variables: variables })

raise LightsparkClient::Errors::ClientError, "Invoice not found" if response["entity"].nil? || response["entity"].empty?

response["entity"]
end

# this method is deprecated, used only for compatibility
def get_transfer(wallet_id, id: nil)
get_invoice(id: id)
end
end
end
end
Loading
Loading