og_pilot_ruby 0.4.0 → 0.4.2

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: 5c13264ba768430bb4aba5a662b4957fe73c01a87ac5ba953897b35335686ab3
4
- data.tar.gz: 699f6f44f60db4eb9afb36a1eb17e6be4d3c80d8acabe0868140be522866d700
3
+ metadata.gz: 7dfbdfc6efc8c0fc36a6b92c6b99af56c0e0ef3b3831d7b91b8e56056b09dd16
4
+ data.tar.gz: 4fe12f91a949bd6117a915b48b88df8d25fda2d9665a5bba1d78533557cad202
5
5
  SHA512:
6
- metadata.gz: dd5ab6d159c8a38f0e1d89742e0112bdc258f3b16f1a770ac5427d0524a1082db68b92658b8be5666f43642092dbfb2a19b5e655b913c1ab29900c41bd229288
7
- data.tar.gz: fdf1e6bb75fef728a3422f6ce47326744c8b8052ec53723df1cfe19a32ba6b140b433096f3f55858ed13a1b28bd69471e81db0da6b725410084f8dc09766f363
6
+ metadata.gz: 6cb305db104f09b0fb0cfc430f8fd1fdc276d02d048e4f4936bde4ba5273569568e351e5fdafe9287bb33fc9f9ba5d94279c38f161ebb4f1a36a87aa56a331c0
7
+ data.tar.gz: e144441605b592d95c672242cb90813c1056dc498fd045a10ee0882dfa157a30600585f11effa2fb976983189e14a53170888a3323c8f4cc3198f458c96a6ca8
data/README.md CHANGED
@@ -61,6 +61,15 @@ image_url = OgPilotRuby.create_image(
61
61
  If you omit `iat`, OG Pilot will cache the image indefinitely. Provide an `iat` to
62
62
  refresh the cache daily.
63
63
 
64
+ ### Fail-safe behavior
65
+
66
+ `create_image` is non-blocking. If any error occurs (request, configuration,
67
+ validation, parsing, etc.), the gem does not raise to your app and logs an
68
+ error-level message instead.
69
+
70
+ - URL mode (`json: false`, default): returns `nil`
71
+ - JSON mode (`json: true`): returns `{ "image_url" => nil }`
72
+
64
73
  ### Template helpers
65
74
 
66
75
  `create_image` defaults to the `page` template when `template` is omitted.
@@ -119,7 +128,7 @@ The gem handles `iss` (domain) and `sub` (API key prefix) automatically.
119
128
 
120
129
  | Option | Default | Description |
121
130
  |-----------|---------|--------------------------------------------------------------------------|
122
- | `json` | `false` | When `true`, sends `Accept: application/json` and parses the JSON response |
131
+ | `json` | `false` | When `true`, sends `Accept: application/json` and parses the JSON response. On failure, returns `{ "image_url" => nil }` |
123
132
  | `headers` | — | Additional HTTP headers to include with the request |
124
133
  | `default` | `false` | Forces `path` to `/` when `true`, unless a manual `path` is provided (see [Path handling](#path-handling)) |
125
134
 
@@ -173,7 +182,7 @@ Fetch JSON metadata instead:
173
182
  ```ruby
174
183
  payload = {
175
184
  template: "page",
176
- title: "Hello OG Pilot"q
185
+ title: "Hello OG Pilot"
177
186
  }
178
187
 
179
188
  data = OgPilotRuby.create_image(**payload, json: true)
@@ -190,7 +199,7 @@ Multiple extensions are also stripped (`/archive.tar.gz` becomes `/archive`).
190
199
  Dotfiles like `/.hidden` are left unchanged. Query strings are preserved.
191
200
 
192
201
  ```ruby
193
- OgPilotRuby.configure do |config|›
202
+ OgPilotRuby.configure do |config|
194
203
  config.strip_extensions = true
195
204
  end
196
205
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "logger"
4
5
  require "net/http"
5
6
  require "uri"
6
7
 
@@ -10,6 +11,7 @@ require_relative "jwt_encoder"
10
11
  module OgPilotRuby
11
12
  class Client
12
13
  ENDPOINT_PATH = "/api/v1/images"
14
+ MAX_REDIRECTS = 5
13
15
 
14
16
  def initialize(config)
15
17
  @config = config
@@ -24,31 +26,67 @@ module OgPilotRuby
24
26
  params[:path] = manual_path.to_s.strip.empty? ? resolved_path(default:) : normalize_path(manual_path)
25
27
 
26
28
  uri = build_uri(params, iat:)
27
- response = request(uri, json:, headers:)
29
+ response, final_uri = request(uri, json:, headers:)
28
30
 
29
31
  if json
30
32
  JSON.parse(response.body)
31
33
  else
32
- response["Location"] || uri.to_s
34
+ response["Location"] || final_uri.to_s
33
35
  end
36
+ rescue StandardError => e
37
+ log_create_image_failure(e, json:)
38
+ json ? { "image_url" => nil } : nil
34
39
  end
35
40
 
36
41
  private
37
42
 
38
43
  attr_reader :config
39
44
 
40
- def request(uri, json:, headers:)
45
+ def log_create_image_failure(error, json:)
46
+ mode = json ? "json" : "url"
47
+ message = "OgPilotRuby create_image failed (mode=#{mode}): #{error.class}: #{error.message}"
48
+ create_image_logger&.error(message)
49
+ rescue StandardError
50
+ nil
51
+ end
52
+
53
+ def create_image_logger
54
+ if defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger
55
+ ::Rails.logger
56
+ else
57
+ @create_image_logger ||= Logger.new($stderr)
58
+ end
59
+ end
60
+
61
+ def request(uri, json:, headers:, method: :post, redirects_left: MAX_REDIRECTS)
41
62
  http = Net::HTTP.new(uri.host, uri.port)
42
63
  http.use_ssl = uri.scheme == "https"
43
64
  http.open_timeout = config.open_timeout if config.open_timeout
44
65
  http.read_timeout = config.read_timeout if config.read_timeout
45
66
 
46
- request = Net::HTTP::Post.new(uri)
67
+ request = build_http_request(method, uri)
47
68
  request["Accept"] = "application/json" if json
48
69
  headers.each { |key, value| request[key] = value }
49
70
 
50
71
  response = http.request(request)
51
- return response unless response.is_a?(Net::HTTPClientError) || response.is_a?(Net::HTTPServerError)
72
+ if response.is_a?(Net::HTTPRedirection)
73
+ location = response["Location"]
74
+ if location && !location.empty?
75
+ raise OgPilotRuby::RequestError, "OG Pilot request failed with too many redirects" if redirects_left <= 0
76
+
77
+ redirect_uri = URI.join(uri.to_s, location)
78
+ redirect_method = redirect_method_for(response, method)
79
+ return request(
80
+ redirect_uri,
81
+ json:,
82
+ headers:,
83
+ method: redirect_method,
84
+ redirects_left: redirects_left - 1
85
+ )
86
+ end
87
+ end
88
+
89
+ return [response, uri] unless response.is_a?(Net::HTTPClientError) || response.is_a?(Net::HTTPServerError)
52
90
 
53
91
  raise OgPilotRuby::RequestError, "OG Pilot request failed with status #{response.code}: #{response.body}"
54
92
  rescue OpenSSL::SSL::SSLError => e
@@ -63,6 +101,24 @@ module OgPilotRuby
63
101
  raise OgPilotRuby::RequestError, "OG Pilot request failed with unauthorized: #{e.message}"
64
102
  end
65
103
 
104
+ def build_http_request(method, uri)
105
+ case method
106
+ when :post
107
+ Net::HTTP::Post.new(uri)
108
+ when :get
109
+ Net::HTTP::Get.new(uri)
110
+ else
111
+ raise ArgumentError, "Unsupported HTTP method: #{method.inspect}"
112
+ end
113
+ end
114
+
115
+ def redirect_method_for(response, current_method)
116
+ return current_method unless current_method == :post
117
+
118
+ status_code = response.code.to_i
119
+ [307, 308].include?(status_code) ? :post : :get
120
+ end
121
+
66
122
  def build_uri(params, iat:)
67
123
  payload = build_payload(params, iat:)
68
124
  token = OgPilotRuby::JwtEncoder.encode(payload, api_key!)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OgPilotRuby
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: og_pilot_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sunergos IT LLC
@@ -97,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  requirements: []
100
- rubygems_version: 3.6.7
100
+ rubygems_version: 4.0.3
101
101
  specification_version: 4
102
102
  summary: Ruby client for the OG Pilot Open Graph image generator.
103
103
  test_files: []