cin7_core_api 0.2.0 → 0.2.1
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 +4 -4
- data/CHANGELOG.md +5 -2
- data/README.md +19 -3
- data/lib/cin7_core_api/connection.rb +32 -6
- data/lib/cin7_core_api/redactor.rb +10 -4
- data/lib/cin7_core_api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 237cda18aeb79904a6fed79dc9deaa53240aff6d45f659d0163f14188af06126
|
|
4
|
+
data.tar.gz: 3ba7316e959ec28916abb55e3036e0550d68ce626be3c0ac08fc80eda8a8bfb8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: caff2995e25a658cd0aaf75513389850a0347a8aa4fc0f38ee3b99015a2e02a9bccd758edecedb4187f06fbd2360bb25ae6ce944a10b1a7fc6fc454f1c02910c
|
|
7
|
+
data.tar.gz: 932d8ea1d610710377946799c1224e099b3e4d6b5cf6b99d5ac32b01e28d7a07cb7bdd563291246430126f01e07f295dd8a1d70d235708ea8a8ed2f527d0b563
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.2.1] - 2026-10-02
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Keep decompression failures and malformed UTF-8 responses, including invalid decoded JSON strings, inside the sanitized error contract, preserving uncertain-write recovery without retries.
|
|
6
|
+
- Preserve callback identity when another webhook subscription has credentials matching this subscription's URL components; continue filtering secrets throughout free text.
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-09-29
|
|
6
9
|
|
|
7
10
|
- Add explicit sale update/undo, order, invoice, payment, sale manual-journal, standalone journal, tax-rule and webhook operations while preserving existing read methods.
|
|
8
11
|
- Send JSON payloads without rounding amounts or FX rates; distinguish parent sale IDs, document task IDs, payment IDs and webhook IDs.
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Version `0.2.0` adds explicit payment, order, invoice, journal, and webhook oper
|
|
|
9
9
|
Add the gem to your Gemfile:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
gem "cin7_core_api",
|
|
12
|
+
gem "cin7_core_api", "0.2.1"
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Then run:
|
|
@@ -20,7 +20,7 @@ bundle install
|
|
|
20
20
|
|
|
21
21
|
Ruby 3.3 or newer is required.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Version `0.2.1` adds response-decoding safety and callback-identity fixes. Published `0.2.0` remains unchanged. Check RubyGems for publication availability; to evaluate a reviewed source before publication, pin its full commit SHA from this repository.
|
|
24
24
|
|
|
25
25
|
## Authentication
|
|
26
26
|
|
|
@@ -253,7 +253,23 @@ mise exec -- bundle exec standardrb --cache false $(git ls-files '*.rb' '*.gemsp
|
|
|
253
253
|
mise exec -- bundle exec rake build
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
-
Stage intended new source/spec files before using the tracked-file lint command. CI tests Ruby 3.3 and 3.4. Specs use Faraday's test adapter and do not verify live accounting behavior. Inspect the built gem's file list before release: the gemspec packages only `lib/**/*.rb`, README, changelog and license. Preserve untracked research files.
|
|
256
|
+
Stage intended new source/spec files before using the tracked-file lint command. CI tests Ruby 3.3 and 3.4. Specs use Faraday's test adapter and do not verify live accounting behavior. Inspect the built gem's file list before release: the gemspec packages only `lib/**/*.rb`, README, changelog and license. Preserve untracked research files.
|
|
257
|
+
|
|
258
|
+
## Releasing
|
|
259
|
+
|
|
260
|
+
Publishing a GitHub release triggers `.github/workflows/release.yml`. It requires the tagged commit to be part of `main`, verifies the tag matches `v<VERSION>`, runs tests and lint, builds the gem, and publishes that exact version to RubyGems. Draft releases and ordinary branch pushes do not publish gems.
|
|
261
|
+
|
|
262
|
+
One-time setup: in the `cin7_core_api` gem's RubyGems **Trusted publishers** settings, register repository owner `PostCo`, repository `cin7_core_api`, workflow filename `release.yml`, and environment `release`. Use the same `release` environment in GitHub. [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) uses GitHub OIDC, so no long-lived API key or interactive MFA code is needed in CI. Signing in locally does not configure this trust relationship.
|
|
263
|
+
|
|
264
|
+
For each new version:
|
|
265
|
+
|
|
266
|
+
1. Update `lib/cin7_core_api/version.rb` and `CHANGELOG.md`, review the PR, and merge it into `main`.
|
|
267
|
+
2. Create and publish a GitHub release at that merged commit, with a tag such as `v0.2.1` matching the gem version.
|
|
268
|
+
3. Check the **Publish gem** workflow and verify the version is available on RubyGems before updating application dependencies.
|
|
269
|
+
|
|
270
|
+
The manually published `0.2.0` already exists. Do not republish it; this workflow is for subsequent versions. A failed attempt to publish an existing version does not replace that version. If a workflow is rerun after a partial success, check RubyGems first.
|
|
271
|
+
|
|
272
|
+
The manual fallback is `bundle exec rake release` from a clean, merged checkout. Tests and Standard run before Bundler's tag/push/publish steps. The main-branch ancestry guard belongs to the GitHub workflow; the manual command does not enforce it. Manual publishing still uses your RubyGems credentials and applicable MFA.
|
|
257
273
|
|
|
258
274
|
## CIN7 references
|
|
259
275
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
4
|
require "json"
|
|
5
|
+
require "zlib"
|
|
5
6
|
|
|
6
7
|
module Cin7CoreAPI
|
|
7
8
|
class Connection
|
|
@@ -61,12 +62,10 @@ module Cin7CoreAPI
|
|
|
61
62
|
context = {request_method: method, request_path: redactor.filter(path.split("?").first)}
|
|
62
63
|
write = method != :get
|
|
63
64
|
|
|
64
|
-
raw_response =
|
|
65
|
-
request.params.update(params)
|
|
66
|
-
end
|
|
65
|
+
raw_response = dispatch(method, path, body, params, context, write: write)
|
|
67
66
|
parsed_body, valid_json = parse_body(raw_response.body)
|
|
68
67
|
values = {"headers" => normalized_headers(raw_response.headers), "body" => parsed_body}
|
|
69
|
-
sanitized = redactor.with_sensitive_values(values).filter(values)
|
|
68
|
+
sanitized = redactor.with_sensitive_values(values).filter(values, callback_redactor: redactor)
|
|
70
69
|
response = Response.new(status: raw_response.status, headers: sanitized.fetch("headers"), body: sanitized.fetch("body"))
|
|
71
70
|
|
|
72
71
|
raise_for_status!(response, context, write: write)
|
|
@@ -75,7 +74,13 @@ module Cin7CoreAPI
|
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
response
|
|
78
|
-
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def dispatch(method, path, body, params, context, write:)
|
|
80
|
+
@http.run_request(method, path, body, nil) do |request|
|
|
81
|
+
request.params.update(params)
|
|
82
|
+
end
|
|
83
|
+
rescue Faraday::Error, Zlib::Error => error
|
|
79
84
|
# Faraday exceptions can retain the entire authenticated request in their cause.
|
|
80
85
|
raise TransportError.new("CIN7 Core transport failed (#{error.class})", ambiguous: write, **context), cause: nil
|
|
81
86
|
end
|
|
@@ -87,11 +92,32 @@ module Cin7CoreAPI
|
|
|
87
92
|
def parse_body(body)
|
|
88
93
|
return [nil, true] if body.nil? || body.empty?
|
|
89
94
|
|
|
90
|
-
|
|
95
|
+
# JSON is UTF-8 even when the adapter labels its bytes as binary. Reject
|
|
96
|
+
# malformed success bodies, but keep rejection details safe to redact.
|
|
97
|
+
body = body.dup.force_encoding(Encoding::UTF_8)
|
|
98
|
+
return [body.scrub, false] unless body.valid_encoding?
|
|
99
|
+
|
|
100
|
+
parsed = JSON.parse(body)
|
|
101
|
+
# Some JSON parsers accept lone low-surrogate escapes and produce invalid
|
|
102
|
+
# strings. Do not expose or regex-filter partially decoded credentials.
|
|
103
|
+
valid_utf8?(parsed) ? [parsed, true] : [Redactor::FILTERED, false]
|
|
91
104
|
rescue JSON::ParserError
|
|
92
105
|
[body, false]
|
|
93
106
|
end
|
|
94
107
|
|
|
108
|
+
def valid_utf8?(value)
|
|
109
|
+
case value
|
|
110
|
+
when Hash
|
|
111
|
+
value.all? { |key, entry| valid_utf8?(key) && valid_utf8?(entry) }
|
|
112
|
+
when Array
|
|
113
|
+
value.all? { |entry| valid_utf8?(entry) }
|
|
114
|
+
when String
|
|
115
|
+
value.valid_encoding?
|
|
116
|
+
else
|
|
117
|
+
true
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
95
121
|
def raise_for_status!(response, context, write:)
|
|
96
122
|
return if response.success?
|
|
97
123
|
|
|
@@ -18,7 +18,7 @@ module Cin7CoreAPI
|
|
|
18
18
|
self.class.new(@secrets + sensitive_values(value))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def filter(value)
|
|
21
|
+
def filter(value, callback_redactor: self)
|
|
22
22
|
case value
|
|
23
23
|
when Hash
|
|
24
24
|
value.each_with_object({}) do |(key, entry), result|
|
|
@@ -26,16 +26,18 @@ module Cin7CoreAPI
|
|
|
26
26
|
result[key.to_s] = if sensitive_key?(key) || external_headers?(key)
|
|
27
27
|
FILTERED
|
|
28
28
|
elsif callback_url?(key) && entry.is_a?(String)
|
|
29
|
-
|
|
29
|
+
# Another subscription's credentials may equal this callback's
|
|
30
|
+
# integration ID or host label. Scope URL filtering to this record.
|
|
31
|
+
callback_redactor.with_sensitive_values(value).filter_url(entry)
|
|
30
32
|
elsif structured_identifier?(key, entry)
|
|
31
33
|
# Short header values must not corrupt identifiers or schema enums.
|
|
32
34
|
@secrets.include?(entry) ? FILTERED : entry.dup
|
|
33
35
|
else
|
|
34
|
-
filter(entry)
|
|
36
|
+
filter(entry, callback_redactor: callback_redactor)
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
when Array
|
|
38
|
-
value.map { |entry| filter(entry) }
|
|
40
|
+
value.map { |entry| filter(entry, callback_redactor: callback_redactor) }
|
|
39
41
|
when String
|
|
40
42
|
filter_string(value)
|
|
41
43
|
else
|
|
@@ -126,6 +128,8 @@ module Cin7CoreAPI
|
|
|
126
128
|
[]
|
|
127
129
|
end
|
|
128
130
|
|
|
131
|
+
protected
|
|
132
|
+
|
|
129
133
|
def filter_url(value)
|
|
130
134
|
return FILTERED if @secrets.include?(value)
|
|
131
135
|
|
|
@@ -145,6 +149,8 @@ module Cin7CoreAPI
|
|
|
145
149
|
FILTERED
|
|
146
150
|
end
|
|
147
151
|
|
|
152
|
+
private
|
|
153
|
+
|
|
148
154
|
def filter_authority(authority, host)
|
|
149
155
|
_, separator, host_port = authority.rpartition("@")
|
|
150
156
|
filtered_host = filter_url_component(host)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cin7_core_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- PostCo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|