Skip to content

Commit 5d5b888

Browse files
V0.1.0 (#3)
* v0.1.0 * updates github check ruby version * remove json * adds ffi as dependency * private attributes * remove \n from token * update tests * raise error when invoice is not found * get transaction * validate if return for all queries * validate if return for all queries * parse json on a single method
1 parent 4213f03 commit 5d5b888

File tree

14 files changed

+470
-25
lines changed

14 files changed

+470
-25
lines changed

.github/workflows/check.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
tests:
1414
runs-on: ubuntu-latest
1515
container:
16-
image: ruby:2.7.6
16+
image: ruby:2.6.6
1717
env:
1818
BUNDLE_JOBS: 3
1919
BUNDLE_RETRY: 3

Gemfile.lock

+14-18
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
PATH
22
remote: .
33
specs:
4-
lightspark-client (0.0.1)
4+
lightspark-client (0.1.0)
5+
base64 (~> 0.2)
6+
ffi (~> 1.9.10)
57
typhoeus (~> 1.3)
68

79
GEM
810
remote: https://rubygems.org/
911
specs:
10-
activesupport (7.1.3.4)
11-
base64
12-
bigdecimal
12+
activesupport (6.1.7.8)
1313
concurrent-ruby (~> 1.0, >= 1.0.2)
14-
connection_pool (>= 2.2.5)
15-
drb
1614
i18n (>= 1.6, < 2)
1715
minitest (>= 5.1)
18-
mutex_m
1916
tzinfo (~> 2.0)
17+
zeitwerk (~> 2.3)
2018
addressable (2.8.7)
2119
public_suffix (>= 2.0.2, < 7.0)
2220
ast (2.4.2)
@@ -25,22 +23,19 @@ GEM
2523
byebug (10.0.2)
2624
coderay (1.1.3)
2725
concurrent-ruby (1.3.4)
28-
connection_pool (2.4.1)
2926
crack (1.0.0)
3027
bigdecimal
3128
rexml
3229
diff-lcs (1.5.1)
33-
drb (2.2.1)
34-
ethon (0.16.0)
35-
ffi (>= 1.15.0)
36-
ffi (1.16.3)
30+
ethon (0.12.0)
31+
ffi (>= 1.3.0)
32+
ffi (1.9.25)
3733
hashdiff (1.1.1)
3834
i18n (1.14.5)
3935
concurrent-ruby (~> 1.0)
4036
method_source (1.1.0)
4137
minitest (5.25.1)
42-
mutex_m (0.2.0)
43-
parallel (1.26.3)
38+
parallel (1.24.0)
4439
parser (3.3.4.2)
4540
ast (~> 2.4.1)
4641
racc
@@ -56,7 +51,7 @@ GEM
5651
rainbow (3.1.1)
5752
rake (12.3.3)
5853
regexp_parser (2.9.2)
59-
rexml (3.3.5)
54+
rexml (3.3.6)
6055
strscan
6156
rspec (3.13.0)
6257
rspec-core (~> 3.13.0)
@@ -80,8 +75,8 @@ GEM
8075
rubocop-ast (>= 0.6.0)
8176
ruby-progressbar (~> 1.7)
8277
unicode-display_width (>= 1.4.0, < 2.0)
83-
rubocop-ast (1.32.1)
84-
parser (>= 3.3.1.0)
78+
rubocop-ast (1.30.0)
79+
parser (>= 3.2.1.0)
8580
rubocop-github (0.17.0)
8681
rubocop
8782
rubocop-performance
@@ -107,6 +102,7 @@ GEM
107102
addressable (>= 2.8.0)
108103
crack (>= 0.3.2)
109104
hashdiff (>= 0.4.0, < 2.0.0)
105+
zeitwerk (2.6.17)
110106

111107
PLATFORMS
112108
ruby
@@ -124,4 +120,4 @@ DEPENDENCIES
124120
webmock (~> 3.5)
125121

126122
BUNDLED WITH
127-
2.1.4
123+
1.17.3

lib/lightspark-client.rb

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# frozen_string_literal: true
22

33
# Dependencies
4+
require "base64"
5+
require "json"
6+
require "typhoeus"
47

58
# Source
69
require "lightspark-client/version"
10+
require "lightspark-client/errors/client_error"
11+
require "lightspark-client/mutations/invoices"
12+
require "lightspark-client/queries/invoices"
13+
require "lightspark-client/queries/transactions"
14+
require "lightspark-client/client"

lib/lightspark-client/client.rb

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# frozen_string_literal: true
2+
3+
module LightsparkClient
4+
class Client
5+
include LightsparkClient::Mutations::Invoices
6+
include LightsparkClient::Queries::Invoices
7+
include LightsparkClient::Queries::Transactions
8+
9+
DEFAULT_API_URL = "https://api.lightspark.com/graphql/server/2023-09-13"
10+
DEFAULT_ERROR_MESSAGE = "An error occurred while processing the request"
11+
LIGHTSPARK_ERRORS = {
12+
"400" => "You may have malformed your GraphQL request (for example, forgot to include the query field in the payload)",
13+
"401" => "The token/token_id pair is not valid and we cannot authenticate your account in the request.",
14+
"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.",
15+
"403" => "Your account might be on hold because of suspicious activity.",
16+
"429" => "Your account sent too many requests in a short period of time and was rate limited.",
17+
"5xx" => "The server is experiencing a problem. Please try again later."
18+
}
19+
LOGGER_TAG = "LightsparkClient"
20+
21+
def initialize(client_id:, client_secret:, api_url: DEFAULT_API_URL, logger: nil)
22+
@client_id = client_id
23+
@client_secret = client_secret
24+
@api_url = api_url
25+
@logger = logger
26+
end
27+
28+
private
29+
30+
def request(payload)
31+
log("Requesting Lightspark API")
32+
log("Payload: #{payload}")
33+
34+
response = Typhoeus.post(
35+
@api_url,
36+
body: payload.to_json,
37+
headers: request_headers
38+
)
39+
40+
handle_response(response)
41+
end
42+
43+
def request_headers
44+
token = Base64.encode64("#{@client_id}:#{@client_secret}").gsub("\n", "")
45+
46+
{
47+
"Content-Type" => "application/json",
48+
"Authorization" => "Basic #{token}",
49+
}
50+
end
51+
52+
def log(message, level = :info)
53+
return unless @logger && looger.respond_to?(:tagged) && @logger.respond_to?(level)
54+
55+
@logger.send(:tagged, LOGGER_TAG) { @logger.send(level, message) }
56+
end
57+
58+
def handle_response(response)
59+
handle_status(response)
60+
61+
response_body = parse_response_body(response)
62+
63+
handle_errors(response_body)
64+
65+
log("Request to Lightspark API was successful")
66+
67+
response_body["data"]
68+
end
69+
70+
def handle_status(response)
71+
return response if response.success?
72+
73+
status = response.code
74+
status = "5xx" if status >= 500
75+
76+
message = LIGHTSPARK_ERRORS[status.to_s]
77+
message ||= DEFAULT_ERROR_MESSAGE
78+
79+
log("Request failed with status: #{response.code}. Message: #{message}", :error)
80+
81+
raise LightsparkClient::Errors::ClientError, message
82+
end
83+
84+
def handle_errors(body)
85+
return if body["errors"].nil? || body["errors"].empty?
86+
87+
message = body["errors"].map { |error| error["message"] }.join(", ")
88+
89+
log("Request failed with errors: #{message}", :error)
90+
91+
raise LightsparkClient::Errors::ClientError, message
92+
end
93+
94+
def parse_response_body(response)
95+
JSON.parse(response.body)
96+
rescue StandardError
97+
raise LightsparkClient::Errors::ClientError, "An error occurred while parsing the response"
98+
end
99+
end
100+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
module LightsparkClient
4+
module Errors
5+
class ClientError < StandardError; end
6+
end
7+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
module LightsparkClient
4+
module Mutations
5+
module Invoices
6+
def create_invoice(node_id:, amount_msats:, memo: nil, type: nil, expiry_secs: nil)
7+
mutation = <<~GQL
8+
mutation CreateInvoice(
9+
$node_id: ID!
10+
$amount_msats: Long!
11+
$memo: String
12+
$type: InvoiceType = null
13+
$expiry_secs: Int = null
14+
) {
15+
create_invoice(
16+
input: {
17+
node_id: $node_id,
18+
amount_msats: $amount_msats,
19+
memo: $memo,
20+
invoice_type: $type,
21+
expiry_secs: $expiry_secs
22+
}
23+
) {
24+
invoice {
25+
id
26+
data {
27+
encoded_payment_request
28+
payment_hash
29+
}
30+
}
31+
}
32+
}
33+
GQL
34+
35+
variables = {
36+
node_id: node_id,
37+
amount_msats: amount_msats,
38+
memo: memo,
39+
type: type,
40+
expiry_secs: expiry_secs
41+
}.compact
42+
43+
response = request({ query: mutation, variables: variables })
44+
45+
raise LightsparkClient::Errors::ClientError, "Invoice not created" if response.dig("create_invoice", "invoice").nil? || response.dig("create_invoice", "invoice").empty?
46+
47+
response.dig("create_invoice", "invoice")
48+
end
49+
end
50+
end
51+
end
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# frozen_string_literal: true
2+
3+
module LightsparkClient
4+
module Queries
5+
module Invoices
6+
def get_invoice(id: nil)
7+
query = <<~GQL
8+
query GetPaymentRequest($id: ID!) {
9+
entity(id: $id) {
10+
... on PaymentRequest {
11+
...PaymentRequestFragment
12+
}
13+
}
14+
}
15+
16+
fragment PaymentRequestFragment on PaymentRequest {
17+
__typename
18+
... on Invoice {
19+
__typename
20+
invoice_id: id
21+
invoice_created_at: created_at
22+
invoice_updated_at: updated_at
23+
invoice_data: data {
24+
__typename
25+
invoice_data_encoded_payment_request: encoded_payment_request
26+
invoice_data_bitcoin_network: bitcoin_network
27+
invoice_data_payment_hash: payment_hash
28+
invoice_data_amount: amount {
29+
__typename
30+
currency_amount_original_value: original_value
31+
currency_amount_original_unit: original_unit
32+
currency_amount_preferred_currency_unit: preferred_currency_unit
33+
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
34+
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
35+
}
36+
invoice_data_created_at: created_at
37+
invoice_data_expires_at: expires_at
38+
invoice_data_memo: memo
39+
}
40+
invoice_status: status
41+
invoice_amount_paid: amount_paid {
42+
__typename
43+
currency_amount_original_value: original_value
44+
currency_amount_original_unit: original_unit
45+
currency_amount_preferred_currency_unit: preferred_currency_unit
46+
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
47+
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
48+
}
49+
invoice_is_uma: is_uma
50+
invoice_is_lnurl: is_lnurl
51+
}
52+
}
53+
GQL
54+
55+
variables = { id: id }.compact
56+
57+
response = request({ query: query, variables: variables })
58+
59+
raise LightsparkClient::Errors::ClientError, "Invoice not found" if response["entity"].nil? || response["entity"].empty?
60+
61+
response["entity"]
62+
end
63+
64+
# this method is deprecated, used only for compatibility
65+
def get_transfer(wallet_id, id: nil)
66+
get_invoice(id: id)
67+
end
68+
end
69+
end
70+
end

0 commit comments

Comments
 (0)