bunny_cdn 1.3.1 → 2.0.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: b9a5a9f040b398baf6c3bbbebec07479c2814b9d19afd0ea4a54925515be7f40
4
- data.tar.gz: da187f28d3fac19b735995b0f9f74b82453995fceba6896cc2be1293644707e9
3
+ metadata.gz: 0336e058f73a0e1d61e658506a8e9f7ff0c477cefa9736d083038076d3946901
4
+ data.tar.gz: c292d19c85e442f43f4c0a381130b3740731d14dc9ef84a9f2bf9fee971d57a2
5
5
  SHA512:
6
- metadata.gz: 15cd8d2c06247609032588ec640af54a07041b194212c486db5a58d2f802c4489f8c3adc19a0065053677b89026858c847a3a752f52ff20a8466687fd7e6a30b
7
- data.tar.gz: 3dda63f9681deb5a5a4c9579112104a1495bf3569bf31364cf7d90b4bd2607d65f97da607c2365f4e076b2999966de67c8b9a80ef0cd42a933cff030d4f7d016
6
+ metadata.gz: 10255e02cf0e24172d8c077b2777b1b6d3e30267f672c19cdf4d8dbc0ebae0715e0880cd4d7d1c1950d1975c195449d0625685948a51b22aad321951c271b513
7
+ data.tar.gz: f51dda17e364d3b4c0d2d147fd8ec2fa990d08b5d8a5e3db374cfa8b7a7abe23f97f7a9826541568bd29db3af7136960b45c351a225025f12250bbd37b33b6e9
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bunny_cdn (2.0.0)
5
+ faraday (>= 2.0, < 3)
6
+ faraday-retry (>= 2.0, < 3)
7
+ json (~> 2.12)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.9.0)
13
+ public_suffix (>= 2.0.2, < 8.0)
14
+ bigdecimal (4.1.2)
15
+ crack (1.0.1)
16
+ bigdecimal
17
+ rexml
18
+ diff-lcs (1.6.2)
19
+ faraday (2.14.2)
20
+ faraday-net_http (>= 2.0, < 3.5)
21
+ json
22
+ logger
23
+ faraday-net_http (3.4.4)
24
+ net-http (~> 0.5)
25
+ faraday-retry (2.4.0)
26
+ faraday (~> 2.0)
27
+ hashdiff (1.2.1)
28
+ json (2.19.9)
29
+ logger (1.7.0)
30
+ net-http (0.9.1)
31
+ uri (>= 0.11.1)
32
+ public_suffix (7.0.5)
33
+ rake (13.4.2)
34
+ rexml (3.4.4)
35
+ rspec (3.13.2)
36
+ rspec-core (~> 3.13.0)
37
+ rspec-expectations (~> 3.13.0)
38
+ rspec-mocks (~> 3.13.0)
39
+ rspec-core (3.13.6)
40
+ rspec-support (~> 3.13.0)
41
+ rspec-expectations (3.13.5)
42
+ diff-lcs (>= 1.2.0, < 2.0)
43
+ rspec-support (~> 3.13.0)
44
+ rspec-mocks (3.13.8)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.13.0)
47
+ rspec-support (3.13.7)
48
+ uri (1.1.1)
49
+ webmock (3.26.2)
50
+ addressable (>= 2.8.0)
51
+ crack (>= 0.3.2)
52
+ hashdiff (>= 0.4.0, < 2.0.0)
53
+
54
+ PLATFORMS
55
+ ruby
56
+ x86_64-linux
57
+
58
+ DEPENDENCIES
59
+ bundler (~> 2.6)
60
+ bunny_cdn!
61
+ rake (~> 13.2, >= 13.2.1)
62
+ rspec (~> 3.13)
63
+ webmock (~> 3.25, >= 3.25.1)
64
+
65
+ CHECKSUMS
66
+ addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
67
+ bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
68
+ bunny_cdn (2.0.0)
69
+ crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
70
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
71
+ faraday (2.14.2) sha256=73ccb9994a9e8648f010e32eca2ae82e41c57860aa10932cda29418b9e0223ad
72
+ faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
73
+ faraday-retry (2.4.0) sha256=7b79c48fb7e56526faf247b12d94a680071ff40c9fda7cf1ec1549439ad11ebe
74
+ hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
75
+ json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
76
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
77
+ net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
78
+ public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
79
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
80
+ rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
81
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
82
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
83
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
84
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
85
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
86
+ uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
87
+ webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
88
+
89
+ BUNDLED WITH
90
+ 2.7.2
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/bunny_cdn.svg)](https://badge.fury.io/rb/bunny_cdn)
2
- [![Codeship Status for brandon-meeks/bunny_cdn](https://app.codeship.com/projects/7f94a660-529a-0138-70bd-36e3badc0e07/status?branch=master)](https://app.codeship.com/projects/390509)
3
2
  [![Maintainability](https://api.codeclimate.com/v1/badges/2cc8e5b9529c32d7473f/maintainability)](https://codeclimate.com/github/brandon-meeks/bunny_cdn/maintainability)
4
3
 
5
4
  # BunnyCdn
6
5
 
7
- This gem allows you to interact with the BunnyCdn API. Currently you can interact with the Storage and Pullzone APIs.
6
+ This gem allows you to interact with the BunnyCDN API via the Pull Zones and Storage APIs.
8
7
 
9
- This does require you to have an account with [BunnyCDN](https://bunnycdn.com/).
8
+ > **⚠️ v2.0 Breaking Change:** This is a complete rewrite. The old class-based global configuration was replaced with an instance-based client. See [Migrating from v1](#migrating-from-v1) below.
9
+
10
+ You need an account with [BunnyCDN](https://bunnycdn.com/).
11
+
12
+ ## Requirements
13
+
14
+ - Ruby >= 3.1.0
10
15
 
11
16
  ## Installation
12
17
 
@@ -24,53 +29,241 @@ Or install it yourself as:
24
29
 
25
30
  $ gem install bunny_cdn
26
31
 
27
- ## Usage
32
+ ## Quick Start
33
+
34
+ ```ruby
35
+ client = BunnyCdn::Client.new(api_key: ENV["BUNNY_API_KEY"])
36
+
37
+ # Pull Zones
38
+ client.pull_zones.list
39
+ client.pull_zones.find(123)
40
+ client.pull_zones.create(name: "my-zone", type: 0, origin_url: "https://example.com")
28
41
 
29
- ### Rails
42
+ # Storage
43
+ client.storage(zone_name: "my-storage-zone").list
44
+ client.storage(zone_name: "my-storage-zone", region: "ny").upload("/path/to/file.txt", "Hello")
45
+ ```
46
+
47
+ ## Client
30
48
 
31
- Create the initializer `config/initializers/bunny_cdn.rb` and set the configuration options.
49
+ Create a client with your BunnyCDN API key:
32
50
 
33
51
  ```ruby
34
- # Valid regions are:
35
- # 'de' for Frankfurt, DE
36
- # 'uk' for London, UK
37
- # 'ny' for New York, US
38
- # 'la' for Los Angeles, US
39
- # 'sg' for Singapore, SG
40
- # 'se' for Stockholm, SE
41
- # 'br' for São Paulo, BR
42
- # 'jh' for Johannesburg, SA
43
- # 'syd' for Sydney, SYD
44
- BunnyCdn.configure do |config|
45
- config.apiKey = # The API key for your BunnyCDN account
46
- config.storageZone = # The storage zone you want to work with
47
- config.region = # The region of the storage zone.
48
- config.accessKey = # The password for your storage zone
52
+ client = BunnyCdn::Client.new(api_key: "your-api-key")
53
+ ```
54
+
55
+ Options:
56
+
57
+ | Option | Default | Description |
58
+ |--------|---------|-------------|
59
+ | `api_key` | (required) | Your BunnyCDN API key |
60
+ | `base_url` | `https://api.bunny.net` | API base URL |
61
+ | `adapter` | `Faraday.default_adapter` | Faraday adapter for HTTP |
62
+
63
+ The client configures Faraday with retry middleware (max 3 retries on timeouts and server errors). All requests set `AccessKey` and `Accept: application/json` headers automatically.
64
+
65
+ ### Resource Accessors
66
+
67
+ ```ruby
68
+ client.pull_zones # => BunnyCdn::Resources::PullZones (memoized)
69
+ client.storage(zone_name: "my-zone") # => BunnyCdn::Resources::Storage
70
+ client.storage(zone_name: "my-zone", region: "ny") # with region override
71
+ ```
72
+
73
+ ## Pull Zones
74
+
75
+ All Pull Zone operations use `client.pull_zones`.
76
+
77
+ ### List
78
+
79
+ ```ruby
80
+ # Default: page 1, 1000 per page
81
+ client.pull_zones.list
82
+
83
+ # Custom pagination
84
+ client.pull_zones.list(page: 2, per_page: 50)
85
+ ```
86
+
87
+ ### Find
88
+
89
+ ```ruby
90
+ client.pull_zones.find(123)
91
+ ```
92
+
93
+ ### Create
94
+
95
+ ```ruby
96
+ client.pull_zones.create(name: "my-zone", type: 0, origin_url: "https://origin.example.com")
97
+ ```
98
+
99
+ ### Delete
100
+
101
+ ```ruby
102
+ client.pull_zones.delete(123)
103
+ ```
104
+
105
+ ### Purge Cache
106
+
107
+ ```ruby
108
+ # Purge entire pull zone cache
109
+ client.pull_zones.purge(123)
110
+
111
+ # Purge by cache tag
112
+ client.pull_zones.purge_by_tag(123, "my-tag")
113
+ ```
114
+
115
+ ### Hostnames
116
+
117
+ ```ruby
118
+ # Add a hostname to a pull zone
119
+ client.pull_zones.add_hostname(123, "cdn.example.com")
120
+
121
+ # If the hostname is already registered, returns { "success" => true, "skipped" => true }
122
+ ```
123
+
124
+ ### SSL
125
+
126
+ ```ruby
127
+ client.pull_zones.load_free_ssl(hostname: "cdn.example.com")
128
+ ```
129
+
130
+ ### Health Check
131
+
132
+ ```ruby
133
+ if client.pull_zones.health_check
134
+ puts "API is healthy"
135
+ else
136
+ puts "API is down"
137
+ end
138
+ ```
139
+
140
+ ## Storage
141
+
142
+ Storage operations target the BunnyCDN storage API. Each call specifies the storage zone name. The connection is routed to the correct regional subdomain automatically.
143
+
144
+ ### Regions
145
+
146
+ Available regions:
147
+
148
+ | Key | Location |
149
+ |-----|----------|
150
+ | `de` | Frankfurt, DE |
151
+ | `uk` | London, UK |
152
+ | `ny` | New York, US |
153
+ | `la` | Los Angeles, US |
154
+ | `sg` | Singapore, SG |
155
+ | `se` | Stockholm, SE |
156
+ | `br` | São Paulo, BR |
157
+ | `jh` | Johannesburg, SA |
158
+ | `syd` | Sydney, SYD |
159
+
160
+ If no region is specified, `de` (Frankfurt) is used.
161
+
162
+ ### List Files
163
+
164
+ ```ruby
165
+ client.storage(zone_name: "my-zone").list # root
166
+ client.storage(zone_name: "my-zone").list(path: "images") # /images/
167
+ ```
168
+
169
+ A trailing slash is appended automatically if missing.
170
+
171
+ ### Download a File
172
+
173
+ ```ruby
174
+ data = client.storage(zone_name: "my-zone").get("images/photo.jpg")
175
+ # Returns raw file body (StringIO / binary string)
176
+ ```
177
+
178
+ ### Upload a File
179
+
180
+ Two approaches:
181
+
182
+ ```ruby
183
+ storage = client.storage(zone_name: "my-zone")
184
+
185
+ # 1. From a file path on disk
186
+ storage.upload("images/photo.jpg", "/local/path/photo.jpg")
187
+
188
+ # 2. From a Rack-style uploaded file (e.g. Rails file input)
189
+ storage.upload("images/photo.jpg", uploaded_file)
190
+ ```
191
+
192
+ The uploaded file must respond to `original_filename` and `tempfile`, or be a String path.
193
+
194
+ ### Delete a File
195
+
196
+ ```ruby
197
+ client.storage(zone_name: "my-zone").delete("images/old-photo.jpg")
198
+ ```
199
+
200
+ ## Error Handling
201
+
202
+ All API errors raise `BunnyCdn::ApiError`:
203
+
204
+ ```ruby
205
+ begin
206
+ client.pull_zones.find(999)
207
+ rescue BunnyCdn::ApiError => e
208
+ puts e.message # "[BunnyCdn] pullzone/999 failed: 404 Not Found"
209
+ puts e.status # 404
210
+ puts e.response # parsed JSON or raw string body
49
211
  end
50
212
  ```
51
213
 
52
- ### File Uploads
214
+ ## Migrating from v1
215
+
216
+ v2 replaces the global class-based API with an instance-based client.
53
217
 
54
- New to v1.2.0, there are now two methods for handling file uploads.
218
+ ### Before (v1)
55
219
 
56
- To upload a file from a file input on a form, simply use
57
220
  ```ruby
58
- BunnyCdn::Storage.uploadFormFile(path, file)
221
+ BunnyCdn.configure do |config|
222
+ config.apiKey = ENV["BUNNY_API_KEY"]
223
+ config.storageZone = "my-zone"
224
+ config.region = "de"
225
+ end
226
+
227
+ BunnyCdn::Storage.uploadFile("path/to/file.txt", file_data)
228
+ BunnyCdn::PullZone.list
59
229
  ```
60
230
 
61
- To upload a file to BunnyCDN that already exists on the system, or to upload a file as a secondary option to storing it on the system use
231
+ ### After (v2)
232
+
62
233
  ```ruby
63
- BunnyCdn::Storage.uploadFile(path, file)
234
+ client = BunnyCdn::Client.new(api_key: ENV["BUNNY_API_KEY"])
235
+
236
+ # Storage — zone and region are per-call, not global
237
+ client.storage(zone_name: "my-zone").upload("path/to/file.txt", file_data)
238
+
239
+ # Pull Zones — single client covers all operations
240
+ client.pull_zones.list
64
241
  ```
242
+
243
+ ### Method Mapping
244
+
245
+ | v1 | v2 |
246
+ |----|----|
247
+ | `BunnyCdn.configure { ... }` | `BunnyCdn::Client.new(api_key: ...)` |
248
+ | `BunnyCdn::PullZone.list` | `client.pull_zones.list` |
249
+ | `BunnyCdn::PullZone.find(id)` | `client.pull_zones.find(id)` |
250
+ | `BunnyCdn::PullZone.create(...)` | `client.pull_zones.create(...)` |
251
+ | `BunnyCdn::PullZone.delete(id)` | `client.pull_zones.delete(id)` |
252
+ | `BunnyCdn::Storage.uploadFile(...)` | `client.storage(...).upload(...)` |
253
+ | `BunnyCdn::Storage.uploadFormFile(...)` | `client.storage(...).upload(...)` |
254
+ | `BunnyCdn::Storage.list(...)` | `client.storage(...).list(...)` |
255
+ | `BunnyCdn::Storage.get(...)` | `client.storage(...).get(...)` |
256
+ | `BunnyCdn::Storage.delete(...)` | `client.storage(...).delete(...)` |
257
+
65
258
  ## Development
66
259
 
67
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
260
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt.
68
261
 
69
262
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
70
263
 
71
264
  ## Contributing
72
265
 
73
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bunny_cdn. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
266
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bunny_cdn.
74
267
 
75
268
  ## License
76
269
 
@@ -78,4 +271,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
78
271
 
79
272
  ## Code of Conduct
80
273
 
81
- Everyone interacting in the BunnyCdn projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/bunny_cdn/blob/master/CODE_OF_CONDUCT.md).
274
+ Everyone interacting in the BunnyCdn project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/bunny_cdn/blob/master/CODE_OF_CONDUCT.md).
data/bunny_cdn.gemspec CHANGED
@@ -8,19 +8,15 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = "Brandon Meeks"
9
9
  spec.email = "meeksb86@gmail.com"
10
10
 
11
- spec.summary = %q{Gem to work with BunnyCDN}
12
- spec.description = %q{This is a simple gem to help you work with BunnyCDN.}
11
+ spec.summary = %q{Modern Ruby client for Bunny.net CDN API}
12
+ spec.description = %q{Instance-based Faraday client for Bunny.net Storage and Pullzone APIs.}
13
13
  spec.homepage = "https://github.com/brandon-meeks/bunny_cdn_gem"
14
14
  spec.license = "MIT"
15
15
 
16
- # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
17
-
18
16
  spec.metadata["homepage_uri"] = spec.homepage
19
17
  spec.metadata["source_code_uri"] = "https://github.com/brandon-meeks/bunny_cdn_gem"
20
18
  spec.metadata["changelog_uri"] = "https://github.com/brandon-meeks/bunny_cdn/releases"
21
19
 
22
- # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
20
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
25
21
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
22
  end
@@ -28,13 +24,14 @@ Gem::Specification.new do |spec|
28
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
25
  spec.require_paths = ["lib"]
30
26
 
31
- spec.add_dependency 'rest-client', '~> 2.1'
32
- spec.add_dependency 'json', '~> 2.12'
27
+ spec.add_dependency "faraday", ">= 2.0", "< 3"
28
+ spec.add_dependency "faraday-retry", ">= 2.0", "< 3"
29
+ spec.add_dependency "json", "~> 2.12"
33
30
 
34
- spec.add_development_dependency "bundler", '~> 2.6.9'
35
- spec.add_development_dependency "rake", '~> 13.2', '>= 13.2.1'
36
- spec.add_development_dependency "rspec", '~> 3.13'
37
- spec.add_development_dependency "webmock", '~> 3.25', '>= 3.25.1'
31
+ spec.add_development_dependency "bundler", "~> 2.6"
32
+ spec.add_development_dependency "rake", "~> 13.2", ">= 13.2.1"
33
+ spec.add_development_dependency "rspec", "~> 3.13"
34
+ spec.add_development_dependency "webmock", "~> 3.25", ">= 3.25.1"
38
35
 
39
- spec.required_ruby_version = '>= 3.0.0'
36
+ spec.required_ruby_version = ">= 3.1.0"
40
37
  end
@@ -0,0 +1,38 @@
1
+ require "faraday"
2
+
3
+ module BunnyCdn
4
+ class Client
5
+ DEFAULT_BASE_URL = "https://api.bunny.net"
6
+
7
+ attr_reader :api_key, :base_url, :adapter
8
+
9
+ def initialize(api_key:, base_url: DEFAULT_BASE_URL, adapter: Faraday.default_adapter)
10
+ @api_key = api_key
11
+ @base_url = base_url
12
+ @adapter = adapter
13
+ end
14
+
15
+ def pull_zones
16
+ @pull_zones ||= Resources::PullZones.new(self)
17
+ end
18
+
19
+ def storage(zone_name:, region: nil)
20
+ Resources::Storage.new(self, zone_name: zone_name, region: region)
21
+ end
22
+
23
+ def connection
24
+ @connection ||= Faraday.new(url: base_url) do |conn|
25
+ conn.headers["AccessKey"] = api_key
26
+ conn.headers["Accept"] = "application/json"
27
+ conn.request :json
28
+ conn.request :retry,
29
+ max: 3,
30
+ interval: 1,
31
+ interval_randomness: 0.5,
32
+ backoff_factor: 2,
33
+ exceptions: [Faraday::ServerError, Faraday::TimeoutError, Faraday::ConnectionFailed]
34
+ conn.adapter adapter
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ module BunnyCdn
2
+ class ApiError < StandardError
3
+ attr_reader :status, :response
4
+
5
+ def initialize(message, status:, response:)
6
+ super(message)
7
+ @status = status
8
+ @response = response
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ module BunnyCdn
2
+ module Resources
3
+ class Base
4
+ attr_reader :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ protected
11
+
12
+ def connection
13
+ client.connection
14
+ end
15
+
16
+ private
17
+
18
+ def get(path, params = {})
19
+ response = connection.get(path, params)
20
+ handle_response(response, path)
21
+ end
22
+
23
+ def post(path, body = {})
24
+ response = connection.post(path, body)
25
+ handle_response(response, path)
26
+ end
27
+
28
+ def delete(path)
29
+ response = connection.delete(path)
30
+ handle_response(response, path)
31
+ end
32
+
33
+ def handle_response(response, path)
34
+ if response.success?
35
+ parse_body(response.body)
36
+ else
37
+ raise ApiError.new(
38
+ "[BunnyCdn] #{path} failed: #{response.status} #{response.body}",
39
+ status: response.status,
40
+ response: parse_body(response.body)
41
+ )
42
+ end
43
+ end
44
+
45
+ def parse_body(body)
46
+ return body unless body.is_a?(String)
47
+
48
+ if body.empty?
49
+ ""
50
+ else
51
+ JSON.parse(body)
52
+ end
53
+ rescue JSON::ParserError
54
+ body
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,69 @@
1
+ require "cgi"
2
+
3
+ module BunnyCdn
4
+ module Resources
5
+ class PullZones < Base
6
+ def list(page: 1, per_page: 1000)
7
+ get("pullzone?page=#{page}&perPage=#{per_page}")
8
+ end
9
+
10
+ def find(id)
11
+ get("pullzone/#{id.to_i}")
12
+ end
13
+
14
+ def create(name:, type:, origin_url:)
15
+ post("pullzone", {
16
+ name: name,
17
+ type: type,
18
+ originUrl: origin_url
19
+ })
20
+ end
21
+
22
+ def delete(id)
23
+ path = "pullzone/#{id.to_i}"
24
+ response = connection.delete(path)
25
+ handle_response(response, path)
26
+ end
27
+
28
+ def purge(id)
29
+ post("pullzone/#{id.to_i}/purgeCache")
30
+ end
31
+
32
+ def add_hostname(id, hostname)
33
+ post("pullzone/#{id.to_i}/addHostname", {
34
+ "Hostname" => hostname
35
+ })
36
+ rescue ApiError => e
37
+ if hostname_already_registered?(e)
38
+ { "success" => true, "skipped" => true }
39
+ else
40
+ raise
41
+ end
42
+ end
43
+
44
+ def load_free_ssl(hostname:)
45
+ get("pullzone/loadFreeCertificate", { hostname: hostname })
46
+ end
47
+
48
+ def purge_by_tag(id, tag)
49
+ post("pullzone/#{id.to_i}/purgeCache?cacheTag=#{CGI.escape(tag)}", {})
50
+ end
51
+
52
+ def health_check
53
+ get("pullzone", { page: 1, perPage: 1 })
54
+ true
55
+ rescue ApiError
56
+ false
57
+ end
58
+
59
+ private
60
+
61
+ def hostname_already_registered?(error)
62
+ return false unless error.status == 400
63
+ return false unless error.response.is_a?(Hash)
64
+
65
+ error.response["ErrorKey"] == "pullzone.hostname_already_registered"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,66 @@
1
+ module BunnyCdn
2
+ module Resources
3
+ class Storage < Base
4
+ attr_reader :zone_name, :region
5
+
6
+ def initialize(client, zone_name:, region: nil)
7
+ super(client)
8
+ @zone_name = zone_name
9
+ @region = region
10
+ end
11
+
12
+ def list(path: "")
13
+ get(normalize_path(path))
14
+ end
15
+
16
+ def get(remote_path)
17
+ super("#{zone_name}/#{remote_path}")
18
+ end
19
+
20
+ def upload(remote_path, file)
21
+ contents = extract_file_data(file)
22
+ put_request("#{zone_name}/#{remote_path}", contents)
23
+ end
24
+
25
+ def delete(remote_path)
26
+ super("#{zone_name}/#{remote_path}")
27
+ end
28
+
29
+ private
30
+
31
+ def connection
32
+ @storage_connection ||= Faraday.new(url: storage_base_url) do |conn|
33
+ conn.headers["AccessKey"] = client.api_key
34
+ conn.request :json
35
+ conn.adapter Faraday.default_adapter
36
+ end
37
+ end
38
+
39
+ def storage_base_url
40
+ if region.nil? || region == "de"
41
+ "https://storage.bunnycdn.com"
42
+ else
43
+ "https://#{region}.storage.bunnycdn.com"
44
+ end
45
+ end
46
+
47
+ def normalize_path(path)
48
+ path = path.to_s
49
+ path.end_with?("/") || path.empty? ? path : "#{path}/"
50
+ end
51
+
52
+ def extract_file_data(file)
53
+ if file.respond_to?(:original_filename) && file.respond_to?(:tempfile)
54
+ file.tempfile.read
55
+ else
56
+ File.read(file)
57
+ end
58
+ end
59
+
60
+ def put_request(path, body)
61
+ response = connection.put(path, body)
62
+ handle_response(response, path)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module BunnyCdn
2
- VERSION = "1.3.1"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/bunny_cdn.rb CHANGED
@@ -1,9 +1,9 @@
1
- require "rest-client"
1
+ require "faraday"
2
+ require "faraday/retry"
2
3
  require "json"
3
4
  require "bunny_cdn/version"
4
- require_relative "bunny_cdn/configuration"
5
- require_relative "bunny_cdn/storage"
6
- require_relative "bunny_cdn/pullzone"
7
-
8
- module BunnyCdn
9
- end
5
+ require "bunny_cdn/error"
6
+ require "bunny_cdn/client"
7
+ require "bunny_cdn/resources/base"
8
+ require "bunny_cdn/resources/pull_zones"
9
+ require "bunny_cdn/resources/storage"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunny_cdn
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Meeks
@@ -10,19 +10,45 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: rest-client
13
+ name: faraday
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ - - "<"
17
20
  - !ruby/object:Gem::Version
18
- version: '2.1'
21
+ version: '3'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
22
25
  requirements:
23
- - - "~>"
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '2.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '3'
32
+ - !ruby/object:Gem::Dependency
33
+ name: faraday-retry
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '2.0'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '3'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
49
+ - - "<"
24
50
  - !ruby/object:Gem::Version
25
- version: '2.1'
51
+ version: '3'
26
52
  - !ruby/object:Gem::Dependency
27
53
  name: json
28
54
  requirement: !ruby/object:Gem::Requirement
@@ -43,14 +69,14 @@ dependencies:
43
69
  requirements:
44
70
  - - "~>"
45
71
  - !ruby/object:Gem::Version
46
- version: 2.6.9
72
+ version: '2.6'
47
73
  type: :development
48
74
  prerelease: false
49
75
  version_requirements: !ruby/object:Gem::Requirement
50
76
  requirements:
51
77
  - - "~>"
52
78
  - !ruby/object:Gem::Version
53
- version: 2.6.9
79
+ version: '2.6'
54
80
  - !ruby/object:Gem::Dependency
55
81
  name: rake
56
82
  requirement: !ruby/object:Gem::Requirement
@@ -105,7 +131,7 @@ dependencies:
105
131
  - - ">="
106
132
  - !ruby/object:Gem::Version
107
133
  version: 3.25.1
108
- description: This is a simple gem to help you work with BunnyCDN.
134
+ description: Instance-based Faraday client for Bunny.net Storage and Pullzone APIs.
109
135
  email: meeksb86@gmail.com
110
136
  executables: []
111
137
  extensions: []
@@ -116,6 +142,7 @@ files:
116
142
  - ".travis.yml"
117
143
  - CODE_OF_CONDUCT.md
118
144
  - Gemfile
145
+ - Gemfile.lock
119
146
  - LICENSE.txt
120
147
  - README.md
121
148
  - Rakefile
@@ -123,9 +150,11 @@ files:
123
150
  - bin/setup
124
151
  - bunny_cdn.gemspec
125
152
  - lib/bunny_cdn.rb
126
- - lib/bunny_cdn/configuration.rb
127
- - lib/bunny_cdn/pullzone.rb
128
- - lib/bunny_cdn/storage.rb
153
+ - lib/bunny_cdn/client.rb
154
+ - lib/bunny_cdn/error.rb
155
+ - lib/bunny_cdn/resources/base.rb
156
+ - lib/bunny_cdn/resources/pull_zones.rb
157
+ - lib/bunny_cdn/resources/storage.rb
129
158
  - lib/bunny_cdn/version.rb
130
159
  homepage: https://github.com/brandon-meeks/bunny_cdn_gem
131
160
  licenses:
@@ -141,14 +170,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
141
170
  requirements:
142
171
  - - ">="
143
172
  - !ruby/object:Gem::Version
144
- version: 3.0.0
173
+ version: 3.1.0
145
174
  required_rubygems_version: !ruby/object:Gem::Requirement
146
175
  requirements:
147
176
  - - ">="
148
177
  - !ruby/object:Gem::Version
149
178
  version: '0'
150
179
  requirements: []
151
- rubygems_version: 3.6.9
180
+ rubygems_version: 3.7.1
152
181
  specification_version: 4
153
- summary: Gem to work with BunnyCDN
182
+ summary: Modern Ruby client for Bunny.net CDN API
154
183
  test_files: []
@@ -1,25 +0,0 @@
1
- module BunnyCdn
2
- class Configuration
3
- attr_accessor :storageZone, :region, :accessKey, :apiKey
4
-
5
- # Sets the configuration variables upon calling BunnyCdn::Configuration.new
6
- def initialize
7
- @storageZone = nil
8
- @region = nil # Options are: eu, ny, la or sg (Asia)
9
- @accessKey = nil
10
- @apiKey = nil
11
- end
12
- end
13
-
14
- def self.configuration
15
- @configuration ||= Configuration.new
16
- end
17
-
18
- def self.configuration=(config)
19
- @configuration = config
20
- end
21
-
22
- def self.configure
23
- yield(configuration)
24
- end
25
- end
@@ -1,87 +0,0 @@
1
- module BunnyCdn
2
- class Pullzone
3
-
4
- RestClient.log = STDOUT # enables RestClient logging
5
-
6
- BASE_URL = 'https://bunnycdn.com/api/pullzone' # URL to for BunnyCDN's Pullzone API
7
-
8
- # Sets the apiKey to that in configuration
9
- def self.apiKey
10
- @apiKey = BunnyCdn.configuration.apiKey
11
- end
12
-
13
- # Sets the necessary headers to make requests to the BunnyCDN API
14
- def self.headers
15
- {
16
- :accesskey => apiKey,
17
- :accept => 'application/json',
18
- :content_type => 'application/json'
19
- }
20
- end
21
-
22
- # Gets all Pull Zones from BunnyCDN
23
- def self.getAllPullzones
24
- begin
25
- response = RestClient.get(BASE_URL, headers)
26
- rescue RestClient::ExceptionWithResponse => exception
27
- return exception
28
- end
29
- return response
30
- end
31
-
32
- # Creates a new pullzone
33
- # Params:
34
- # +name+:: the name of the new Pull Zone
35
- # +type+:: the pricing type of the pull zone you wish to add. 0 = Standard, 1 = High Volume
36
- # originURL+:: the origin URL where the pull zone files are pulled from.
37
- def self.createPullzone(name, type = 0, originUrl)
38
- values = {
39
- :name => name,
40
- :type => type,
41
- :originUrl => originUrl
42
- }
43
- begin
44
- response = RestClient.post(BASE_URL, values.to_json, headers)
45
- rescue RestClient::ExceptionWithResponse => exception
46
- return exception
47
- end
48
- return response
49
- end
50
-
51
- # Gets the details of the pull zone using the given ID
52
- # Params:
53
- # +id+:: the ID of the Pull Zone to return
54
- def self.getSinglePullzone(id)
55
- begin
56
- response = RestClient.get("#{BASE_URL}/#{id}", headers)
57
- rescue RestClient::ExceptionWithResponse => exception
58
- return exception
59
- end
60
- return response
61
- end
62
-
63
- # Deletes the pull zone using the given ID
64
- # Params:
65
- # +id+:: the ID of the Pull Zone to delete
66
- def self.deletePullzone(id)
67
- begin
68
- response = RestClient.delete("#{BASE_URL}/#{id}", headers)
69
- rescue RestClient::ExceptionWithResponse => exception
70
- return exception
71
- end
72
- return response
73
- end
74
-
75
- # Purges the cache for the Pull Zone using the given ID
76
- # Params:
77
- # +id+:: the ID of the zone which should have the cache purged
78
- def self.purgeCache(id)
79
- begin
80
- response = RestClient.post("#{BASE_URL}/#{id}/purgeCache", {}.to_json, headers)
81
- rescue RestClient::ExceptionWithResponse => exception
82
- return exception
83
- end
84
- return response
85
- end
86
- end
87
- end
@@ -1,117 +0,0 @@
1
- module BunnyCdn
2
- class Storage
3
-
4
- RestClient.log = STDOUT # enables RestClient logging
5
-
6
- # Sets the storage zone as set in configuration
7
- def self.storageZone
8
- BunnyCdn.configuration.storageZone
9
- end
10
-
11
- # Sets the proper URL based on the region set in configuration
12
- def self.set_region_url
13
- # case BunnyCdn.configuration.region
14
- # when nil || 'eu'
15
- # 'https://storage.bunnycdn.com'
16
- # when 'ny'
17
- # 'https://ny.storage.bunnycdn.com'
18
- # when 'la'
19
- # 'https://la.storage.bunnycdn.com'
20
- # when 'sg'
21
- # 'https://sg.storage.bunnycdn.com'
22
- # end
23
- if BunnyCdn.configuration.region.nil? || BunnyCdn.configuration.region == 'de'
24
- 'https://storage.bunnycdn.com'
25
- else
26
- "https://#{BunnyCdn.configuration.region}.storage.bunnycdn.com"
27
- end
28
- end
29
-
30
- # Sets the apiKey to that in configuration
31
- def self.apiKey
32
- BunnyCdn.configuration.accessKey
33
- end
34
-
35
- # Sets the necessary headers to make requests to the BunnyCDN API
36
- def self.headers
37
- {
38
- :accesskey => apiKey
39
- }
40
- end
41
-
42
- # Gets all the files from the storage zone
43
- # Params:
44
- # +path+:: desired path to get files
45
- def self.getZoneFiles(path= '')
46
- begin
47
- response = RestClient.get("#{set_region_url}/#{storageZone}/#{path}", headers)
48
- rescue RestClient::ExceptionWithResponse => exception
49
- return exception
50
- end
51
- return response.body
52
- end
53
-
54
- # Gets a single file from the storage zone
55
- # Params:
56
- # +path+:: desired path to get file
57
- # +file+:: specific file to get from storage zone
58
- def self.getFile(path= '', file)
59
- begin
60
- response = RestClient.get("#{set_region_url}/#{storageZone}/#{path}/#{file}", headers)
61
- rescue RestClient::ExceptionWithResponse => exception
62
- return exception
63
- end
64
- return response.body
65
- end
66
-
67
- # Uploads a file on the system to the storage zone
68
- # Params:
69
- # +path+:: desired path to upload file
70
- # +file+:: specific file to upload to storage zone
71
- def self.uploadFile(path= '', file)
72
- fileName = File.basename(file)
73
- headers = {
74
- :accessKey => apiKey,
75
- :checksum => ''
76
- }
77
- begin
78
- response = RestClient.put("#{set_region_url}/#{storageZone}/#{path}/#{fileName}", File.read(file), headers)
79
- rescue RestClient::ExceptionWithResponse => exception
80
- return exception
81
- end
82
- return response.body
83
- end
84
-
85
- # Uploads a file from a file input to the storage zone
86
- # Params:
87
- # +path+:: desired path to upload file
88
- # +file+:: specific file to upload to storage zone
89
- def self.uploadFormFile(path= '', file)
90
- fileName = file.original_filename
91
- headers = {
92
- :accessKey => apiKey,
93
- :checksum => ''
94
- }
95
- begin
96
- response = RestClient.put("#{set_region_url}/#{storageZone}/#{path}/#{fileName}", File.read(file.tempfile), headers)
97
- rescue RestClient::ExceptionWithResponse => exception
98
- return exception
99
- end
100
- return response.body
101
- end
102
-
103
- # Deletes a file from the storage zone
104
- # Params:
105
- # +path+:: path to delete file from
106
- # +file+:: specific file to delete from storage zone
107
- def self.deleteFile(path= '', file)
108
- begin
109
- response = RestClient.delete("#{set_region_url}/#{storageZone}/#{path}/#{file}", headers)
110
- rescue RestClient::ExceptionWithResponse => exception
111
- return exception
112
- end
113
- return response.body
114
- end
115
-
116
- end
117
- end