ksef-rb 0.1.0 → 0.1.3

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: eaa5f61dd0f2d3e8f852cb504016268c73b798da5fe1e896a821115ea01fd2a5
4
- data.tar.gz: 23df18df8297859a7fdf10fe0546dc03dc49e43b7617236de86837f7d2dbbe43
3
+ metadata.gz: 7f0036e4b7913536223a5b778902fa2bd69f334494b76772df2cf74dc7a2219d
4
+ data.tar.gz: 3bf943a76ccb8504b13475b7c0783b5571e46191a94e2f999253d5ebe54fa9d1
5
5
  SHA512:
6
- metadata.gz: a15c7b19048d06f3328c5e4cb4cbc0a85ce3d7acf4daea6af2c24ec9dc3aee09c83c391071949665fa2a9b81d6f9ac2e9a8285a9e0b582f6e8c45c1d3d764422
7
- data.tar.gz: f2b4956065c21bd7ac5dde726ca129664aa04e30efea9c7ffc746cc32900b0d76d523202f63aa74c1a03105108c58b627e0d96abff8e2c54d208f3d41d90fd5b
6
+ metadata.gz: 7c99c25e305544ff19f78829e1aa696711b8b19d8439c29f5577b03a4e4e6a0ebe81e225452f094ccc75edb1556234a6654970d5c5136090a4d7fed0986c038f
7
+ data.tar.gz: 642c4bedf0387bc6591b2132f457324b08f413127cba53dca6360d5317b68457fd93622b6fbc6107e0a9f4178ef17e26f16eb066b15aa16a8a45c5eeae9b96f8
data/CHANGELOG.md CHANGED
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.3] - 2026-07-13
11
+
12
+ ### Fixed
13
+ - Retry middleware: `Faraday::RetriableResponse` was missing from
14
+ `Connection::RETRY_OPTIONS[:exceptions]`. Because that key overrides the
15
+ faraday-retry default (which includes it), its absence silently disabled the
16
+ `retry_statuses: [502, 503, 504]` retries **and** leaked the raw synthetic
17
+ `Faraday::RetriableResponse` up the stack instead of mapping it to a typed
18
+ `Ksef::ServerError`. Observed in production as an unretried gateway blip on
19
+ `POST /auth/challenge` surfacing as `Faraday::RetriableResponse` rather than
20
+ being absorbed. The class is now included, so 502/503/504 are retried up to
21
+ `max` and, if still failing, raised as `Ksef::ServerError`. Added regression
22
+ specs covering both retry-then-recover and retry-then-exhaust paths.
23
+
24
+ ## [0.1.2] - 2026-05-19
25
+
26
+ ### Fixed
27
+ - `/security/public-key-certificates` returns a DER-encoded X.509 certificate,
28
+ not a bare SPKI. The previous `Internal::TokenEncryptor.normalize_public_key`
29
+ fed the DER straight into `OpenSSL::PKey::RSA.new`, which raised
30
+ `OpenSSL::PKey::PKeyError: Neither PUB key nor PRIV key` against the live
31
+ production endpoint. The normaliser now parses the bytes as an X.509
32
+ certificate and extracts the public key, falling back to the bare-key code
33
+ path for backwards compatibility. The spec-helper fixture was updated to
34
+ return a real self-signed X.509 cert (matching the real KSeF response shape)
35
+ so the entire session lifecycle exercises this path.
36
+ - `Ksef::InvoiceHeader` now `require`s `"time"` explicitly. It calls
37
+ `Time.iso8601` to parse `acquisitionDate` / `permanentStorageDate`; the spec
38
+ suite happened to load `time` transitively via `webmock`, but a bare
39
+ consumer that only required `"ksef"` got `NoMethodError: undefined method
40
+ 'iso8601' for class Time` the first time they pulled invoice metadata.
41
+
42
+ ## [0.1.1] - 2026-05-19
43
+
44
+ ### Added
45
+ - `lib/ksef-rb.rb` shim so `gem "ksef-rb"` works in any Gemfile without
46
+ the `require: "ksef"` option. Consumers who already use the explicit
47
+ `require:` form keep working unchanged.
48
+
10
49
  ## [0.1.0] - 2025-05-18
11
50
 
12
51
  ### Added
data/README.md CHANGED
@@ -15,7 +15,7 @@ but additions are expected as more KSeF features land.
15
15
  ## Installation
16
16
 
17
17
  ```ruby
18
- gem "ksef-rb", require: "ksef"
18
+ gem "ksef-rb"
19
19
  ```
20
20
 
21
21
  ## Quick start
@@ -37,7 +37,8 @@ client.sessions.with_interactive do |_session|
37
37
  headers = client.invoices.query(
38
38
  subject_type: :recipient,
39
39
  date_from: Time.now.utc - (7 * 24 * 3600),
40
- date_to: Time.now.utc
40
+ date_to: Time.now.utc,
41
+ date_type: :issue # :permanent_storage (default), :invoicing, or :issue
41
42
  )
42
43
 
43
44
  headers.each do |h|
@@ -49,6 +50,28 @@ client.sessions.with_interactive do |_session|
49
50
  end
50
51
  ```
51
52
 
53
+ `#query` returns at most `page_size` headers per call (default 100, max 250).
54
+ Paginate by bumping `page_offset` until a short page comes back:
55
+
56
+ ```ruby
57
+ all = []
58
+ offset = 0
59
+ loop do
60
+ page = client.invoices.query(
61
+ subject_type: :recipient,
62
+ date_from: Date.new(2026, 4, 1),
63
+ date_to: Date.new(2026, 5, 31),
64
+ date_type: :issue,
65
+ page_size: 250,
66
+ page_offset: offset
67
+ )
68
+ all.concat(page)
69
+ break if page.size < 250
70
+
71
+ offset += 1
72
+ end
73
+ ```
74
+
52
75
  `Ksef::InvoiceHeader` exposes (among others):
53
76
  `ksef_reference_number`, `invoice_number`, `issuer_nip`, `issuer_name`,
54
77
  `recipient_nip`, `recipient_name`, `issued_on`, `gross_amount`, `net_amount`,
@@ -18,10 +18,17 @@ module Ksef
18
18
  backoff_factor: 2,
19
19
  retry_statuses: [502, 503, 504],
20
20
  methods: %i[get head post delete put patch],
21
+ # `Faraday::RetriableResponse` MUST stay in this list: faraday-retry
22
+ # raises it internally to trigger a `retry_statuses` retry. Because we
23
+ # override `exceptions` (rather than inheriting the middleware default,
24
+ # which includes it), omitting it silently disables 502/503/504 retries
25
+ # *and* leaks the raw synthetic exception past `check!` instead of
26
+ # mapping it to a typed `Ksef::ServerError`.
21
27
  exceptions: [
22
28
  Errno::ETIMEDOUT,
23
29
  Faraday::TimeoutError,
24
- Faraday::ConnectionFailed
30
+ Faraday::ConnectionFailed,
31
+ Faraday::RetriableResponse
25
32
  ]
26
33
  }.freeze
27
34
 
@@ -134,7 +141,7 @@ module Ksef
134
141
  end
135
142
 
136
143
  def parse_retry_after(value)
137
- return nil if value.nil?
144
+ return if value.nil?
138
145
 
139
146
  Integer(value)
140
147
  rescue ArgumentError, TypeError
@@ -34,13 +34,20 @@ module Ksef
34
34
  Base64.strict_encode64(ciphertext)
35
35
  end
36
36
 
37
- # Convenience wrapper that handles raw DER-encoded (base64) keys returned
38
- # by the public-key endpoint, falling back to PEM if the input already
39
- # contains BEGIN markers.
37
+ # Normalizes whatever `/security/public-key-certificates` hands us into a
38
+ # PEM-encoded RSA public key suitable for {.encrypt}.
39
+ #
40
+ # KSeF returns the `certificate` field as a base64-encoded DER X.509
41
+ # certificate (not a bare public key), so we parse it as a cert first
42
+ # and extract the SPKI. PEM-armored inputs and bare-key DER blobs are
43
+ # also accepted as a courtesy.
40
44
  def normalize_public_key(raw)
41
- return raw if raw.to_s.include?("BEGIN")
45
+ text = raw.to_s
46
+ return text if text.include?("BEGIN")
42
47
 
43
- der = Base64.decode64(raw.to_s)
48
+ der = Base64.decode64(text)
49
+ OpenSSL::X509::Certificate.new(der).public_key.to_pem
50
+ rescue OpenSSL::X509::CertificateError
44
51
  OpenSSL::PKey::RSA.new(der).to_pem
45
52
  end
46
53
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "date"
4
+ require "time"
4
5
 
5
6
  module Ksef
6
7
  # Lightweight value object describing an invoice as returned by
@@ -56,7 +57,7 @@ module Ksef
56
57
  private
57
58
 
58
59
  def parse_date(value)
59
- return nil if value.nil? || value.empty?
60
+ return if value.nil? || value.empty?
60
61
 
61
62
  Date.iso8601(value)
62
63
  rescue ArgumentError
@@ -64,7 +65,7 @@ module Ksef
64
65
  end
65
66
 
66
67
  def parse_time(value)
67
- return nil if value.nil? || value.empty?
68
+ return if value.nil? || value.empty?
68
69
 
69
70
  Time.iso8601(value)
70
71
  rescue ArgumentError
data/lib/ksef/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ksef
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/ksef-rb.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Bundler's default for `gem "ksef-rb"` is `require "ksef-rb"`. This shim
4
+ # makes that resolve to the canonical entry point without forcing every
5
+ # consumer to add `, require: "ksef"`.
6
+ require_relative "ksef"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ksef-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michał Siwek
@@ -91,6 +91,7 @@ files:
91
91
  - CHANGELOG.md
92
92
  - LICENSE.txt
93
93
  - README.md
94
+ - lib/ksef-rb.rb
94
95
  - lib/ksef.rb
95
96
  - lib/ksef/client.rb
96
97
  - lib/ksef/configuration.rb