openfga 0.2.0 → 0.3.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 +19 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +4 -0
- data/example/api-executor/README.md +83 -0
- data/example/api-executor/main.rb +103 -0
- data/example/example1/main.rb +3 -2
- data/lib/openfga/client/models/api_executor_request.rb +27 -0
- data/lib/openfga/client/models/api_executor_response.rb +17 -0
- data/lib/openfga/client/openfga_client.rb +52 -1
- data/lib/openfga/imports.rb +4 -0
- data/lib/openfga/token_manager/token_manager.rb +3 -3
- data/lib/openfga/version.rb +1 -1
- data/spec/client/api_executor_spec.rb +358 -0
- data/spec/client/openfga_client_spec.rb +75 -0
- data/vendor/bundle/ruby/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1/gem_make.out +6 -6
- data/vendor/bundle/ruby/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1/mkmf.log +2 -2
- data/vendor/bundle/ruby/4.0.0/extensions/x86_64-linux/4.0.0/byebug-13.0.0/gem_make.out +10 -10
- data/vendor/bundle/ruby/4.0.0/extensions/x86_64-linux/4.0.0/json-2.18.1/gem_make.out +5 -5
- data/vendor/bundle/ruby/4.0.0/extensions/x86_64-linux/4.0.0/prism-1.9.0/gem_make.out +6 -6
- data/vendor/bundle/ruby/4.0.0/extensions/x86_64-linux/4.0.0/racc-1.8.1/gem_make.out +5 -5
- data/vendor/bundle/ruby/4.0.0/specifications/bundler-2.5.9.gemspec +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e86ca5c3b828544d91af3d2ef40b045e6df1278c230a4118e4927fd55d3f04fd
|
|
4
|
+
data.tar.gz: 40844f4bce13ec832e5e029036c1c662fdb44c46a2492b4566b7e5cd775c0908
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55460e092ba8ec56bd63cc31ae198b359f97fa310de08882c7600d243aa638787dd3bc9e622442f2ce4068b26d01f9d3f030548abb5d405fcf5a9ad66e983e05
|
|
7
|
+
data.tar.gz: 07e96741790c259696adfd345d64f31a4d4efc4b5ea47cbd1fd13755e36ef0957c2cc3454163525c1ab553bea03ce1d67135500b07f7e923d6dd46cc53c79b99
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.1](https://github.com/carlastabile/openfga-ruby-sdk/compare/openfga/v0.3.0...openfga/v0.3.1) (2026-06-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Miscellaneous Chores
|
|
7
|
+
|
|
8
|
+
* empty rake task to fix publishing ([#39](https://github.com/carlastabile/openfga-ruby-sdk/issues/39)) ([f789069](https://github.com/carlastabile/openfga-ruby-sdk/commit/f7890690cef60d4374f9d09276ae42664a65b065))
|
|
9
|
+
|
|
10
|
+
## [0.3.0](https://github.com/carlastabile/openfga-ruby-sdk/compare/openfga/v0.2.0...openfga/v0.3.0) (2026-06-25)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add execute_api_request for calling arbitrary API endpoints ([#34](https://github.com/carlastabile/openfga-ruby-sdk/issues/34)) ([c5a6f8f](https://github.com/carlastabile/openfga-ruby-sdk/commit/c5a6f8f2c9d7a3f234cc3903fadc14f16e8b8ce7))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* custom logger initialization and usage ([#36](https://github.com/carlastabile/openfga-ruby-sdk/issues/36)) ([50b9d34](https://github.com/carlastabile/openfga-ruby-sdk/commit/50b9d34e8a9cbb0a53a1be65867ddbd437757511))
|
|
21
|
+
|
|
3
22
|
## [0.2.0](https://github.com/carlastabile/openfga-ruby-sdk/compare/openfga/v0.1.5...openfga/v0.2.0) (2026-04-17)
|
|
4
23
|
|
|
5
24
|
|
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Example: API Executor
|
|
2
|
+
|
|
3
|
+
This example demonstrates using `SdkClient#execute_api_request` to call arbitrary OpenFGA API endpoints. Rather than using the SDK's typed methods, you supply an HTTP method, path, and optional parameters directly - useful for endpoints not yet covered by the SDK or for advanced use cases.
|
|
4
|
+
|
|
5
|
+
## 1. Prerequisites
|
|
6
|
+
|
|
7
|
+
- Ruby 3.x (uses `bundler/inline` - no separate `bundle install` step needed)
|
|
8
|
+
- A running OpenFGA server
|
|
9
|
+
|
|
10
|
+
## 2. Start OpenFGA (Local Dev)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
docker run -p 8080:8080 --name openfga openfga/openfga:latest run
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or from the repo root:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
make start-openfga
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 3. Run the Example
|
|
23
|
+
|
|
24
|
+
From the repo root:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ruby example/api-executor/main.rb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or from this directory:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ruby main.rb
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### With a store ID (enables the check example)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
FGA_STORE_ID=<your-store-id> ruby main.rb
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With an API token
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
FGA_API_TOKEN=<your-token> FGA_STORE_ID=<your-store-id> ruby main.rb
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 4. Environment Variables
|
|
49
|
+
|
|
50
|
+
| Variable | Required | Description | Default |
|
|
51
|
+
|-----------------|----------|---------------------------------------------------|--------------------------|
|
|
52
|
+
| `FGA_API_URL` | No | Base URL of the OpenFGA server | `http://localhost:8080` |
|
|
53
|
+
| `FGA_STORE_ID` | No | Store ID for path-param and check examples | *(skips those examples)* |
|
|
54
|
+
| `FGA_API_TOKEN` | No | Bearer token for authenticated requests | *(no auth)* |
|
|
55
|
+
|
|
56
|
+
## 5. What the Example Covers
|
|
57
|
+
|
|
58
|
+
| Example | Method | Path | Demonstrates |
|
|
59
|
+
|---------|--------|--------------------------------|-------------------------------------|
|
|
60
|
+
| 1 | GET | `/stores` | Query params, basic response access |
|
|
61
|
+
| 2 | GET | `/stores/{store_id}` | Path param substitution |
|
|
62
|
+
| 3 | POST | `/stores/{store_id}/check` | Request body, custom headers |
|
|
63
|
+
| 4 | GET | `/stores/nonexistent-store-id` | Error handling with `ApiError` |
|
|
64
|
+
|
|
65
|
+
## 6. API Reference
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
response = client.execute_api_request(
|
|
69
|
+
method: :post, # HTTP verb (symbol or string)
|
|
70
|
+
path: '/stores/{store_id}/check', # Path template
|
|
71
|
+
path_params: { store_id: 'abc123' }, # Substituted into {placeholders}
|
|
72
|
+
query_params: { consistency: 'STRONG' }, # Appended to the URL
|
|
73
|
+
body: { tuple_key: { ... } }, # JSON-serialized automatically
|
|
74
|
+
headers: { 'X-Request-ID' => 'xyz' } # Merged with SDK defaults
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
response.status # Integer HTTP status code
|
|
78
|
+
response.data # Hash with symbolized keys (parsed JSON body)
|
|
79
|
+
response.headers # Response headers
|
|
80
|
+
response.success? # true for 2xx
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Auth headers (`Authorization: Bearer ...`) are injected automatically from the client's configured credentials.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/inline'
|
|
5
|
+
|
|
6
|
+
gemfile do
|
|
7
|
+
source 'https://rubygems.org'
|
|
8
|
+
gem 'json'
|
|
9
|
+
gem 'openfga', path: File.expand_path('../../', __dir__)
|
|
10
|
+
gem 'logger'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# OpenFGA API Executor Example
|
|
14
|
+
#
|
|
15
|
+
# Demonstrates how to call arbitrary OpenFGA API endpoints using
|
|
16
|
+
# SdkClient#execute_api_request. Auth headers are injected automatically,
|
|
17
|
+
# and path parameters are substituted and URL-encoded.
|
|
18
|
+
#
|
|
19
|
+
# Usage:
|
|
20
|
+
# FGA_API_URL=http://localhost:8080 ruby example/api-executor/main.rb
|
|
21
|
+
#
|
|
22
|
+
# Optional env vars:
|
|
23
|
+
# FGA_STORE_ID — run the check and get-store examples
|
|
24
|
+
# FGA_API_TOKEN — authenticate with a bearer token
|
|
25
|
+
|
|
26
|
+
logger = Logger.new($stdout)
|
|
27
|
+
logger.level = Logger::INFO
|
|
28
|
+
|
|
29
|
+
api_url = ENV.fetch('FGA_API_URL', 'http://localhost:8080')
|
|
30
|
+
store_id = ENV.fetch('FGA_STORE_ID', nil)
|
|
31
|
+
api_token = ENV.fetch('FGA_API_TOKEN', nil)
|
|
32
|
+
|
|
33
|
+
credentials = if api_token
|
|
34
|
+
{ method: :api_token, api_token: }
|
|
35
|
+
else
|
|
36
|
+
{ method: :none }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
client = OpenFga::SdkClient.new(
|
|
40
|
+
api_url:,
|
|
41
|
+
store_id:,
|
|
42
|
+
credentials:,
|
|
43
|
+
logger:
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# --- Example 1: GET /stores (no path params, with query params) ---
|
|
47
|
+
logger.info '=== Example 1: List stores ==='
|
|
48
|
+
|
|
49
|
+
response = client.execute_api_request(
|
|
50
|
+
method: :get,
|
|
51
|
+
path: '/stores',
|
|
52
|
+
query_params: { page_size: 5 }
|
|
53
|
+
)
|
|
54
|
+
logger.info "Status : #{response.status}"
|
|
55
|
+
logger.info "Success: #{response.success?}"
|
|
56
|
+
logger.info "Stores : #{response.data[:stores]&.length || 0} found"
|
|
57
|
+
|
|
58
|
+
# --- Example 2: GET /stores/{store_id} (path param substitution) ---
|
|
59
|
+
if store_id
|
|
60
|
+
logger.info '=== Example 2: Get store by ID ==='
|
|
61
|
+
|
|
62
|
+
response = client.execute_api_request(
|
|
63
|
+
method: :get,
|
|
64
|
+
path: '/stores/{store_id}',
|
|
65
|
+
path_params: { store_id: }
|
|
66
|
+
)
|
|
67
|
+
logger.info "Status : #{response.status}"
|
|
68
|
+
logger.info "Store name: #{response.data[:name]}"
|
|
69
|
+
else
|
|
70
|
+
logger.warn 'Set FGA_STORE_ID to run example 2'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# --- Example 3: POST using the block builder, with custom headers ---
|
|
74
|
+
if store_id
|
|
75
|
+
logger.info '=== Example 3: Check authorization via block builder ==='
|
|
76
|
+
|
|
77
|
+
response = client.execute_api_request(
|
|
78
|
+
method: :post,
|
|
79
|
+
path: '/stores/{store_id}/check',
|
|
80
|
+
path_params: { store_id: },
|
|
81
|
+
body: {
|
|
82
|
+
tuple_key: {
|
|
83
|
+
user: 'user:anne',
|
|
84
|
+
relation: 'reader',
|
|
85
|
+
object: 'document:2021-budget'
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
headers: { 'X-Request-ID' => 'api-executor-example-001' }
|
|
89
|
+
)
|
|
90
|
+
logger.info "Status : #{response.status}"
|
|
91
|
+
logger.info "Allowed: #{response.data[:allowed]}"
|
|
92
|
+
else
|
|
93
|
+
logger.warn 'Set FGA_STORE_ID to run example 3'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# --- Example 4: Error handling ---
|
|
97
|
+
logger.info '=== Example 4: Error handling ==='
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
client.execute_api_request(method: :get, path: '/stores/nonexistent-store-id')
|
|
101
|
+
rescue OpenFga::ApiError => e
|
|
102
|
+
logger.info "Caught ApiError — HTTP #{e.code}"
|
|
103
|
+
end
|
data/example/example1/main.rb
CHANGED
|
@@ -18,7 +18,7 @@ require 'ulid'
|
|
|
18
18
|
# This example demonstrates how to use the OpenFGA Ruby SDK to interact with an OpenFGA server.
|
|
19
19
|
class OpenFgaExample
|
|
20
20
|
def initialize
|
|
21
|
-
@logger = Logger.new(
|
|
21
|
+
@logger = Logger.new(STDOUT)
|
|
22
22
|
@logger.level = Logger::INFO
|
|
23
23
|
|
|
24
24
|
# Initialize the OpenFGA client
|
|
@@ -105,7 +105,8 @@ class OpenFgaExample
|
|
|
105
105
|
# Update client with store_id
|
|
106
106
|
@client = OpenFga::SdkClient.new(
|
|
107
107
|
api_url: ENV.fetch('FGA_API_URL', 'http://localhost:8080'),
|
|
108
|
-
store_id: @store_id
|
|
108
|
+
store_id: @store_id,
|
|
109
|
+
logger: @logger
|
|
109
110
|
)
|
|
110
111
|
end
|
|
111
112
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenFga
|
|
4
|
+
class ApiExecutorRequest
|
|
5
|
+
attr_accessor :method, :path, :path_params, :query_params, :body, :headers
|
|
6
|
+
|
|
7
|
+
def initialize(method:, path:, path_params: {}, query_params: {}, body: nil, headers: {})
|
|
8
|
+
@method = method
|
|
9
|
+
@path = path
|
|
10
|
+
@path_params = path_params || {}
|
|
11
|
+
@query_params = query_params || {}
|
|
12
|
+
@body = body
|
|
13
|
+
@headers = headers || {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.build
|
|
17
|
+
req = new(method: nil, path: nil)
|
|
18
|
+
yield req
|
|
19
|
+
req
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate!
|
|
23
|
+
raise ArgumentError, 'ApiExecutorRequest#method is required' if @method.nil?
|
|
24
|
+
raise ArgumentError, 'ApiExecutorRequest#path is required' if @path.nil? || @path.empty?
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenFga
|
|
4
|
+
class ApiExecutorResponse
|
|
5
|
+
attr_reader :data, :status, :headers
|
|
6
|
+
|
|
7
|
+
def initialize(data:, status:, headers:)
|
|
8
|
+
@data = data
|
|
9
|
+
@status = status
|
|
10
|
+
@headers = headers
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def success?
|
|
14
|
+
!@status.nil? && @status >= 200 && @status < 300
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'cgi'
|
|
3
4
|
require 'concurrent'
|
|
4
5
|
require 'set'
|
|
5
6
|
|
|
@@ -17,7 +18,8 @@ module OpenFga
|
|
|
17
18
|
method: :none
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
@logger = config
|
|
21
|
+
@logger = new_logger(@config)
|
|
22
|
+
@logger.debug('Using custom logger instance') if @config[:logger]
|
|
21
23
|
|
|
22
24
|
# Later we can support custom token managers.
|
|
23
25
|
@token_manager = case @config[:credentials][:method]
|
|
@@ -47,6 +49,7 @@ module OpenFga
|
|
|
47
49
|
c.host = @config[:api_url]
|
|
48
50
|
c.scheme = URI(@config[:api_url]).scheme
|
|
49
51
|
c.logger = @logger
|
|
52
|
+
c.debugging = true if @config[:logger]
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
@api_client = OpenFga::OpenFgaApi.new(ApiClient.new(api_client_config))
|
|
@@ -464,6 +467,39 @@ module OpenFga
|
|
|
464
467
|
@api_client.write_authorization_model(store_id(opts), request_body, wrap_options(opts))
|
|
465
468
|
end
|
|
466
469
|
|
|
470
|
+
# Executes a raw API request against the FGA server with automatic auth injection.
|
|
471
|
+
# @param method [String, Symbol] HTTP method (e.g. :get, :post)
|
|
472
|
+
# @param path [String] URL path template (e.g. '/stores/{store_id}/check')
|
|
473
|
+
# @param path_params [Hash] Values for {placeholder} substitution
|
|
474
|
+
# @param query_params [Hash] URL query parameters
|
|
475
|
+
# @param body [Hash, nil] Request body (JSON-serialized automatically)
|
|
476
|
+
# @param headers [Hash] Additional request headers
|
|
477
|
+
# @return [ApiExecutorResponse]
|
|
478
|
+
def execute_api_request(method:, path:, path_params: {}, query_params: {}, body: nil, headers: {})
|
|
479
|
+
request = ApiExecutorRequest.new(
|
|
480
|
+
method:,
|
|
481
|
+
path:,
|
|
482
|
+
path_params:,
|
|
483
|
+
query_params:,
|
|
484
|
+
body:,
|
|
485
|
+
headers:
|
|
486
|
+
)
|
|
487
|
+
request.validate!
|
|
488
|
+
|
|
489
|
+
resolved_path = substitute_path_params(request.path, request.path_params)
|
|
490
|
+
merged_headers = build_auth_headers.merge(request.headers)
|
|
491
|
+
|
|
492
|
+
opts = {
|
|
493
|
+
header_params: merged_headers,
|
|
494
|
+
query_params: request.query_params,
|
|
495
|
+
body: request.body,
|
|
496
|
+
return_type: 'Object'
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
data, status, response_headers = @api_client.api_client.call_api(request.method, resolved_path, opts)
|
|
500
|
+
ApiExecutorResponse.new(data:, status:, headers: response_headers)
|
|
501
|
+
end
|
|
502
|
+
|
|
467
503
|
private
|
|
468
504
|
|
|
469
505
|
# Executes a single batch check (used when no splitting is needed)
|
|
@@ -580,5 +616,20 @@ module OpenFga
|
|
|
580
616
|
'Authorization' => "Bearer #{token}"
|
|
581
617
|
}
|
|
582
618
|
end
|
|
619
|
+
|
|
620
|
+
def substitute_path_params(path, path_params)
|
|
621
|
+
path.gsub(/\{(\w+)\}/) do
|
|
622
|
+
key = $1
|
|
623
|
+
value = path_params[key] || path_params[key.to_sym]
|
|
624
|
+
value ? CGI.escape(value.to_s) : "{#{key}}"
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def new_logger(config)
|
|
629
|
+
logger = Logger.new($stdout)
|
|
630
|
+
logger.level = Logger::INFO
|
|
631
|
+
|
|
632
|
+
config[:logger] || (defined?(Rails) ? Rails.logger : logger)
|
|
633
|
+
end
|
|
583
634
|
end
|
|
584
635
|
end
|
data/lib/openfga/imports.rb
CHANGED
|
@@ -12,5 +12,9 @@ require 'openfga/configuration'
|
|
|
12
12
|
require 'openfga/client/client_errors'
|
|
13
13
|
require 'openfga/client/openfga_client'
|
|
14
14
|
|
|
15
|
+
# Client models
|
|
16
|
+
require 'openfga/client/models/api_executor_request'
|
|
17
|
+
require 'openfga/client/models/api_executor_response'
|
|
18
|
+
|
|
15
19
|
# Token management
|
|
16
20
|
require 'openfga/token_manager/token_manager'
|
|
@@ -55,7 +55,7 @@ module OpenFga
|
|
|
55
55
|
@config = config
|
|
56
56
|
@access_token_expires_at = nil
|
|
57
57
|
@access_token = nil
|
|
58
|
-
@logger = config.logger || Logger.new(
|
|
58
|
+
@logger = config.logger || Logger.new(STDOUT)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def access_token
|
|
@@ -63,7 +63,7 @@ module OpenFga
|
|
|
63
63
|
return @access_token
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
@logger.
|
|
66
|
+
@logger.debug "Refreshing access token from #{@config.token_issuer}"
|
|
67
67
|
|
|
68
68
|
form_data = {
|
|
69
69
|
'grant_type' => 'client_credentials',
|
|
@@ -92,7 +92,7 @@ module OpenFga
|
|
|
92
92
|
@access_token = body['access_token']
|
|
93
93
|
@access_token_expires_at = Time.now.utc + body['expires_in'].to_i
|
|
94
94
|
|
|
95
|
-
@logger.
|
|
95
|
+
@logger.debug "Obtained new access token, expires at #{@access_token_expires_at}"
|
|
96
96
|
|
|
97
97
|
@access_token
|
|
98
98
|
else raise TokenRefreshError.new("Failed to obtain access token: #{response.code} #{response.body}")
|
data/lib/openfga/version.rb
CHANGED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe OpenFga::ApiExecutorRequest do
|
|
4
|
+
describe '.new' do
|
|
5
|
+
it 'accepts keyword arguments' do
|
|
6
|
+
req = described_class.new(
|
|
7
|
+
method: :get,
|
|
8
|
+
path: '/stores',
|
|
9
|
+
query_params: { page_size: 10 }
|
|
10
|
+
)
|
|
11
|
+
expect(req.method).to eq(:get)
|
|
12
|
+
expect(req.path).to eq('/stores')
|
|
13
|
+
expect(req.query_params).to eq({ page_size: 10 })
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'defaults path_params, query_params, and headers to empty hashes' do
|
|
17
|
+
req = described_class.new(method: :get, path: '/stores')
|
|
18
|
+
expect(req.path_params).to eq({})
|
|
19
|
+
expect(req.query_params).to eq({})
|
|
20
|
+
expect(req.headers).to eq({})
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'defaults body to nil' do
|
|
24
|
+
req = described_class.new(method: :get, path: '/stores')
|
|
25
|
+
expect(req.body).to be_nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#validate!' do
|
|
30
|
+
it 'raises ArgumentError when method is nil' do
|
|
31
|
+
req = described_class.new(method: nil, path: '/stores')
|
|
32
|
+
expect { req.validate! }.to raise_error(ArgumentError, /method is required/)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'raises ArgumentError when path is empty' do
|
|
36
|
+
req = described_class.new(method: :get, path: '')
|
|
37
|
+
expect { req.validate! }.to raise_error(ArgumentError, /path is required/)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'does not raise when method and path are present' do
|
|
41
|
+
req = described_class.new(method: :get, path: '/stores')
|
|
42
|
+
expect { req.validate! }.not_to raise_error
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe OpenFga::ApiExecutorResponse do
|
|
48
|
+
it 'exposes data, status, and headers' do
|
|
49
|
+
resp = described_class.new(data: { allowed: true }, status: 200, headers: { 'x-foo' => 'bar' })
|
|
50
|
+
expect(resp.data).to eq({ allowed: true })
|
|
51
|
+
expect(resp.status).to eq(200)
|
|
52
|
+
expect(resp.headers).to eq({ 'x-foo' => 'bar' })
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it '#success? returns true for 2xx' do
|
|
56
|
+
expect(described_class.new(data: nil, status: 200, headers: {}).success?).to be(true)
|
|
57
|
+
expect(described_class.new(data: nil, status: 201, headers: {}).success?).to be(true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it '#success? returns false for non-2xx' do
|
|
61
|
+
expect(described_class.new(data: nil, status: 404, headers: {}).success?).to be(false)
|
|
62
|
+
expect(described_class.new(data: nil, status: 500, headers: {}).success?).to be(false)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe OpenFga::SdkClient, '#execute_api_request' do
|
|
67
|
+
let(:api_url) { 'http://localhost:8090' }
|
|
68
|
+
let(:store_id) { '01JSKYVY76JYW2DG65NG1444T4' }
|
|
69
|
+
let(:subject) { OpenFga::SdkClient.new(api_url:, store_id:) }
|
|
70
|
+
|
|
71
|
+
let(:subject_with_token) do
|
|
72
|
+
OpenFga::SdkClient.new(
|
|
73
|
+
api_url:,
|
|
74
|
+
store_id:,
|
|
75
|
+
credentials: { method: :api_token, api_token: 'my-token' }
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# --- input validation ---
|
|
80
|
+
|
|
81
|
+
it 'raises ArgumentError when method is missing' do
|
|
82
|
+
expect { subject.execute_api_request(path: '/stores') }
|
|
83
|
+
.to raise_error(ArgumentError)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'raises ArgumentError when path is empty' do
|
|
87
|
+
expect { subject.execute_api_request(method: :get, path: '') }
|
|
88
|
+
.to raise_error(ArgumentError, /path is required/)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# --- return value ---
|
|
92
|
+
|
|
93
|
+
it 'returns an ApiExecutorResponse with data, status, and headers' do
|
|
94
|
+
stub_request(:get, "#{api_url}/stores")
|
|
95
|
+
.to_return(
|
|
96
|
+
status: 200,
|
|
97
|
+
body: { stores: [] }.to_json,
|
|
98
|
+
headers: { 'Content-Type' => 'application/json', 'X-Request-Id' => 'abc' }
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
resp = subject.execute_api_request(method: :get, path: '/stores')
|
|
102
|
+
|
|
103
|
+
expect(resp).to be_a(OpenFga::ApiExecutorResponse)
|
|
104
|
+
expect(resp.status).to eq(200)
|
|
105
|
+
expect(resp.data).to eq({ stores: [] })
|
|
106
|
+
expect(resp.headers).to include('X-Request-Id' => 'abc')
|
|
107
|
+
expect(resp.success?).to be(true)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'returns nil data for a 204 No Content response' do
|
|
111
|
+
stub_request(:delete, "#{api_url}/stores/#{store_id}")
|
|
112
|
+
.to_return(status: 204, body: '', headers: {})
|
|
113
|
+
|
|
114
|
+
resp = subject.execute_api_request(
|
|
115
|
+
method: :delete,
|
|
116
|
+
path: '/stores/{store_id}',
|
|
117
|
+
path_params: { store_id: }
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
expect(resp.status).to eq(204)
|
|
121
|
+
expect(resp.data).to be_nil
|
|
122
|
+
expect(resp.success?).to be(true)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# --- HTTP methods ---
|
|
126
|
+
|
|
127
|
+
it 'sends a PUT request' do
|
|
128
|
+
stub = stub_request(:put, "#{api_url}/stores/#{store_id}")
|
|
129
|
+
.to_return(status: 200, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
130
|
+
|
|
131
|
+
subject.execute_api_request(
|
|
132
|
+
method: :put,
|
|
133
|
+
path: '/stores/{store_id}',
|
|
134
|
+
path_params: { store_id: },
|
|
135
|
+
body: { name: 'updated' }
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
expect(stub).to have_been_requested
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'sends a PATCH request' do
|
|
142
|
+
stub = stub_request(:patch, "#{api_url}/stores/#{store_id}")
|
|
143
|
+
.to_return(status: 200, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
144
|
+
|
|
145
|
+
subject.execute_api_request(
|
|
146
|
+
method: :patch,
|
|
147
|
+
path: '/stores/{store_id}',
|
|
148
|
+
path_params: { store_id: },
|
|
149
|
+
body: { name: 'patched' }
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
expect(stub).to have_been_requested
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it 'sends a DELETE request' do
|
|
156
|
+
stub = stub_request(:delete, "#{api_url}/stores/#{store_id}")
|
|
157
|
+
.to_return(status: 204, body: '', headers: {})
|
|
158
|
+
|
|
159
|
+
subject.execute_api_request(
|
|
160
|
+
method: :delete,
|
|
161
|
+
path: '/stores/{store_id}',
|
|
162
|
+
path_params: { store_id: }
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
expect(stub).to have_been_requested
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'accepts method as a string' do
|
|
169
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
170
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
171
|
+
|
|
172
|
+
subject.execute_api_request(method: 'GET', path: '/stores')
|
|
173
|
+
|
|
174
|
+
expect(stub).to have_been_requested
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# --- path param substitution ---
|
|
178
|
+
|
|
179
|
+
context 'path params' do
|
|
180
|
+
it 'substitutes a single {placeholder} and CGI-encodes the value' do
|
|
181
|
+
stub = stub_request(:post, "#{api_url}/stores/#{store_id}/check")
|
|
182
|
+
.to_return(status: 200, body: { allowed: true }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
183
|
+
|
|
184
|
+
resp = subject.execute_api_request(
|
|
185
|
+
method: :post,
|
|
186
|
+
path: '/stores/{store_id}/check',
|
|
187
|
+
path_params: { store_id: },
|
|
188
|
+
body: { tuple_key: { user: 'user:anne', relation: 'reader', object: 'doc:1' } }
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
expect(stub).to have_been_requested
|
|
192
|
+
expect(resp.data[:allowed]).to be(true)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'substitutes multiple placeholders' do
|
|
196
|
+
model_id = '01G50QVV17PECNVAHX1GG4Y5NC'
|
|
197
|
+
stub = stub_request(:get, "#{api_url}/stores/#{store_id}/authorization-models/#{model_id}")
|
|
198
|
+
.to_return(status: 200, body: { authorization_model: {} }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
199
|
+
|
|
200
|
+
subject.execute_api_request(
|
|
201
|
+
method: :get,
|
|
202
|
+
path: '/stores/{store_id}/authorization-models/{model_id}',
|
|
203
|
+
path_params: { store_id:, model_id: }
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
expect(stub).to have_been_requested
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it 'accepts symbol keys in path_params' do
|
|
210
|
+
stub = stub_request(:get, "#{api_url}/stores/#{store_id}")
|
|
211
|
+
.to_return(status: 200, body: { id: store_id }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
212
|
+
|
|
213
|
+
subject.execute_api_request(
|
|
214
|
+
method: :get,
|
|
215
|
+
path: '/stores/{store_id}',
|
|
216
|
+
path_params: { store_id: }
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
expect(stub).to have_been_requested
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'URL-encodes special characters in path param values' do
|
|
223
|
+
encoded = CGI.escape('store/with spaces')
|
|
224
|
+
stub = stub_request(:get, "#{api_url}/stores/#{encoded}")
|
|
225
|
+
.to_return(status: 200, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
226
|
+
|
|
227
|
+
subject.execute_api_request(
|
|
228
|
+
method: :get,
|
|
229
|
+
path: '/stores/{store_id}',
|
|
230
|
+
path_params: { store_id: 'store/with spaces' }
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
expect(stub).to have_been_requested
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it 'raises URI::InvalidURIError when a placeholder has no matching path_param' do
|
|
237
|
+
expect {
|
|
238
|
+
subject.execute_api_request(method: :get, path: '/stores/{unknown_key}')
|
|
239
|
+
}.to raise_error(URI::InvalidURIError)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# --- auth header injection ---
|
|
244
|
+
|
|
245
|
+
context 'auth headers' do
|
|
246
|
+
it 'sends no Authorization header when credentials method is :none' do
|
|
247
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
248
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
249
|
+
|
|
250
|
+
subject.execute_api_request(method: :get, path: '/stores')
|
|
251
|
+
|
|
252
|
+
expect(stub).to have_been_requested
|
|
253
|
+
expect(stub.with(headers: { 'Authorization' => /.*/ })).not_to have_been_requested
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it 'injects Authorization header when api_token credential is configured' do
|
|
257
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
258
|
+
.with(headers: { 'Authorization' => 'Bearer my-token' })
|
|
259
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
260
|
+
|
|
261
|
+
subject_with_token.execute_api_request(method: :get, path: '/stores')
|
|
262
|
+
|
|
263
|
+
expect(stub).to have_been_requested
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it 'caller-supplied Authorization header overrides the SDK token' do
|
|
267
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
268
|
+
.with(headers: { 'Authorization' => 'Bearer caller-override' })
|
|
269
|
+
.to_return(status: 200, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
270
|
+
|
|
271
|
+
subject_with_token.execute_api_request(
|
|
272
|
+
method: :get,
|
|
273
|
+
path: '/stores',
|
|
274
|
+
headers: { 'Authorization' => 'Bearer caller-override' }
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
expect(stub).to have_been_requested
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it 'merges caller-supplied headers with SDK auth headers' do
|
|
281
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
282
|
+
.with(headers: { 'Authorization' => 'Bearer my-token', 'X-Custom' => 'value' })
|
|
283
|
+
.to_return(status: 200, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
284
|
+
|
|
285
|
+
subject_with_token.execute_api_request(
|
|
286
|
+
method: :get,
|
|
287
|
+
path: '/stores',
|
|
288
|
+
headers: { 'X-Custom' => 'value' }
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
expect(stub).to have_been_requested
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# --- query params ---
|
|
296
|
+
|
|
297
|
+
context 'query params' do
|
|
298
|
+
it 'passes a single query param to the HTTP layer' do
|
|
299
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
300
|
+
.with(query: { 'page_size' => '25' })
|
|
301
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
302
|
+
|
|
303
|
+
subject.execute_api_request(method: :get, path: '/stores', query_params: { page_size: 25 })
|
|
304
|
+
|
|
305
|
+
expect(stub).to have_been_requested
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it 'passes multiple query params to the HTTP layer' do
|
|
309
|
+
stub = stub_request(:get, "#{api_url}/stores")
|
|
310
|
+
.with(query: { 'page_size' => '10', 'continuation_token' => 'tok123' })
|
|
311
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
312
|
+
|
|
313
|
+
subject.execute_api_request(
|
|
314
|
+
method: :get,
|
|
315
|
+
path: '/stores',
|
|
316
|
+
query_params: { page_size: 10, continuation_token: 'tok123' }
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
expect(stub).to have_been_requested
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# --- error responses ---
|
|
324
|
+
|
|
325
|
+
context 'error responses' do
|
|
326
|
+
it 'raises ApiError for a 404 response' do
|
|
327
|
+
stub_request(:get, "#{api_url}/stores/does-not-exist")
|
|
328
|
+
.to_return(status: 404, body: { code: 'store_id_not_found' }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
329
|
+
|
|
330
|
+
expect {
|
|
331
|
+
subject.execute_api_request(method: :get, path: '/stores/does-not-exist')
|
|
332
|
+
}.to raise_error(OpenFga::ApiError)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it 'raises ApiError for a 500 response' do
|
|
336
|
+
stub_request(:post, "#{api_url}/stores/#{store_id}/check")
|
|
337
|
+
.to_return(status: 500, body: { code: 'internal_error' }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
338
|
+
|
|
339
|
+
expect {
|
|
340
|
+
subject.execute_api_request(
|
|
341
|
+
method: :post,
|
|
342
|
+
path: '/stores/{store_id}/check',
|
|
343
|
+
path_params: { store_id: },
|
|
344
|
+
body: { tuple_key: {} }
|
|
345
|
+
)
|
|
346
|
+
}.to raise_error(OpenFga::ApiError)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
it 'includes the HTTP status code in the raised ApiError' do
|
|
350
|
+
stub_request(:get, "#{api_url}/stores/does-not-exist")
|
|
351
|
+
.to_return(status: 404, body: { code: 'store_id_not_found' }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
352
|
+
|
|
353
|
+
expect {
|
|
354
|
+
subject.execute_api_request(method: :get, path: '/stores/does-not-exist')
|
|
355
|
+
}.to raise_error(OpenFga::ApiError) { |e| expect(e.code).to eq(404) }
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
end
|
|
@@ -2185,4 +2185,79 @@ describe OpenFga::SdkClient do
|
|
|
2185
2185
|
end
|
|
2186
2186
|
end
|
|
2187
2187
|
end
|
|
2188
|
+
|
|
2189
|
+
describe '#execute_api_request' do
|
|
2190
|
+
it 'raises ArgumentError when method is omitted' do
|
|
2191
|
+
expect { subject.execute_api_request(path: '/stores') }.to raise_error(ArgumentError)
|
|
2192
|
+
end
|
|
2193
|
+
|
|
2194
|
+
it 'raises ArgumentError when path is empty' do
|
|
2195
|
+
expect { subject.execute_api_request(method: :get, path: '') }
|
|
2196
|
+
.to raise_error(ArgumentError, /path is required/)
|
|
2197
|
+
end
|
|
2198
|
+
|
|
2199
|
+
it 'returns an ApiExecutorResponse on success' do
|
|
2200
|
+
stub_request(:get, stores_url)
|
|
2201
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
2202
|
+
|
|
2203
|
+
resp = subject.execute_api_request(method: :get, path: '/stores')
|
|
2204
|
+
|
|
2205
|
+
expect(resp).to be_a(OpenFga::ApiExecutorResponse)
|
|
2206
|
+
expect(resp.status).to eq(200)
|
|
2207
|
+
expect(resp.data).to eq({ stores: [] })
|
|
2208
|
+
expect(resp.success?).to be(true)
|
|
2209
|
+
end
|
|
2210
|
+
|
|
2211
|
+
it 'substitutes path params into the URL' do
|
|
2212
|
+
stub = stub_request(:get, stores_url(store_id))
|
|
2213
|
+
.to_return(status: 200, body: { id: store_id }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
2214
|
+
|
|
2215
|
+
subject.execute_api_request(
|
|
2216
|
+
method: :get,
|
|
2217
|
+
path: '/stores/{store_id}',
|
|
2218
|
+
path_params: { store_id: }
|
|
2219
|
+
)
|
|
2220
|
+
|
|
2221
|
+
expect(stub).to have_been_requested
|
|
2222
|
+
end
|
|
2223
|
+
|
|
2224
|
+
it 'sends the request body as JSON' do
|
|
2225
|
+
stub = stub_request(:post, "#{stores_url(store_id)}/check")
|
|
2226
|
+
.with(body: { tuple_key: { user: 'user:anne', relation: 'reader', object: 'doc:1' } }.to_json)
|
|
2227
|
+
.to_return(status: 200, body: { allowed: true }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
2228
|
+
|
|
2229
|
+
resp = subject.execute_api_request(
|
|
2230
|
+
method: :post,
|
|
2231
|
+
path: '/stores/{store_id}/check',
|
|
2232
|
+
path_params: { store_id: },
|
|
2233
|
+
body: { tuple_key: { user: 'user:anne', relation: 'reader', object: 'doc:1' } }
|
|
2234
|
+
)
|
|
2235
|
+
|
|
2236
|
+
expect(stub).to have_been_requested
|
|
2237
|
+
expect(resp.data[:allowed]).to be(true)
|
|
2238
|
+
end
|
|
2239
|
+
|
|
2240
|
+
it 'injects the Authorization header when credentials are configured' do
|
|
2241
|
+
stub = stub_request(:get, stores_url)
|
|
2242
|
+
.with(headers: api_token_authz_header)
|
|
2243
|
+
.to_return(status: 200, body: { stores: [] }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
2244
|
+
|
|
2245
|
+
subject_with_api_token.execute_api_request(method: :get, path: '/stores')
|
|
2246
|
+
|
|
2247
|
+
expect(stub).to have_been_requested
|
|
2248
|
+
end
|
|
2249
|
+
|
|
2250
|
+
it 'raises ApiError for non-2xx responses' do
|
|
2251
|
+
stub_request(:get, stores_url(store_id))
|
|
2252
|
+
.to_return(status: 404, body: { code: 'store_id_not_found' }.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
2253
|
+
|
|
2254
|
+
expect {
|
|
2255
|
+
subject.execute_api_request(
|
|
2256
|
+
method: :get,
|
|
2257
|
+
path: '/stores/{store_id}',
|
|
2258
|
+
path_params: { store_id: }
|
|
2259
|
+
)
|
|
2260
|
+
}.to raise_error(OpenFga::ApiError)
|
|
2261
|
+
end
|
|
2262
|
+
end
|
|
2188
2263
|
end
|
|
@@ -22,17 +22,17 @@ checking for RB_WARN_CATEGORY_DEPRECATED in ruby.h... yes
|
|
|
22
22
|
creating Makefile
|
|
23
23
|
|
|
24
24
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.0.1/ext/bigdecimal
|
|
25
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
25
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-8ayezx sitelibdir\=./.gem.20260625-2219-8ayezx clean
|
|
26
26
|
|
|
27
27
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.0.1/ext/bigdecimal
|
|
28
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
29
|
-
compiling bigdecimal.c
|
|
28
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-8ayezx sitelibdir\=./.gem.20260625-2219-8ayezx
|
|
30
29
|
compiling missing.c
|
|
30
|
+
compiling bigdecimal.c
|
|
31
31
|
linking shared-object bigdecimal.so
|
|
32
32
|
|
|
33
33
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.0.1/ext/bigdecimal
|
|
34
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
35
|
-
/usr/bin/install -c -m 0755 bigdecimal.so ./.gem.
|
|
34
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-8ayezx sitelibdir\=./.gem.20260625-2219-8ayezx install
|
|
35
|
+
/usr/bin/install -c -m 0755 bigdecimal.so ./.gem.20260625-2219-8ayezx
|
|
36
36
|
|
|
37
37
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.0.1/ext/bigdecimal
|
|
38
|
-
make DESTDIR\= sitearchdir\=./.gem.
|
|
38
|
+
make DESTDIR\= sitearchdir\=./.gem.20260625-2219-8ayezx sitelibdir\=./.gem.20260625-2219-8ayezx clean
|
|
@@ -135,7 +135,7 @@ checked program was:
|
|
|
135
135
|
have_func: checking for _lzcnt_u32() in x86intrin.h... -------------------- no
|
|
136
136
|
|
|
137
137
|
LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/4.0.1/x64/lib LSAN_OPTIONS=detect_leaks=0 "gcc -o conftest -I/opt/hostedtoolcache/Ruby/4.0.1/x64/include/ruby-4.0.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/4.0.1/x64/include/ruby-4.0.0/ruby/backward -I/opt/hostedtoolcache/Ruby/4.0.1/x64/include/ruby-4.0.0 -I. -DENABLE_PATH_CHECK=0 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -L/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -lruby -lm -lpthread -lc"
|
|
138
|
-
/usr/bin/ld: /tmp/
|
|
138
|
+
/usr/bin/ld: /tmp/cchVHUwO.o: in function `t':
|
|
139
139
|
/home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.0.1/ext/bigdecimal/conftest.c:16:(.text+0x7): undefined reference to `_lzcnt_u32'
|
|
140
140
|
collect2: error: ld returned 1 exit status
|
|
141
141
|
checked program was:
|
|
@@ -197,7 +197,7 @@ checked program was:
|
|
|
197
197
|
have_func: checking for _lzcnt_u64() in x86intrin.h... -------------------- no
|
|
198
198
|
|
|
199
199
|
LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/4.0.1/x64/lib LSAN_OPTIONS=detect_leaks=0 "gcc -o conftest -I/opt/hostedtoolcache/Ruby/4.0.1/x64/include/ruby-4.0.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/4.0.1/x64/include/ruby-4.0.0/ruby/backward -I/opt/hostedtoolcache/Ruby/4.0.1/x64/include/ruby-4.0.0 -I. -DENABLE_PATH_CHECK=0 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -L/opt/hostedtoolcache/Ruby/4.0.1/x64/lib -lruby -lm -lpthread -lc"
|
|
200
|
-
/usr/bin/ld: /tmp/
|
|
200
|
+
/usr/bin/ld: /tmp/cc0DFTcc.o: in function `t':
|
|
201
201
|
/home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.0.1/ext/bigdecimal/conftest.c:16:(.text+0x7): undefined reference to `_lzcnt_u64'
|
|
202
202
|
collect2: error: ld returned 1 exit status
|
|
203
203
|
checked program was:
|
|
@@ -3,23 +3,23 @@ current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bu
|
|
|
3
3
|
creating Makefile
|
|
4
4
|
|
|
5
5
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/byebug-13.0.0/ext/byebug
|
|
6
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
6
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-efvxys sitelibdir\=./.gem.20260625-2219-efvxys clean
|
|
7
7
|
|
|
8
8
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/byebug-13.0.0/ext/byebug
|
|
9
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
9
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-efvxys sitelibdir\=./.gem.20260625-2219-efvxys
|
|
10
10
|
compiling breakpoint.c
|
|
11
11
|
compiling byebug.c
|
|
12
12
|
compiling context.c
|
|
13
|
-
compiling locker.c
|
|
14
13
|
compiling threads.c
|
|
15
|
-
|
|
16
|
-
byebug.c:69:1: warning: old-style function definition [-Wold-style-definition]
|
|
17
|
-
69 | check_started()
|
|
18
|
-
| ^~~~~~~~~~~~~
|
|
14
|
+
compiling locker.c
|
|
19
15
|
locker.c: In function ‘byebug_pop_from_locked’:
|
|
20
16
|
locker.c:53:1: warning: old-style function definition [-Wold-style-definition]
|
|
21
17
|
53 | byebug_pop_from_locked()
|
|
22
18
|
| ^~~~~~~~~~~~~~~~~~~~~~
|
|
19
|
+
byebug.c: In function ‘check_started’:
|
|
20
|
+
byebug.c:69:1: warning: old-style function definition [-Wold-style-definition]
|
|
21
|
+
69 | check_started()
|
|
22
|
+
| ^~~~~~~~~~~~~
|
|
23
23
|
byebug.c: In function ‘Init_byebug’:
|
|
24
24
|
byebug.c:871:1: warning: old-style function definition [-Wold-style-definition]
|
|
25
25
|
871 | Init_byebug()
|
|
@@ -35,8 +35,8 @@ cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’
|
|
|
35
35
|
linking shared-object byebug/byebug.so
|
|
36
36
|
|
|
37
37
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/byebug-13.0.0/ext/byebug
|
|
38
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
39
|
-
/usr/bin/install -c -m 0755 byebug.so ./.gem.
|
|
38
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-efvxys sitelibdir\=./.gem.20260625-2219-efvxys install
|
|
39
|
+
/usr/bin/install -c -m 0755 byebug.so ./.gem.20260625-2219-efvxys/byebug
|
|
40
40
|
|
|
41
41
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/byebug-13.0.0/ext/byebug
|
|
42
|
-
make DESTDIR\= sitearchdir\=./.gem.
|
|
42
|
+
make DESTDIR\= sitearchdir\=./.gem.20260625-2219-efvxys sitelibdir\=./.gem.20260625-2219-efvxys clean
|
|
@@ -10,16 +10,16 @@ checking for cpuid.h... yes
|
|
|
10
10
|
creating Makefile
|
|
11
11
|
|
|
12
12
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/json-2.18.1/ext/json/ext/parser
|
|
13
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
13
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-2s49ni sitelibdir\=./.gem.20260625-2219-2s49ni clean
|
|
14
14
|
|
|
15
15
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/json-2.18.1/ext/json/ext/parser
|
|
16
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
16
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-2s49ni sitelibdir\=./.gem.20260625-2219-2s49ni
|
|
17
17
|
compiling parser.c
|
|
18
18
|
linking shared-object json/ext/parser.so
|
|
19
19
|
|
|
20
20
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/json-2.18.1/ext/json/ext/parser
|
|
21
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
22
|
-
/usr/bin/install -c -m 0755 parser.so ./.gem.
|
|
21
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-2s49ni sitelibdir\=./.gem.20260625-2219-2s49ni install
|
|
22
|
+
/usr/bin/install -c -m 0755 parser.so ./.gem.20260625-2219-2s49ni/json/ext
|
|
23
23
|
|
|
24
24
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/json-2.18.1/ext/json/ext/parser
|
|
25
|
-
make DESTDIR\= sitearchdir\=./.gem.
|
|
25
|
+
make DESTDIR\= sitearchdir\=./.gem.20260625-2219-2s49ni sitelibdir\=./.gem.20260625-2219-2s49ni clean
|
|
@@ -6,15 +6,15 @@ checking for whether -fvisibility=hidden is accepted as CFLAGS... yes
|
|
|
6
6
|
creating Makefile
|
|
7
7
|
|
|
8
8
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/ext/prism
|
|
9
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
9
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-q0aq54 sitelibdir\=./.gem.20260625-2219-q0aq54 clean
|
|
10
10
|
|
|
11
11
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/ext/prism
|
|
12
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
12
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-q0aq54 sitelibdir\=./.gem.20260625-2219-q0aq54
|
|
13
13
|
compiling api_node.c
|
|
14
14
|
compiling api_pack.c
|
|
15
15
|
compiling extension.c
|
|
16
|
-
compiling ./../../src/diagnostic.c
|
|
17
16
|
compiling ./../../src/encoding.c
|
|
17
|
+
compiling ./../../src/diagnostic.c
|
|
18
18
|
compiling ./../../src/node.c
|
|
19
19
|
compiling ./../../src/options.c
|
|
20
20
|
compiling ./../../src/pack.c
|
|
@@ -37,8 +37,8 @@ compiling ./../../src/util/pm_strpbrk.c
|
|
|
37
37
|
linking shared-object prism/prism.so
|
|
38
38
|
|
|
39
39
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/ext/prism
|
|
40
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
41
|
-
/usr/bin/install -c -m 0755 prism.so ./.gem.
|
|
40
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-q0aq54 sitelibdir\=./.gem.20260625-2219-q0aq54 install
|
|
41
|
+
/usr/bin/install -c -m 0755 prism.so ./.gem.20260625-2219-q0aq54/prism
|
|
42
42
|
|
|
43
43
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/ext/prism
|
|
44
|
-
make DESTDIR\= sitearchdir\=./.gem.
|
|
44
|
+
make DESTDIR\= sitearchdir\=./.gem.20260625-2219-q0aq54 sitelibdir\=./.gem.20260625-2219-q0aq54 clean
|
|
@@ -3,16 +3,16 @@ current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bu
|
|
|
3
3
|
creating Makefile
|
|
4
4
|
|
|
5
5
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ext/racc/cparse
|
|
6
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
6
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-rl1ihv sitelibdir\=./.gem.20260625-2219-rl1ihv clean
|
|
7
7
|
|
|
8
8
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ext/racc/cparse
|
|
9
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
9
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-rl1ihv sitelibdir\=./.gem.20260625-2219-rl1ihv
|
|
10
10
|
compiling cparse.c
|
|
11
11
|
linking shared-object racc/cparse.so
|
|
12
12
|
|
|
13
13
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ext/racc/cparse
|
|
14
|
-
make -j5 DESTDIR\= sitearchdir\=./.gem.
|
|
15
|
-
/usr/bin/install -c -m 0755 cparse.so ./.gem.
|
|
14
|
+
make -j5 DESTDIR\= sitearchdir\=./.gem.20260625-2219-rl1ihv sitelibdir\=./.gem.20260625-2219-rl1ihv install
|
|
15
|
+
/usr/bin/install -c -m 0755 cparse.so ./.gem.20260625-2219-rl1ihv/racc
|
|
16
16
|
|
|
17
17
|
current directory: /home/runner/work/openfga-ruby-sdk/openfga-ruby-sdk/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ext/racc/cparse
|
|
18
|
-
make DESTDIR\= sitearchdir\=./.gem.
|
|
18
|
+
make DESTDIR\= sitearchdir\=./.gem.20260625-2219-rl1ihv sitelibdir\=./.gem.20260625-2219-rl1ihv clean
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openfga
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carla Urrea Stabile, Steven Hobbs
|
|
@@ -196,6 +196,8 @@ files:
|
|
|
196
196
|
- docs/WriteRequest.md
|
|
197
197
|
- docs/WriteRequestDeletes.md
|
|
198
198
|
- docs/WriteRequestWrites.md
|
|
199
|
+
- example/api-executor/README.md
|
|
200
|
+
- example/api-executor/main.rb
|
|
199
201
|
- example/example1/README.md
|
|
200
202
|
- example/example1/main.rb
|
|
201
203
|
- example/example2/.env.example
|
|
@@ -209,6 +211,8 @@ files:
|
|
|
209
211
|
- lib/openfga/api_model_base.rb
|
|
210
212
|
- lib/openfga/client.rb
|
|
211
213
|
- lib/openfga/client/client_errors.rb
|
|
214
|
+
- lib/openfga/client/models/api_executor_request.rb
|
|
215
|
+
- lib/openfga/client/models/api_executor_response.rb
|
|
212
216
|
- lib/openfga/client/openfga_client.rb
|
|
213
217
|
- lib/openfga/configuration.rb
|
|
214
218
|
- lib/openfga/constants.rb
|
|
@@ -308,6 +312,7 @@ files:
|
|
|
308
312
|
- release-please-config.json
|
|
309
313
|
- spec/api/open_fga_api_spec.rb
|
|
310
314
|
- spec/api_client_spec.rb
|
|
315
|
+
- spec/client/api_executor_spec.rb
|
|
311
316
|
- spec/client/openfga_client_spec.rb
|
|
312
317
|
- spec/configuration_spec.rb
|
|
313
318
|
- spec/fixtures/expand_response.json
|
|
@@ -4224,7 +4229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
4224
4229
|
- !ruby/object:Gem::Version
|
|
4225
4230
|
version: '0'
|
|
4226
4231
|
requirements: []
|
|
4227
|
-
rubygems_version: 4.0.
|
|
4232
|
+
rubygems_version: 4.0.15
|
|
4228
4233
|
specification_version: 4
|
|
4229
4234
|
summary: This is community-driven Ruby SDK for OpenFGA. It provides a wrapper around
|
|
4230
4235
|
the [OpenFGA API definition](https://openfga.dev/api).
|