bannerbear 0.1.4 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a63e7f3269afc788dea97a56ba271553cce7ae8ad8c605246321be788bbe5601
4
- data.tar.gz: 68aa7455b8b4b71ddcf47ee0a59563d0a36f437670822680b9030ff0d41993a2
3
+ metadata.gz: 850108dc51b0e1e90d1e7e6452ce69b65c972638e33d7f6b946f0f81ab4fe1cd
4
+ data.tar.gz: f4c1e8883281527d9c515a07d358a251629ec19fb06a2dab4506fb95cfd9576a
5
5
  SHA512:
6
- metadata.gz: 2e4cc99311a703073a184a5211bf1c7c14b18df1062b03592558368e4d017d3f5e116f1c7948c63703300bb4af6cc6da5c0b15bdaf5942e4a1465624fc89c0af
7
- data.tar.gz: a3017ce751ebfac3260bd7f174b61040ac2c0130a28f13ced336f7d77e0944507a195ef28aba8701d706fc4b6bef8d5b428a7ee3e51bfa716ef92fddf33af26a
6
+ metadata.gz: de72ce94b734dbcfc73ca872ce5b2ce9f41c23b6f5c1bf40e2cf78bf8e2938b81933ab20113c41b5e8ad0af2dc034ff251b696ef310219063ea066dfdbf759ba
7
+ data.tar.gz: 92bfc10f330f5d318084b8e446491678029de74807ef353e2d679279590e22487780e15f4b093fa0bf374c8601fc6e685faf8d9d96f9fa4266f088638080186f
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /smoke_test_v5.rb
data/Gemfile.lock CHANGED
@@ -1,19 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bannerbear (0.1.4)
4
+ bannerbear (0.2.0)
5
5
  httparty
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- httparty (0.20.0)
11
- mime-types (~> 3.0)
10
+ bigdecimal (4.1.2)
11
+ csv (3.3.5)
12
+ httparty (0.24.2)
13
+ csv
14
+ mini_mime (>= 1.0.0)
12
15
  multi_xml (>= 0.5.2)
13
- mime-types (3.4.1)
14
- mime-types-data (~> 3.2015)
15
- mime-types-data (3.2022.0105)
16
- multi_xml (0.6.0)
16
+ mini_mime (1.1.5)
17
+ multi_xml (0.9.1)
18
+ bigdecimal (>= 3.1, < 5)
17
19
  rake (12.3.3)
18
20
 
19
21
  PLATFORMS
data/README.md CHANGED
@@ -18,6 +18,228 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install bannerbear
20
20
 
21
+ ## V5 API
22
+
23
+ The [V5 API](https://developers.bannerbear.com/v5/) is a new generation of the Bannerbear API. **V5 API keys do not work with V2 endpoints, and V2 API keys do not work with V5 endpoints** — you must use the right client class for your key.
24
+
25
+ For the **V5 API**, use `Bannerbear::V5::Client` (this section).
26
+ For the **legacy V2 API**, see [Usage](#usage) below — that section is unchanged.
27
+
28
+ ### Table of Contents
29
+
30
+ - [Authentication (V5)](#authentication-v5)
31
+ - [Account (V5)](#account-v5)
32
+ - [Image Templates (V5)](#image-templates-v5)
33
+ - [Images (V5)](#images-v5)
34
+ - [Batches (V5)](#batches-v5)
35
+ - [Webhooks (V5)](#webhooks-v5)
36
+ - [Instant URLs (V5)](#instant-urls-v5)
37
+
38
+ ### Authentication (V5)
39
+
40
+ ```ruby
41
+ bb = Bannerbear::V5::Client.new("your V5 API key")
42
+ ```
43
+
44
+ Or set `BANNERBEAR_API_KEY` and call without arguments:
45
+
46
+ ```ruby
47
+ bb = Bannerbear::V5::Client.new
48
+ ```
49
+
50
+ ### Account (V5)
51
+
52
+ ```ruby
53
+ bb.account
54
+ ```
55
+
56
+ ### Image Templates (V5)
57
+
58
+ V5 renames V2's `templates` resource to `image_templates`.
59
+
60
+ ```ruby
61
+ bb.list_image_templates(page: 1)
62
+ bb.get_image_template("template uid")
63
+ bb.update_image_template("template uid", name: "New Name", description: "...", tags: ["portrait"])
64
+ ```
65
+
66
+ ### Images (V5)
67
+
68
+ V5's `modifications` is an **object** with two sub-keys:
69
+
70
+ - `template` — template-level changes (width, height, etc.)
71
+ - `objects` — array of per-layer changes (equivalent to V2's flat modifications array)
72
+
73
+ ```ruby
74
+ bb.create_image("template uid",
75
+ modifications: {
76
+ template: { width: 1080, height: 1080 },
77
+ objects: [
78
+ { name: "headline", text: "Hello World!" },
79
+ { name: "photo", image_url: "https://images.unsplash.com/photo-1555400038-63f5ba517a47?w=1000&q=80" }
80
+ ]
81
+ }
82
+ )
83
+ ```
84
+
85
+ Synchronous generation routes to `sync.api.bannerbear.com/v5` (10s timeout). The `sync:` flag is a Ruby-level switch — it is **not** sent in the request body:
86
+
87
+ ```ruby
88
+ bb.create_image("template uid", sync: true, modifications: { objects: [...] })
89
+ ```
90
+
91
+ ##### Options for `create_image`
92
+
93
+ - `modifications`: V5 modifications object (`hash`)
94
+ - `formats`: output formats, e.g. `["jpg", "pdf"]` (`array`)
95
+ - `scale`: scale multiplier, 1–4 (`integer`)
96
+ - `dpi`: DPI metadata (`integer`)
97
+ - `quality`: quality control (`integer`)
98
+ - `proxy`: proxy server for asset fetching (`string`)
99
+ - `metadata`: include any metadata to reference at a later point (`string`)
100
+ - `version`: pin template version (`integer`)
101
+ - `sync`: route to the sync host (`boolean`; Ruby-only, not sent to the API)
102
+
103
+ ```ruby
104
+ bb.get_image("image uid")
105
+ bb.list_images(page: 1)
106
+ ```
107
+
108
+ ### Batches (V5)
109
+
110
+ Generate multiple images in one request (up to 100).
111
+
112
+ ```ruby
113
+ bb.create_batch(
114
+ type: "image",
115
+ items: [
116
+ { template: "template uid 1", modifications: { objects: [...] } },
117
+ { template: "template uid 2", modifications: { objects: [...] } }
118
+ ]
119
+ )
120
+ bb.get_batch("batch uid")
121
+ bb.list_batches(page: 1)
122
+ ```
123
+
124
+ ### Webhooks (V5)
125
+
126
+ Webhooks are managed as a first-class resource in V5 (instead of being a per-request `webhook_url` parameter).
127
+
128
+ ```ruby
129
+ hook = bb.create_webhook(
130
+ name: "my-webhook",
131
+ url: "https://example.com/hook",
132
+ resource: "image",
133
+ event: "completed",
134
+ status: "active",
135
+ scope: "all",
136
+ templates: []
137
+ )
138
+
139
+ # IMPORTANT: signing_key is ONLY returned in the create response. Store it now —
140
+ # subsequent get_webhook calls will not include it.
141
+ puts hook["signing_key"]
142
+ ```
143
+
144
+ CRUD:
145
+
146
+ ```ruby
147
+ bb.get_webhook("webhook uid")
148
+ bb.update_webhook("webhook uid",
149
+ name: "renamed",
150
+ url: "https://example.com/hook",
151
+ resource: "image",
152
+ event: "completed",
153
+ status: "active",
154
+ scope: "all"
155
+ )
156
+ bb.delete_webhook("webhook uid")
157
+ bb.list_webhooks(page: 1)
158
+ ```
159
+
160
+ ### Instant URLs (V5)
161
+
162
+ Instant URLs are URLs bound to a template that can be manipulated with query strings — the V5 equivalent of V2's "Signed URLs" feature.
163
+
164
+ #### Create an Instant URL base
165
+
166
+ ```ruby
167
+ iurl = bb.create_instant_url(
168
+ name: "my-instant-url",
169
+ template: "template uid",
170
+ mode: "encoded", # or "named_params"
171
+ security: "signed", # or "open"
172
+ status: "active",
173
+ scale: 1 # 1, 2, 3, or 4
174
+ )
175
+
176
+ # IMPORTANT: signing_key is ONLY returned in the create response. Store it now.
177
+ puts iurl["signing_key"]
178
+ puts iurl["base_url"]
179
+ ```
180
+
181
+ ##### Options for `create_instant_url` / `update_instant_url`
182
+
183
+ - `name` *required* (`string`)
184
+ - `template` *required* — image template UID (`string`)
185
+ - `mode`: `"encoded"` or `"named_params"` (`string`)
186
+ - `security`: `"signed"` or `"open"` (`string`)
187
+ - `status`: `"active"` or `"disabled"` (`string`)
188
+ - `scale`: 1, 2, 3, or 4 (`integer`)
189
+ - `rate_limit`: enable per-IP rate limiting (`boolean`)
190
+ - `template_version`: pin template version (`integer`, nullable)
191
+ - `max_renders`: cap total renders (`integer`, nullable)
192
+ - `expires_at`: ISO 8601 expiry (`string`, nullable)
193
+
194
+ CRUD:
195
+
196
+ ```ruby
197
+ bb.get_instant_url("uid")
198
+ bb.update_instant_url("uid", name: "...", template: "...", ...)
199
+ bb.delete_instant_url("uid")
200
+ bb.list_instant_urls(page: 1)
201
+ ```
202
+
203
+ #### Build an Instant URL with modifications
204
+
205
+ `build_instant_url` is a pure local helper — no API call. It composes the URL from a base + modifications and, if a signing key is provided, appends the HMAC signature.
206
+
207
+ ```ruby
208
+ # Encoded mode, signed
209
+ bb.build_instant_url(iurl["base_url"],
210
+ mode: "encoded",
211
+ signing_key: iurl["signing_key"],
212
+ modifications: {
213
+ template: { width: 1030, height: 890 },
214
+ objects: [{ name: "title", text: "Hello!", color: "#ffffff" }]
215
+ }
216
+ )
217
+
218
+ # Named params mode, signed
219
+ bb.build_instant_url(iurl["base_url"],
220
+ mode: "named_params",
221
+ signing_key: iurl["signing_key"],
222
+ modifications: {
223
+ template: { width: 1030, height: 890 },
224
+ objects: [{ name: "title", text: "Hello!" }]
225
+ }
226
+ )
227
+
228
+ # Open (unsigned): omit signing_key
229
+ bb.build_instant_url(iurl["base_url"],
230
+ mode: "encoded",
231
+ modifications: { objects: [{ name: "title", text: "Hello!" }] }
232
+ )
233
+ ```
234
+
235
+ ##### Options for `build_instant_url`
236
+
237
+ - `mode`: `"encoded"` (default) or `"named_params"` (`string`)
238
+ - `signing_key`: only needed when the instant URL was created with `security: "signed"` (`string`)
239
+ - `modifications`: same shape as `create_image`'s modifications (`hash`)
240
+
241
+ ---
242
+
21
243
  ## Usage
22
244
 
23
245
  ### Table of Contents
@@ -0,0 +1,199 @@
1
+ module Bannerbear
2
+ module V5
3
+
4
+ class Client
5
+
6
+ def initialize(api_key = nil)
7
+ @api_key = api_key || ENV["BANNERBEAR_API_KEY"]
8
+ end
9
+
10
+ def account
11
+ get_response "/account"
12
+ end
13
+
14
+ # Image Templates
15
+
16
+ def list_image_templates(params = {})
17
+ get_response "/image_templates?#{URI.encode_www_form(params.slice(:page))}"
18
+ end
19
+
20
+ def get_image_template(uid)
21
+ get_response "/image_templates/#{uid}"
22
+ end
23
+
24
+ def update_image_template(uid, payload = {})
25
+ patch_response "/image_templates/#{uid}", payload.slice(:name, :description, :tags)
26
+ end
27
+
28
+ # Images
29
+
30
+ def list_images(params = {})
31
+ get_response "/images?#{URI.encode_www_form(params.slice(:page))}"
32
+ end
33
+
34
+ def get_image(uid)
35
+ get_response "/images/#{uid}"
36
+ end
37
+
38
+ def create_image(uid, payload = {})
39
+ post_response "/images", payload.slice(:modifications, :formats, :scale, :dpi, :quality, :proxy, :metadata, :version).merge({:template => uid}), payload[:sync]
40
+ end
41
+
42
+ # Batches
43
+
44
+ def list_batches(params = {})
45
+ get_response "/batches?#{URI.encode_www_form(params.slice(:page))}"
46
+ end
47
+
48
+ def get_batch(uid)
49
+ get_response "/batches/#{uid}"
50
+ end
51
+
52
+ def create_batch(payload = {})
53
+ post_response "/batches", payload.slice(:type, :items)
54
+ end
55
+
56
+ # Webhooks
57
+
58
+ def list_webhooks(params = {})
59
+ get_response "/webhooks?#{URI.encode_www_form(params.slice(:page))}"
60
+ end
61
+
62
+ def get_webhook(uid)
63
+ get_response "/webhooks/#{uid}"
64
+ end
65
+
66
+ def create_webhook(payload = {})
67
+ post_response "/webhooks", payload.slice(:name, :url, :resource, :event, :status, :scope, :templates)
68
+ end
69
+
70
+ def update_webhook(uid, payload = {})
71
+ patch_response "/webhooks/#{uid}", payload.slice(:name, :url, :resource, :event, :status, :scope, :templates)
72
+ end
73
+
74
+ def delete_webhook(uid)
75
+ delete_response "/webhooks/#{uid}"
76
+ end
77
+
78
+ # Instant URLs
79
+
80
+ def list_instant_urls(params = {})
81
+ get_response "/instant_urls?#{URI.encode_www_form(params.slice(:page))}"
82
+ end
83
+
84
+ def get_instant_url(uid)
85
+ get_response "/instant_urls/#{uid}"
86
+ end
87
+
88
+ def create_instant_url(payload = {})
89
+ post_response "/instant_urls", payload.slice(:name, :template, :mode, :security, :status, :scale, :rate_limit, :template_version, :max_renders, :expires_at)
90
+ end
91
+
92
+ def update_instant_url(uid, payload = {})
93
+ patch_response "/instant_urls/#{uid}", payload.slice(:name, :template, :mode, :security, :status, :scale, :rate_limit, :template_version, :max_renders, :expires_at)
94
+ end
95
+
96
+ def delete_instant_url(uid)
97
+ delete_response "/instant_urls/#{uid}"
98
+ end
99
+
100
+ def build_instant_url(base_url, payload = {})
101
+ modifications = payload[:modifications]
102
+ mode = (payload[:mode] || "encoded").to_s
103
+
104
+ url = case mode
105
+ when "encoded"
106
+ data =
107
+ if modifications.is_a?(Hash) && modifications.keys.map(&:to_s) == ["objects"]
108
+ modifications[:objects] || modifications["objects"]
109
+ else
110
+ modifications
111
+ end
112
+ "#{base_url}?modifications=#{Base64.urlsafe_encode64(data.to_json, padding: false)}"
113
+ when "named_params"
114
+ template = modifications.is_a?(Hash) ? (modifications[:template] || modifications["template"]) : nil
115
+ objects = modifications.is_a?(Hash) ? (modifications[:objects] || modifications["objects"]) : modifications
116
+ parts = []
117
+ (template || {}).each do |k, v|
118
+ parts << "template:#{k}=#{URI.encode_www_form_component(v)}"
119
+ end
120
+ (objects || []).each do |obj|
121
+ name = obj[:name] || obj["name"]
122
+ obj.each do |k, v|
123
+ key = k.to_s
124
+ next if key == "name"
125
+ parts << "#{name}:#{key}=#{URI.encode_www_form_component(v)}"
126
+ end
127
+ end
128
+ "#{base_url}?#{parts.join('&')}"
129
+ else
130
+ raise ArgumentError, "unknown instant URL mode: #{mode.inspect}"
131
+ end
132
+
133
+ signing_key = payload[:signing_key]
134
+ return url if signing_key.nil? || signing_key.empty?
135
+ sig = OpenSSL::HMAC.hexdigest("SHA256", signing_key, url)
136
+ "#{url}&s=#{sig}"
137
+ end
138
+
139
+
140
+ private
141
+
142
+ BB_API_ENDPOINT = "https://api.bannerbear.com/v5"
143
+ BB_API_ENDPOINT_SYNCHRONOUS = "https://sync.api.bannerbear.com/v5"
144
+
145
+ def get_response(url)
146
+ response = HTTParty.get("#{BB_API_ENDPOINT}#{url}", timeout: 3, headers: { 'Authorization' => "Bearer #{@api_key}" })
147
+ body = JSON.parse(response.body)
148
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
149
+ return body
150
+ end
151
+
152
+ def patch_response(url, payload)
153
+ response = HTTParty.patch("#{BB_API_ENDPOINT}#{url}",
154
+ body: payload.to_json,
155
+ timeout: 5,
156
+ headers: {
157
+ 'Authorization' => "Bearer #{@api_key}",
158
+ 'Content-Type' => 'application/json'
159
+ }
160
+ )
161
+ body = JSON.parse(response.body)
162
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
163
+ return body
164
+ end
165
+
166
+ def post_response(url, payload, sync = false)
167
+ endpoint = BB_API_ENDPOINT
168
+ timeout = 5
169
+ if sync == true
170
+ endpoint = BB_API_ENDPOINT_SYNCHRONOUS
171
+ timeout = 10
172
+ end
173
+ response = HTTParty.post("#{endpoint}#{url}",
174
+ body: payload.to_json,
175
+ timeout: timeout,
176
+ headers: {
177
+ 'Authorization' => "Bearer #{@api_key}",
178
+ 'Content-Type' => 'application/json'
179
+ }
180
+ )
181
+ body = JSON.parse(response.body)
182
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
183
+ return body
184
+ end
185
+
186
+ def delete_response(url)
187
+ response = HTTParty.delete("#{BB_API_ENDPOINT}#{url}",
188
+ timeout: 5,
189
+ headers: { 'Authorization' => "Bearer #{@api_key}" }
190
+ )
191
+ body = response.body.to_s.empty? ? {} : JSON.parse(response.body)
192
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
193
+ return {"code" => response.code}
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
@@ -1,3 +1,3 @@
1
1
  module Bannerbear
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/bannerbear.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require "httparty"
2
2
  require "openssl"
3
3
  require "base64"
4
+ require "json"
4
5
  require "bannerbear/version"
5
6
  require "bannerbear/client"
7
+ require "bannerbear/v5/client"
6
8
 
7
9
  module Bannerbear
8
10
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bannerbear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Yongfook
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-20 00:00:00.000000000 Z
11
+ date: 2026-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -43,6 +43,7 @@ files:
43
43
  - bin/setup
44
44
  - lib/bannerbear.rb
45
45
  - lib/bannerbear/client.rb
46
+ - lib/bannerbear/v5/client.rb
46
47
  - lib/bannerbear/version.rb
47
48
  homepage: https://github.com/yongfook/bannerbear-ruby
48
49
  licenses:
@@ -64,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
65
  - !ruby/object:Gem::Version
65
66
  version: '0'
66
67
  requirements: []
67
- rubygems_version: 3.1.6
68
+ rubygems_version: 3.5.22
68
69
  signing_key:
69
70
  specification_version: 4
70
71
  summary: Ruby wrapper for the Bannerbear API