octaspace 0.1.0 → 0.2.0
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 +29 -1
- data/README.md +11 -7
- data/lib/octaspace/errors.rb +26 -0
- data/lib/octaspace/payload_helpers.rb +53 -0
- data/lib/octaspace/resources/services/machine_rental.rb +32 -2
- data/lib/octaspace/resources/services/session_proxy.rb +7 -2
- data/lib/octaspace/resources/services/vpn.rb +1 -1
- data/lib/octaspace/version.rb +1 -1
- data/lib/octaspace.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1826290acf7155b153139f322c170ad14e369808d22f1333fa08571dd8ee3d60
|
|
4
|
+
data.tar.gz: cb3beab9b8e3a29f01cde3049f19cbd20e37103b32b62e52bde63482ef4a0a3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f158f5d579814e26c153776b24dfb29b273a2ff583dc64c2d806dd91616e8a7141465620c38f010a4efccacff50f07452d072af96e3135590d1a1c6a6654549
|
|
7
|
+
data.tar.gz: 588659f9b8449eaed8565bd61d01190be13c5223009e5cfe5aafa6ea949d0834a5ae264c5d2b191f0cad1d9e7b051e653c1721f54ff2ec411d124a1742c9a547
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.0] - 2026-04-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Support for `client.services.session(uuid).logs(recent: true)` for finished-session log retrieval.
|
|
13
|
+
- `OctaSpace::ProvisionRejectedError` for MR create requests that are transport-successful but rejected by the API payload contract.
|
|
14
|
+
- `OctaSpace::PayloadHelpers` with targeted helpers for stringified app port lists and marketplace bandwidth normalization.
|
|
15
|
+
- Diagnostics preset and manual runner support for recent session logs.
|
|
16
|
+
- Live-shaped fixtures for recent sessions and render marketplace responses.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `services.mr.list`, `services.render.list`, and `services.vpn.list` are now documented and tested as marketplace catalog endpoints rather than session collections.
|
|
21
|
+
- `services.mr.create` now forwards optional `organization_id` and `project_id`.
|
|
22
|
+
- Playground Services now presents marketplace-oriented summaries with normalized bandwidth display.
|
|
23
|
+
- Playground and README copy now reflect live API quirks for app ports, recent sessions, and finished-session logs.
|
|
24
|
+
- Publishing guidance now uses an explicit manual release flow, and the local `rake release` helper is disabled to avoid accidental RubyGems publication.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Corrected the Ruby public API gap for recent finished-session logs.
|
|
29
|
+
- Corrected MR create handling for `HTTP 200` batch-style rejection responses.
|
|
30
|
+
- Corrected fixtures and tests that modeled `/services/mr` and `/services/vpn` as session lists.
|
|
31
|
+
- Corrected logs fixtures and tests to match the live `{system, container}` payload shape.
|
|
32
|
+
- Corrected the playground Apps page so port counts work when the live API returns stringified JSON arrays.
|
|
33
|
+
- Corrected test coverage for `sessions?recent=true` by using live-shaped string telemetry fields.
|
|
34
|
+
|
|
8
35
|
## [0.1.0] - 2026-04-12
|
|
9
36
|
|
|
10
37
|
### Added
|
|
@@ -19,4 +46,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
46
|
- Rails integration with automatic client sharing and graceful shutdown.
|
|
20
47
|
- Interactive Playground app for manual testing and diagnostics.
|
|
21
48
|
|
|
22
|
-
[0.
|
|
49
|
+
[0.2.0]: https://github.com/octaspace/ruby-sdk/compare/v0.1.0...HEAD
|
|
50
|
+
[0.1.0]: https://github.com/octaspace/ruby-sdk/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -62,17 +62,18 @@ client.sessions.list # GET /sessions
|
|
|
62
62
|
session = client.services.session("uuid-here")
|
|
63
63
|
session.info # GET /services/:uuid/info
|
|
64
64
|
session.logs # GET /services/:uuid/logs
|
|
65
|
+
session.logs(recent: true) # GET /services/:uuid/logs?recent=true for finished sessions
|
|
65
66
|
session.stop(score: 5) # POST /services/:uuid/stop
|
|
66
67
|
|
|
67
68
|
# Services
|
|
68
|
-
client.services.mr.list # GET /services/mr
|
|
69
|
+
client.services.mr.list # GET /services/mr (marketplace machine catalog)
|
|
69
70
|
client.services.mr.create(
|
|
70
71
|
node_id: 1,
|
|
71
72
|
disk_size: 10,
|
|
72
73
|
image: "ubuntu:24.04",
|
|
73
74
|
app: "249b4cb3-3db1-4c06-98a4-772ba88cd81c"
|
|
74
75
|
) # POST /services/mr
|
|
75
|
-
client.services.vpn.list # GET /services/vpn
|
|
76
|
+
client.services.vpn.list # GET /services/vpn (VPN relay catalog)
|
|
76
77
|
client.services.vpn.create(node_id: 1, subkind: "wg") # POST /services/vpn
|
|
77
78
|
client.services.render.list # GET /services/render
|
|
78
79
|
client.services.render.create(node_id: 1, disk_size: 100) # POST /services/render
|
|
@@ -80,6 +81,9 @@ client.services.render.create(node_id: 1, disk_size: 100) # POST /services/rende
|
|
|
80
81
|
# Apps
|
|
81
82
|
client.apps.list # GET /apps
|
|
82
83
|
|
|
84
|
+
# Note: live API may serialize app port lists as JSON strings
|
|
85
|
+
# (for example "[]"). The raw response is preserved by the SDK.
|
|
86
|
+
|
|
83
87
|
# Network
|
|
84
88
|
client.network.info # GET /network
|
|
85
89
|
|
|
@@ -269,9 +273,9 @@ Pages:
|
|
|
269
273
|
|---|---|
|
|
270
274
|
| `/playground/account` | Profile + balance |
|
|
271
275
|
| `/playground/nodes` | Node list with state badges |
|
|
272
|
-
| `/playground/sessions` |
|
|
273
|
-
| `/playground/services` |
|
|
274
|
-
| `/playground/diagnostics` |
|
|
276
|
+
| `/playground/sessions` | Current + recent sessions, including live-format quirks |
|
|
277
|
+
| `/playground/services` | Marketplace catalogs for MR, Render, and VPN |
|
|
278
|
+
| `/playground/diagnostics` | Direct SDK method runner for contracts, payloads, transport mode, and pool stats |
|
|
275
279
|
|
|
276
280
|
## Development
|
|
277
281
|
|
|
@@ -288,7 +292,7 @@ bundle exec rake test # tests only
|
|
|
288
292
|
You can verify the gem in a clean Ruby environment without Rails:
|
|
289
293
|
|
|
290
294
|
1. Build the gem: `gem build octaspace.gemspec`
|
|
291
|
-
2. Install it locally: `gem install ./octaspace-0.
|
|
295
|
+
2. Install it locally: `gem install ./octaspace-0.2.0.gem`
|
|
292
296
|
3. Test in IRB:
|
|
293
297
|
|
|
294
298
|
```ruby
|
|
@@ -310,7 +314,7 @@ bundle exec appraisal rails-8-0 rake test
|
|
|
310
314
|
|
|
311
315
|
### Dummy Application (Playground)
|
|
312
316
|
|
|
313
|
-
The repository includes a Rails "Dummy" application for manual testing and UI prototyping. It is located in `test/dummy
|
|
317
|
+
The repository includes a Rails "Dummy" application for manual testing and UI prototyping. It is located in `test/dummy`.
|
|
314
318
|
|
|
315
319
|
To run the dummy app:
|
|
316
320
|
|
data/lib/octaspace/errors.rb
CHANGED
|
@@ -24,6 +24,32 @@ module OctaSpace
|
|
|
24
24
|
# API-level errors (HTTP response received, but indicates failure)
|
|
25
25
|
class ApiError < Error; end
|
|
26
26
|
|
|
27
|
+
# Domain-level rejection where transport succeeded but the provision request
|
|
28
|
+
# was rejected by the API payload contract.
|
|
29
|
+
class ProvisionRejectedError < Error
|
|
30
|
+
attr_reader :rejections
|
|
31
|
+
|
|
32
|
+
def initialize(message = nil, response: nil, rejections: [])
|
|
33
|
+
@rejections = Array(rejections)
|
|
34
|
+
super(message || build_message(@rejections), response: response)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def build_message(rejections)
|
|
40
|
+
first_reason =
|
|
41
|
+
rejections.filter_map do |item|
|
|
42
|
+
next unless item.is_a?(Hash)
|
|
43
|
+
|
|
44
|
+
item["reason"] || item[:reason]
|
|
45
|
+
end.first
|
|
46
|
+
|
|
47
|
+
return "Provision request rejected" if first_reason.to_s.empty?
|
|
48
|
+
|
|
49
|
+
"Provision request rejected: #{first_reason}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
27
53
|
# 401 Unauthorized
|
|
28
54
|
class AuthenticationError < ApiError; end
|
|
29
55
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module OctaSpace
|
|
6
|
+
module PayloadHelpers
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def parse_port_list(value)
|
|
10
|
+
case value
|
|
11
|
+
when nil
|
|
12
|
+
[]
|
|
13
|
+
when Array
|
|
14
|
+
value
|
|
15
|
+
when String
|
|
16
|
+
parse_stringified_port_list(value)
|
|
17
|
+
else
|
|
18
|
+
Array(value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def normalize_marketplace_bandwidth(value)
|
|
23
|
+
numeric =
|
|
24
|
+
case value
|
|
25
|
+
when nil
|
|
26
|
+
return nil
|
|
27
|
+
when Numeric
|
|
28
|
+
value.to_f
|
|
29
|
+
when String
|
|
30
|
+
Float(value)
|
|
31
|
+
else
|
|
32
|
+
return value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
return numeric unless numeric > 100_000
|
|
36
|
+
|
|
37
|
+
numeric / 125_000.0
|
|
38
|
+
rescue ArgumentError, TypeError
|
|
39
|
+
value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def parse_stringified_port_list(value)
|
|
43
|
+
stripped = value.strip
|
|
44
|
+
return [] if stripped.empty?
|
|
45
|
+
|
|
46
|
+
parsed = JSON.parse(stripped)
|
|
47
|
+
parsed.is_a?(Array) ? parsed : []
|
|
48
|
+
rescue JSON::ParserError
|
|
49
|
+
[]
|
|
50
|
+
end
|
|
51
|
+
private_class_method :parse_stringified_port_list
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -14,7 +14,7 @@ module OctaSpace
|
|
|
14
14
|
# app: "249b4cb3-3db1-4c06-98a4-772ba88cd81c"
|
|
15
15
|
# )
|
|
16
16
|
class MachineRental < Base
|
|
17
|
-
# List available
|
|
17
|
+
# List available marketplace machines for rent
|
|
18
18
|
# GET /services/mr
|
|
19
19
|
# @param params [Hash] optional filter params
|
|
20
20
|
# @return [OctaSpace::Response]
|
|
@@ -40,7 +40,37 @@ module OctaSpace
|
|
|
40
40
|
entrypoint: attrs[:entrypoint].to_s
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
item[:organization_id] = attrs[:organization_id] if attrs.key?(:organization_id)
|
|
44
|
+
item[:project_id] = attrs[:project_id] if attrs.key?(:project_id)
|
|
45
|
+
|
|
46
|
+
response = post("/services/mr", body: [item])
|
|
47
|
+
raise_if_rejected!(response)
|
|
48
|
+
response
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def raise_if_rejected!(response)
|
|
54
|
+
rejections = extract_rejections(response.data)
|
|
55
|
+
return if rejections.empty?
|
|
56
|
+
|
|
57
|
+
raise OctaSpace::ProvisionRejectedError.new(response: response, rejections: rejections)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def extract_rejections(data)
|
|
61
|
+
return [] unless data.is_a?(Array)
|
|
62
|
+
|
|
63
|
+
data.filter_map do |item|
|
|
64
|
+
next unless item.is_a?(Hash)
|
|
65
|
+
|
|
66
|
+
reason = item["reason"] || item[:reason]
|
|
67
|
+
status = item["status"] || item[:status]
|
|
68
|
+
uuid = item["uuid"] || item[:uuid]
|
|
69
|
+
next if uuid
|
|
70
|
+
next unless reason || status.to_i.positive?
|
|
71
|
+
|
|
72
|
+
item
|
|
73
|
+
end
|
|
44
74
|
end
|
|
45
75
|
end
|
|
46
76
|
end
|
|
@@ -31,9 +31,14 @@ module OctaSpace
|
|
|
31
31
|
|
|
32
32
|
# Fetch session logs
|
|
33
33
|
# GET /services/:uuid/logs
|
|
34
|
+
# @param recent [Boolean, nil] use the recent logs branch for finished sessions
|
|
34
35
|
# @return [OctaSpace::Response]
|
|
35
|
-
def logs
|
|
36
|
-
|
|
36
|
+
def logs(recent: nil)
|
|
37
|
+
params = {}
|
|
38
|
+
params[:recent] = true if recent
|
|
39
|
+
return @transport.get("/services/#{@uuid}/logs") if params.empty?
|
|
40
|
+
|
|
41
|
+
@transport.get("/services/#{@uuid}/logs", params:)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
# Stop the session
|
|
@@ -9,7 +9,7 @@ module OctaSpace
|
|
|
9
9
|
# client.services.vpn.list
|
|
10
10
|
# client.services.vpn.create(node_id: 123)
|
|
11
11
|
class Vpn < Base
|
|
12
|
-
# List
|
|
12
|
+
# List available VPN relay nodes
|
|
13
13
|
# GET /services/vpn
|
|
14
14
|
# @param params [Hash] optional filter params
|
|
15
15
|
# @return [OctaSpace::Response]
|
data/lib/octaspace/version.rb
CHANGED
data/lib/octaspace.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: octaspace
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OctaSpace Team
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- lib/octaspace/configuration.rb
|
|
54
54
|
- lib/octaspace/errors.rb
|
|
55
55
|
- lib/octaspace/middleware/url_rotator.rb
|
|
56
|
+
- lib/octaspace/payload_helpers.rb
|
|
56
57
|
- lib/octaspace/railtie.rb
|
|
57
58
|
- lib/octaspace/resources/accounts.rb
|
|
58
59
|
- lib/octaspace/resources/apps.rb
|