checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
---
2
2
SHA256:
3
- metadata.gz: 93312512447e91ad4a4e49d249d0474ce69304fbaed539a49cee2277afdc35f7
4
- data.tar.gz: 40c655960faf06672e9b0720cb9e24bfa2b47d09e87bc6e24f23b7def429f014
3
+ metadata.gz: 9da58bc3e8cf7394296aafbe26d50731d145f1587df2ecd940057c01de31585c
4
+ data.tar.gz: 0b8523d30369c61c1fa5aaf1d749daa9a05c03f36c1a9aa898bb5782e662fa6c
5
5
SHA512:
6
- metadata.gz: 212b1e7f5ec0c274676c6458a940eff14c9df4d2e43d990e1a03833bfa2ece184cb85e002a9eafe879ac6e8001e7750d23ebb07efb10bd8f1c02ae31686c5766
7
- data.tar.gz: c8e62b92827dc64b0833f821f07b526164a1da58cd408dfbe9b597d5c64a38d51ab805359655049a4dc98ce3e2e520f330ddf58f98039cfcb6e04dfdbeea5c2a
6
+ metadata.gz: 8079f41055721191fd999e1b4e948d0061bf5e710503940c07d4125a315c812cda8e5603cc891e50671f5992dbc75ab1e65d6a297c9f6656db0317e85784cc35
7
+ data.tar.gz: 9f6ac470ada76aea32143026ac1b7923b76b0b50775a362759817dd7fc3118b792d6ad36f4dcd2829e3b3faba097f3c1072e01db3d1a9d21ab5c653285421683
data/.gitignore CHANGED
@@ -1,6 +1,14 @@
1
- t/
2
- Gemfile*
3
- *.gem
4
- tags
5
- .byebug_history
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
6
12
.rspec_status
13
+ .covered.db
14
+ .env
data/.travis.yml CHANGED
@@ -2,9 +2,13 @@ language: ruby
2
2
dist: xenial
3
3
cache: bundler
4
4
5
+ addons:
6
+ apt:
7
+ packages:
8
+ - squid
9
+
5
10
matrix:
6
11
include:
7
- - rvm: 2.3
8
12
- rvm: 2.4
9
13
- rvm: 2.5
10
14
- rvm: 2.6
@@ -14,6 +18,12 @@ matrix:
14
18
env: JRUBY_OPTS="--debug -X+O"
15
19
- rvm: truffleruby
16
20
- rvm: ruby-head
21
+ - rvm: 2.6
22
+ env: CLOUDFLARE_PROXY=http://localhost:3128
23
+ before_script: sudo service squid start
24
+ - rvm: jruby-head
25
+ env: JRUBY_OPTS="--debug -X+O" CLOUDFLARE_PROXY=http://localhost:3128
26
+ before_script: sudo service squid start
17
27
allow_failures:
18
28
- rvm: ruby-head
19
29
- rvm: jruby-head
@@ -21,4 +31,5 @@ matrix:
21
31
22
32
env:
23
33
global:
24
- secure: c5yG7N1r9nYuw47Y90jGeoHNvkL59AAC55dtPAhKP+gjXWW8hKbntj3oj+lVkxEqzRpzEQgYZzUElrP+6mJnb20ldclZg03L56243tMtVEcpGOx/MFhnIBkx3kKu1H6ydKKMxieHxjsLQ3vnpcIZ3p0skTQjYbjdu607tjbyg7s=
34
+ - CLOUDFLARE_TEST_ZONE_MANAGEMENT=true
35
+ - secure: c5yG7N1r9nYuw47Y90jGeoHNvkL59AAC55dtPAhKP+gjXWW8hKbntj3oj+lVkxEqzRpzEQgYZzUElrP+6mJnb20ldclZg03L56243tMtVEcpGOx/MFhnIBkx3kKu1H6ydKKMxieHxjsLQ3vnpcIZ3p0skTQjYbjdu607tjbyg7s=
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in cloudflare.gemspec
6
+ gemspec
7
+
8
+ gem 'async-http', '~> 0.48', '>= 0.48.2'
9
+
10
+ group :development do
11
+ gem 'pry'
12
+ gem 'pry-coolline'
13
+ end
14
+
15
+ group :test do
16
+ gem 'coveralls', require: false
17
+ gem 'simplecov'
18
+ gem 'sinatra'
19
+ gem 'webmock'
20
+ end
data/README.md CHANGED
@@ -36,7 +36,7 @@ require 'cloudflare'
36
36
email = ENV['CLOUDFLARE_EMAIL']
37
37
key = ENV['CLOUDFLARE_KEY']
38
38
39
- Cloudflare.connect(key: key, email: email) do
39
+ Cloudflare.connect(key: key, email: email) do |connection|
40
40
# Get all available zones:
41
41
zones = connection.zones
42
42
@@ -63,6 +63,20 @@ Cloudflare.connect(key: key, email: email) do
63
63
end
64
64
```
65
65
66
+ ### Using a Bearer Token
67
+
68
+ You can read more about [bearer tokens here](https://blog.cloudflare.com/api-tokens-general-availability/). This allows you to limit priviledges.
69
+
70
+ ```ruby
71
+ require 'cloudflare'
72
+
73
+ token = 'a_generated_api_token'
74
+
75
+ Cloudflare.connect(token: token) do |connection|
76
+ # ...
77
+ end
78
+ ```
79
+
66
80
## Contributing
67
81
68
82
1. Fork it
@@ -101,6 +115,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
101
115
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
102
116
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
103
117
THE SOFTWARE.
104
-
105
-
106
-
data/cloudflare.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
22
22
spec.required_ruby_version = '>= 2.0.0'
23
23
24
- spec.add_dependency 'async-rest'
24
+ spec.add_dependency 'async-rest', '~> 0.10.0'
25
25
26
26
spec.add_development_dependency 'async-rspec'
27
27
data/lib/cloudflare.rb CHANGED
@@ -25,13 +25,13 @@ require 'async'
25
25
require_relative 'cloudflare/connection'
26
26
27
27
module Cloudflare
28
- DEFAULT_URL = 'https://api.cloudflare.com/client/v4'
28
+ DEFAULT_ENDPOINT = Async::HTTP::Endpoint.parse('https://api.cloudflare.com/client/v4')
29
29
30
- def self.connect(key: nil, email: nil)
31
- representation = Connection.for(DEFAULT_URL)
30
+ def self.connect(endpoint = DEFAULT_ENDPOINT, **auth_info)
31
+ representation = Connection.for(endpoint)
32
32
33
- if key
34
- representation = representation.authenticated(key, email)
33
+ if !auth_info.empty?
34
+ representation = representation.authenticated(**auth_info)
35
35
end
36
36
37
37
return representation unless block_given?
data/lib/cloudflare/accounts.rb CHANGED
@@ -24,28 +24,24 @@
24
24
25
25
require_relative 'representation'
26
26
require_relative 'paginate'
27
+ require_relative 'kv/namespaces'
27
28
28
29
module Cloudflare
29
30
class Account < Representation
31
+ def id
32
+ value[:id]
33
+ end
34
+
35
+ def kv_namespaces
36
+ self.with(KV::Namespaces, path: 'storage/kv/namespaces')
37
+ end
30
38
end
31
-
39
+
32
40
class Accounts < Representation
33
41
include Paginate
34
-
35
- def represent(metadata, attributes)
36
- resource = @resource.with(path: attributes[:id])
42
+
43
+ def representation
44
+ Account
37
-
38
- return Account.new(resource, metadata: metadata, value: attributes)
39
- end
40
-
41
- def create(name)
42
- response = self.post(name: name)
43
-
44
- return represent(response.headers, response.read)
45
- end
46
-
47
- def find_by_id(id)
48
- Zone.new(@resource.with(path: id))
49
45
end
50
46
end
51
47
end
data/lib/cloudflare/connection.rb CHANGED
@@ -28,29 +28,33 @@ require_relative 'user'
28
28
29
29
module Cloudflare
30
30
class Connection < Representation
31
- def authenticated(key, email = nil)
31
+ def authenticated(token: nil, key: nil, email: nil)
32
32
headers = {}
33
33
34
- if email.nil?
35
- headers['X-Auth-User-Service-Key'] = key
36
- else
37
- headers['X-Auth-Key'] = key
38
- headers['X-Auth-Email'] = email
34
+ if token
35
+ headers['Authorization'] = "Bearer #{token}"
36
+ elsif key
37
+ if email
38
+ headers['X-Auth-Key'] = key
39
+ headers['X-Auth-Email'] = email
40
+ else
41
+ headers['X-Auth-User-Service-Key'] = key
42
+ end
39
43
end
40
44
41
- self.class.new(@resource.with(headers: headers))
45
+ self.with(headers: headers)
42
46
end
43
47
44
48
def zones
45
- Zones.new(@resource.with(path: 'zones'))
49
+ self.with(Zones, path: 'zones')
46
50
end
47
51
48
52
def accounts
49
- Accounts.new(@resource.with(path: 'accounts'))
53
+ self.with(Accounts, path: 'accounts')
50
54
end
51
55
52
56
def user
53
- User.new(@resource.with(path: 'user'))
57
+ self.with(User, path: 'user')
54
58
end
55
59
end
56
60
end
data/lib/cloudflare/custom_hostname/ssl_attribute.rb ADDED
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './ssl_attribute/settings'
4
+
5
+ module Cloudflare
6
+ class CustomHostname < Representation
7
+ class SSLAttribute
8
+ def initialize(params)
9
+ @params = params
10
+ end
11
+
12
+ def active?
13
+ status == 'active'
14
+ end
15
+
16
+ def cname
17
+ @params[:cname]
18
+ end
19
+
20
+ def cname_target
21
+ @params[:cname_target]
22
+ end
23
+
24
+ def http_body
25
+ @params[:http_body]
26
+ end
27
+
28
+ def http_url
29
+ @params[:http_url]
30
+ end
31
+
32
+ def method
33
+ @params[:method]
34
+ end
35
+
36
+ def pending_validation?
37
+ status == 'pending_validation'
38
+ end
39
+
40
+ # Wraps the settings hash if it exists or initializes the settings hash and then wraps it
41
+ def settings
42
+ @settings ||= Settings.new(@params[:settings] ||= {})
43
+ end
44
+
45
+ def status
46
+ @params[:status]
47
+ end
48
+
49
+ def to_h
50
+ @params
51
+ end
52
+
53
+ def type
54
+ @params[:type]
55
+ end
56
+
57
+ def validation_errors
58
+ @params[:validation_errors]
59
+ end
60
+ end
61
+ end
62
+ end
data/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb ADDED
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloudflare
4
+ class CustomHostname < Representation
5
+ class SSLAttribute
6
+ class Settings
7
+ def initialize(settings)
8
+ @settings = settings
9
+ end
10
+
11
+ def ciphers
12
+ @settings[:ciphers]
13
+ end
14
+
15
+ def ciphers=(value)
16
+ @settings[:ciphers] = value
17
+ end
18
+
19
+ # This will return the raw value, it is needed because
20
+ # if a value is nil we can't assume that it means it is off
21
+ def http2
22
+ @settings[:http2]
23
+ end
24
+
25
+ # Always coerce into a boolean, if the key is not
26
+ # provided, this value may not be accurate
27
+ def http2?
28
+ http2 == 'on'
29
+ end
30
+
31
+ def http2=(value)
32
+ process_boolean(:http2, value)
33
+ end
34
+
35
+ def min_tls_version
36
+ @settings[:min_tls_version]
37
+ end
38
+
39
+ def min_tls_version=(value)
40
+ @settings[:min_tls_version] = value
41
+ end
42
+
43
+ # This will return the raw value, it is needed because
44
+ # if a value is nil we can't assume that it means it is off
45
+ def tls_1_3
46
+ @settings[:tls_1_3]
47
+ end
48
+
49
+ # Always coerce into a boolean, if the key is not
50
+ # provided, this value may not be accurate
51
+ def tls_1_3?
52
+ tls_1_3 == 'on'
53
+ end
54
+
55
+ def tls_1_3=(value)
56
+ process_boolean(:tls_1_3, value)
57
+ end
58
+
59
+ private
60
+
61
+ def process_boolean(key, value)
62
+ if value.nil?
63
+ @settings.delete(key)
64
+ else
65
+ @settings[key] = !value || value == 'off' ? 'off' : 'on'
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/cloudflare/custom_hostnames.rb ADDED
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This implements the Custom Hostname API
4
+ # https://api.cloudflare.com/#custom-hostname-for-a-zone-properties
5
+
6
+ require_relative 'custom_hostname/ssl_attribute'
7
+ require_relative 'paginate'
8
+ require_relative 'representation'
9
+
10
+ module Cloudflare
11
+ class CustomHostname < Representation
12
+ # Only available if enabled for your zone
13
+ def custom_origin
14
+ value[:custom_origin_server]
15
+ end
16
+
17
+ # Only available if enabled for your zone
18
+ def custom_metadata
19
+ value[:custom_metadata]
20
+ end
21
+
22
+ def hostname
23
+ value[:hostname]
24
+ end
25
+
26
+ def id
27
+ value[:id]
28
+ end
29
+
30
+ def ssl
31
+ @ssl ||= SSLAttribute.new(value[:ssl])
32
+ end
33
+
34
+ # Check if the cert has been validated
35
+ # passing true will send a request to Cloudflare to try to validate the cert
36
+ def ssl_active?(force_update = false)
37
+ send_patch(ssl: { method: ssl.method, type: ssl.type }) if force_update && ssl.pending_validation?
38
+ ssl.active?
39
+ end
40
+
41
+ def update_settings(metadata: nil, origin: nil, ssl: nil)
42
+ attrs = {}
43
+ attrs[:custom_metadata] = metadata if metadata
44
+ attrs[:custom_origin_server] = origin if origin
45
+ attrs[:ssl] = ssl if ssl
46
+
47
+ send_patch(attrs)
48
+ end
49
+
50
+ alias :to_s :hostname
51
+
52
+ private
53
+
54
+ def send_patch(data)
55
+ response = patch(data)
56
+
57
+ @ssl = nil # Kill off our cached version of the ssl object so it will be regenerated from the response
58
+ @value = response.result
59
+ end
60
+ end
61
+
62
+ class CustomHostnames < Representation
63
+ include Paginate
64
+
65
+ def representation
66
+ CustomHostname
67
+ end
68
+
69
+ # initializes a custom hostname object and yields it for customization before saving
70
+ def create(hostname, metadata: nil, origin: nil, ssl: {}, &block)
71
+ attrs = { hostname: hostname, ssl: { method: 'http', type: 'dv' }.merge(ssl) }
72
+ attrs[:custom_metadata] = metadata if metadata
73
+ attrs[:custom_origin_server] = origin if origin
74
+
75
+ represent_message(self.post(attrs))
76
+ end
77
+
78
+ def find_by_hostname(hostname)
79
+ each(hostname: hostname).first
80
+ end
81
+ end
82
+ end
data/lib/cloudflare/dns.rb CHANGED
@@ -34,28 +34,33 @@ module Cloudflare
34
34
@record = record || get.result
35
35
end
36
36
37
- def update_content(content)
37
+ def update_content(content, **options)
38
38
response = put(
39
39
type: @record[:type],
40
40
name: @record[:name],
41
- content: content
41
+ content: content,
42
+ **options
42
43
)
43
-
44
+
44
45
@value = response.result
45
46
end
46
-
47
+
47
48
def type
48
49
value[:type]
49
50
end
50
-
51
+
51
52
def name
52
53
value[:name]
53
54
end
54
-
55
+
55
56
def content
56
57
value[:content]
57
58
end
58
-
59
+
60
+ def proxied
61
+ value[:proxied]
62
+ end
63
+
59
64
def to_s
60
65
"#{@record[:name]} #{@record[:type]} #{@record[:content]}"
61
66
end
@@ -63,29 +68,20 @@ module Cloudflare
63
68
64
69
class Records < Representation
65
70
include Paginate
66
-
71
+
67
72
def representation
68
73
Record
69
74
end
70
-
75
+
71
76
TTL_AUTO = 1
72
77
73
78
def create(type, name, content, **options)
74
- message = self.post(type: type, name: name, content: content, **options)
79
+ represent_message(self.post(type: type, name: name, content: content, **options))
75
-
76
- id = message.result[:id]
77
- resource = @resource.with(path: id)
78
-
79
- return representation.new(resource, metadata: message.headers, value: message.result)
80
80
end
81
-
81
+
82
82
def find_by_name(name)
83
83
each(name: name).first
84
84
end
85
-
86
- def find_by_id(id)
87
- Record.new(@resource.with(path: id))
88
- end
89
85
end
90
86
end
91
87
end
data/lib/cloudflare/firewall.rb CHANGED
@@ -29,15 +29,15 @@ module Cloudflare
29
29
def mode
30
30
value[:mode]
31
31
end
32
-
32
+
33
33
def notes
34
34
value[:notes]
35
35
end
36
-
36
+
37
37
def configuration
38
38
value[:configuration]
39
39
end
40
-
40
+
41
41
def to_s
42
42
"#{configuration[:value]} - #{mode} - #{notes}"
43
43
end
@@ -45,14 +45,14 @@ module Cloudflare
45
45
46
46
class Rules < Representation
47
47
include Paginate
48
-
48
+
49
49
def representation
50
50
Rule
51
51
end
52
-
52
+
53
53
def set(mode, value, notes: nil, target: 'ip')
54
54
notes ||= "cloudflare gem [#{mode}] #{Time.now.strftime('%m/%d/%y')}"
55
-
55
+
56
56
message = self.post({
57
57
mode: mode.to_s,
58
58
notes: notes,
@@ -61,17 +61,10 @@ module Cloudflare
61
61
value: value.to_s,
62
62
}
63
63
})
64
-
65
- id = message.result[:id]
64
+
65
+ represent_message(message)
66
- resource = @resource.with(path: id)
67
-
68
- return representation.new(resource, metadata: message.headers, value: message.result)
69
- end
70
-
71
- def find_by_id(id)
72
- Rule.new(@resource.with(path: id))
73
66
end
74
-
67
+
75
68
def each_by_value(value, &block)
76
69
each(configuration_value: value, &block)
77
70
end
data/lib/cloudflare/kv/namespaces.rb ADDED
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This implements the Worker KV Store API
4
+ # https://api.cloudflare.com/#workers-kv-namespace-properties
5
+
6
+ require_relative '../paginate'
7
+ require_relative '../representation'
8
+
9
+ module Cloudflare
10
+ module KV
11
+ class Key < Representation
12
+ def name
13
+ value[:name]
14
+ end
15
+ end
16
+
17
+ class Keys < Representation
18
+ include Paginate
19
+
20
+ def representation
21
+ Key
22
+ end
23
+ end
24
+
25
+ class Namespace < Representation
26
+ def delete_value(name)
27
+ value_representation(name).delete.success?
28
+ end
29
+
30
+ def id
31
+ value[:id]
32
+ end
33
+
34
+ def keys
35
+ self.with(Keys, path: 'keys')
36
+ end
37
+
38
+ def read_value(name)
39
+ value_representation(name).value
40
+ end
41
+
42
+ def rename(new_title)
43
+ put(title: new_title)
44
+ value[:title] = new_title
45
+ end
46
+
47
+ def title
48
+ value[:title]
49
+ end
50
+
51
+ def write_value(name, value)
52
+ value_representation(name).put(value).success?
53
+ end
54
+
55
+ private
56
+
57
+ def value_representation(name)
58
+ self.with(Representation, path: "values/#{name}")
59
+ end
60
+ end
61
+
62
+ class Namespaces < Representation
63
+ include Paginate
64
+
65
+ def representation
66
+ Namespace
67
+ end
68
+
69
+ def create(title)
70
+ represent_message(post(title: title))
71
+ end
72
+
73
+ def find_by_title(title)
74
+ each.find {|namespace| namespace.title == title }
75
+ end
76
+ end
77
+ end
78
+ end
data/lib/cloudflare/paginate.rb CHANGED
@@ -1,15 +1,15 @@
1
1
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
2
+ #
3
3
# Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
# of this software and associated documentation files (the "Software"), to deal
5
5
# in the Software without restriction, including without limitation the rights
6
6
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
7
# copies of the Software, and to permit persons to whom the Software is
8
8
# furnished to do so, subject to the following conditions:
9
- #
9
+ #
10
10
# The above copyright notice and this permission notice shall be included in
11
11
# all copies or substantial portions of the Software.
12
- #
12
+ #
13
13
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
14
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
15
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,34 +21,32 @@
21
21
module Cloudflare
22
22
module Paginate
23
23
include Enumerable
24
-
24
+
25
- def empty?
26
- self.value.empty?
27
- end
28
-
29
- def representation
30
- Representation
31
- end
32
-
33
25
def each(page: 1, per_page: 50, **parameters)
34
26
return to_enum(:each, page: page, per_page: per_page, **parameters) unless block_given?
35
-
27
+
36
28
while true
37
29
zones = @resource.get(self.class, page: page, per_page: per_page, **parameters)
38
-
30
+
39
31
break if zones.empty?
40
-
32
+
41
33
Array(zones.value).each do |attributes|
42
- resource = @resource.with(path: attributes[:id])
34
+ yield represent(zones.metadata, attributes)
43
-
44
- yield representation.new(resource, metadata: zones.metadata, value: attributes)
45
35
end
46
-
36
+
47
37
page += 1
48
-
38
+
49
39
# Was this the last page?
50
- break if zones.value.count < per_page
40
+ break if zones.value.size < per_page
51
41
end
52
42
end
43
+
44
+ def empty?
45
+ self.value.empty?
46
+ end
47
+
48
+ def find_by_id(id)
49
+ representation.new(@resource.with(path: id))
50
+ end
53
51
end
54
52
end
data/lib/cloudflare/representation.rb CHANGED
@@ -29,62 +29,79 @@ module Cloudflare
29
29
class RequestError < StandardError
30
30
def initialize(resource, errors)
31
31
super("#{resource}: #{errors.map{|attributes| attributes[:message]}.join(', ')}")
32
-
32
+
33
33
@representation = representation
34
34
end
35
-
35
+
36
36
attr_reader :representation
37
37
end
38
-
38
+
39
39
class Message
40
40
def initialize(response)
41
41
@response = response
42
42
@body = response.read
43
+
44
+ # Some endpoints return the value instead of a message object (like KV reads)
45
+ @body = { success: true, result: @body } unless @body.is_a?(Hash)
43
46
end
44
-
47
+
45
48
attr :response
46
49
attr :body
47
-
50
+
48
51
def headers
49
52
@response.headers
50
53
end
51
-
54
+
52
55
def result
53
56
@body[:result]
54
57
end
55
-
58
+
56
59
def read
57
60
@body[:result]
58
61
end
59
-
62
+
60
63
def results
61
64
Array(result)
62
65
end
63
-
66
+
64
67
def errors
65
68
@body[:errors]
66
69
end
67
-
70
+
68
71
def messages
69
72
@body[:messages]
70
73
end
71
-
74
+
72
75
def success?
73
76
@body[:success]
74
77
end
75
78
end
76
-
79
+
77
80
class Representation < Async::REST::Representation
78
81
def process_response(*)
79
82
message = Message.new(super)
80
-
83
+
81
84
unless message.success?
82
85
raise RequestError.new(@resource, message.errors)
83
86
end
84
-
87
+
85
88
return message
86
89
end
87
-
90
+
91
+ def representation
92
+ Representation
93
+ end
94
+
95
+ def represent(metadata, attributes)
96
+ resource = @resource.with(path: attributes[:id])
97
+
98
+ representation.new(resource, metadata: metadata, value: attributes)
99
+ end
100
+
101
+ def represent_message(message)
102
+ represent(message.headers, message.result)
103
+ end
104
+
88
105
def to_hash
89
106
if value.is_a?(Hash)
90
107
return value
data/lib/cloudflare/rspec/connection.rb CHANGED
@@ -21,6 +21,8 @@
21
21
# THE SOFTWARE.
22
22
23
23
require 'async/rspec'
24
+ require 'async/http/proxy'
25
+
24
26
require_relative '../../cloudflare'
25
27
26
28
module Cloudflare
@@ -35,10 +37,20 @@ module Cloudflare
35
37
let(:email) {ENV['CLOUDFLARE_EMAIL']}
36
38
let(:key) {ENV['CLOUDFLARE_KEY']}
37
39
38
- let(:connection) {@connection = Cloudflare.connect(key: key, email: email)}
40
+ let(:connection) do
41
+ if proxy_url = ENV['CLOUDFLARE_PROXY']
42
+ proxy_endpoint = Async::HTTP::Endpoint.parse(proxy_url)
43
+ @client = Async::HTTP::Client.new(proxy_endpoint)
44
+ @connection = Cloudflare.connect(@client.proxied_endpoint(DEFAULT_ENDPOINT), key: key, email: email)
45
+ else
46
+ @client = nil
47
+ @connection = Cloudflare.connect(key: key, email: email)
48
+ end
49
+ end
39
50
40
51
after do
41
52
@connection&.close
53
+ @client&.close
42
54
end
43
55
end
44
56
end
data/lib/cloudflare/version.rb CHANGED
@@ -22,5 +22,5 @@
22
22
# THE SOFTWARE.
23
23
24
24
module Cloudflare
25
- VERSION = '4.0.1'
25
+ VERSION = '4.2.0'
26
26
end
data/lib/cloudflare/zones.rb CHANGED
@@ -25,22 +25,27 @@
25
25
require_relative 'representation'
26
26
require_relative 'paginate'
27
27
28
+ require_relative 'custom_hostnames'
28
29
require_relative 'firewall'
29
30
require_relative 'dns'
30
31
require_relative 'logs'
31
32
32
33
module Cloudflare
33
34
class Zone < Representation
35
+ def custom_hostnames
36
+ self.with(CustomHostnames, path: 'custom_hostnames')
37
+ end
38
+
34
39
def dns_records
35
- DNS::Records.new(@resource.with(path: 'dns_records'))
40
+ self.with(DNS::Records, path: 'dns_records')
36
41
end
37
42
38
43
def firewall_rules
39
- Firewall::Rules.new(@resource.with(path: 'firewall/access_rules/rules'))
44
+ self.with(Firewall::Rules, path: 'firewall/access_rules/rules')
40
45
end
41
46
42
47
def logs
43
- Logs::Received.new(@resource.with(path: 'logs/received'))
48
+ self.with(Logs::Received, path: 'logs/received')
44
49
end
45
50
46
51
DEFAULT_PURGE_CACHE_PARAMS = {
@@ -48,9 +53,9 @@ module Cloudflare
48
53
}.freeze
49
54
50
55
def purge_cache(parameters = DEFAULT_PURGE_CACHE_PARAMS)
51
- message = self.with(path: 'purge_cache').post(parameters)
56
+ self.with(Zone, path: 'purge_cache').post(parameters)
52
57
53
- return message.success?
58
+ return self
54
59
end
55
60
56
61
def name
@@ -66,22 +71,13 @@ module Cloudflare
66
71
def representation
67
72
Zone
68
73
end
69
-
74
+
70
75
def create(name, account, jump_start = false)
71
- message = self.post(name: name, account: account.to_hash, jump_start: jump_start)
76
+ represent_message(self.post(name: name, account: account.to_hash, jump_start: jump_start))
72
-
73
- id = message.result[:id]
74
- resource = @resource.with(path: id)
75
-
76
- return representation.new(resource, metadata: message.headers, value: message.result)
77
77
end
78
-
78
+
79
79
def find_by_name(name)
80
80
each(name: name).first
81
81
end
82
-
83
- def find_by_id(id)
84
- Zone.new(@resource.with(path: id))
85
- end
86
82
end
87
83
end
data/spec/cloudflare/accounts_spec.rb ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Cloudflare::Accounts, order: :defined, timeout: 30 do
4
+ include_context Cloudflare::Account
5
+
6
+ before do
7
+ account.id # Force a fetch if it hasn't happened yet
8
+ end
9
+
10
+ it 'can list existing accounts' do
11
+ accounts = connection.accounts.to_a
12
+ expect(accounts.any? {|a| a.id == account.id }).to be true
13
+ end
14
+
15
+ it 'can get a specific account' do
16
+ expect(connection.accounts.find_by_id(account.id).id).to eq account.id
17
+ end
18
+
19
+ it 'can generate a representation for the KV namespace endpoint' do
20
+ ns = connection.accounts.find_by_id(account.id).kv_namespaces
21
+ expect(ns).to be_kind_of(Cloudflare::KV::Namespaces)
22
+ expect(ns.resource.reference.path).to end_with("/#{account.id}/storage/kv/namespaces")
23
+ end
24
+ end
data/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb ADDED
@@ -0,0 +1,54 @@
1
+ RSpec.describe Cloudflare::CustomHostname::SSLAttribute::Settings do
2
+
3
+ subject { described_class.new({}) }
4
+
5
+ it 'has an accessor for ciphers' do
6
+ ciphers = double
7
+ expect(subject.ciphers).to be_nil
8
+ subject.ciphers = ciphers
9
+ expect(subject.ciphers).to be ciphers
10
+ end
11
+
12
+ it 'has a boolean accessor for http2' do
13
+ expect(subject.http2).to be_nil
14
+ expect(subject.http2?).to be false
15
+ subject.http2 = true
16
+ expect(subject.http2).to eq 'on'
17
+ expect(subject.http2?).to be true
18
+ subject.http2 = false
19
+ expect(subject.http2).to eq 'off'
20
+ expect(subject.http2?).to be false
21
+ subject.http2 = 'on'
22
+ expect(subject.http2).to eq 'on'
23
+ expect(subject.http2?).to be true
24
+ subject.http2 = 'off'
25
+ expect(subject.http2).to eq 'off'
26
+ expect(subject.http2?).to be false
27
+ end
28
+
29
+ it 'has an accessor for min_tls_version' do
30
+ tls_version = double
31
+ expect(subject.min_tls_version).to be_nil
32
+ subject.min_tls_version = tls_version
33
+ expect(subject.min_tls_version).to be tls_version
34
+ end
35
+
36
+ it 'has a boolean accessor for tls_1_3' do
37
+ expect(subject.tls_1_3).to be_nil
38
+ expect(subject.tls_1_3?).to be false
39
+ subject.tls_1_3 = true
40
+ expect(subject.tls_1_3).to eq 'on'
41
+ expect(subject.tls_1_3?).to be true
42
+ subject.tls_1_3 = false
43
+ expect(subject.tls_1_3).to eq 'off'
44
+ expect(subject.tls_1_3?).to be false
45
+ subject.tls_1_3 = 'on'
46
+ expect(subject.tls_1_3).to eq 'on'
47
+ expect(subject.tls_1_3?).to be true
48
+ subject.tls_1_3 = 'off'
49
+ expect(subject.tls_1_3).to eq 'off'
50
+ expect(subject.tls_1_3?).to be false
51
+ end
52
+
53
+
54
+ end
data/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb ADDED
@@ -0,0 +1,73 @@
1
+ RSpec.describe Cloudflare::CustomHostname::SSLAttribute do
2
+
3
+ accessors = [:cname, :cname_target, :http_body, :http_url, :method, :status, :type, :validation_errors]
4
+
5
+ let(:original_hash) { {} }
6
+
7
+ subject { described_class.new(original_hash) }
8
+
9
+ accessors.each do |key|
10
+
11
+ it "has an accessor for the #{key} value" do
12
+ test_value = double
13
+ expect(subject.send(key)).to be_nil
14
+ original_hash[key] = test_value
15
+ expect(subject.send(key)).to be test_value
16
+ end
17
+
18
+ end
19
+
20
+ it '#active? returns true when the status is "active" and false otherwise' do
21
+ expect(subject.active?).to be false
22
+ original_hash[:status] = 'initializing'
23
+ expect(subject.active?).to be false
24
+ original_hash[:status] = 'pending_validation'
25
+ expect(subject.active?).to be false
26
+ original_hash[:status] = 'pending_deployment'
27
+ expect(subject.active?).to be false
28
+ original_hash[:status] = 'active'
29
+ expect(subject.active?).to be true
30
+ end
31
+
32
+ it '#pending_validation? returns true when the status is "pending_validation" and false otherwise' do
33
+ expect(subject.pending_validation?).to be false
34
+ original_hash[:status] = 'initializing'
35
+ expect(subject.pending_validation?).to be false
36
+ original_hash[:status] = 'active'
37
+ expect(subject.pending_validation?).to be false
38
+ original_hash[:status] = 'pending_deployment'
39
+ expect(subject.pending_validation?).to be false
40
+ original_hash[:status] = 'pending_validation'
41
+ expect(subject.pending_validation?).to be true
42
+ end
43
+
44
+ describe '#settings' do
45
+
46
+ it 'should return a Settings object' do
47
+ expect(subject.settings).to be_kind_of Cloudflare::CustomHostname::SSLAttribute::Settings
48
+ end
49
+
50
+ it 'initailizes the settings object with the value from the settings key' do
51
+ settings = { min_tls_version: double }
52
+ original_hash[:settings] = settings
53
+ expect(subject.settings.min_tls_version).to be settings[:min_tls_version]
54
+ end
55
+
56
+ it 'initializes the settings object with a new hash if the settings key does not exist' do
57
+ expected_value = double
58
+ expect(original_hash[:settings]).to be_nil
59
+ expect(subject.settings.min_tls_version).to be_nil
60
+ expect(original_hash[:settings]).not_to be_nil
61
+ original_hash[:settings][:min_tls_version] = expected_value
62
+ expect(subject.settings.min_tls_version).to be expected_value
63
+ end
64
+
65
+ it 'updates the stored hash with values set on the settings object' do
66
+ expected_value = double
67
+ expect(subject.settings.min_tls_version).to be_nil
68
+ subject.settings.min_tls_version = expected_value
69
+ expect(original_hash[:settings][:min_tls_version]).to be expected_value
70
+ end
71
+ end
72
+
73
+ end
data/spec/cloudflare/custom_hostnames_spec.rb ADDED
@@ -0,0 +1,213 @@
1
+
2
+ RSpec.xdescribe Cloudflare::CustomHostnames, order: :defined, timeout: 30 do
3
+ include_context Cloudflare::Zone
4
+
5
+ let(:domain) { "www#{ENV['TRAVIS_JOB_ID'] || rand(1..5)}.ourtest.com" }
6
+
7
+ let(:record) { @record = zone.custom_hostnames.create(domain) }
8
+
9
+ let(:custom_origin) do
10
+ id = rand(1...100)
11
+ id += (job_id * 100) if job_id.positive?
12
+ subdomain = "origin-#{id}"
13
+ @dns_record = zone.dns_records.create("A", subdomain, "1.2.3.4") # This needs to exist or the calls will fail
14
+ "#{subdomain}.#{zone.name}"
15
+ end
16
+
17
+ after do
18
+ if defined? @record
19
+ expect(@record.delete).to be_success
20
+ end
21
+
22
+ if defined? @dns_record
23
+ expect(@dns_record.delete).to be_success
24
+ end
25
+ end
26
+
27
+ it 'can create a custom hostname record' do
28
+ expect(record).to be_kind_of Cloudflare::CustomHostname
29
+ expect(record.custom_metadata).to be_nil
30
+ expect(record.hostname).to eq domain
31
+ expect(record.custom_origin).to be_nil
32
+ expect(record.ssl.method).to eq 'http'
33
+ expect(record.ssl.type).to eq 'dv'
34
+ end
35
+
36
+ it 'can create a custom hostname record with a custom origin' do
37
+ begin
38
+ @record = zone.custom_hostnames.create(domain, origin: custom_origin)
39
+
40
+ expect(@record).to be_kind_of Cloudflare::CustomHostname
41
+ expect(@record.custom_metadata).to be_nil
42
+ expect(@record.hostname).to eq domain
43
+ expect(@record.custom_origin).to eq custom_origin
44
+ expect(@record.ssl.method).to eq 'http'
45
+ expect(@record.ssl.type).to eq 'dv'
46
+ rescue Cloudflare::RequestError => e
47
+ if e.message.include?('custom origin server has not been granted')
48
+ skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7
49
+ else
50
+ raise
51
+ end
52
+ end
53
+ end
54
+
55
+ it 'can create a custom hostname record with different ssl options' do
56
+ @record = zone.custom_hostnames.create(domain, ssl: { method: 'cname' })
57
+
58
+ expect(@record).to be_kind_of Cloudflare::CustomHostname
59
+ expect(@record.custom_metadata).to be_nil
60
+ expect(@record.hostname).to eq domain
61
+ expect(@record.custom_origin).to be_nil
62
+ expect(@record.ssl.method).to eq 'cname'
63
+ expect(@record.ssl.type).to eq 'dv'
64
+ end
65
+
66
+ it 'can create a custom hostname record with additional metadata' do
67
+ metadata = { a: rand(1..10) }
68
+
69
+ begin
70
+ @record = zone.custom_hostnames.create(domain, metadata: metadata)
71
+
72
+ expect(@record).to be_kind_of Cloudflare::CustomHostname
73
+ expect(@record.custom_metadata).to eq metadata
74
+ expect(@record.hostname).to eq domain
75
+ expect(@record.custom_origin).to be_nil
76
+ expect(@record.ssl.method).to eq 'http'
77
+ expect(@record.ssl.type).to eq 'dv'
78
+ rescue Cloudflare::RequestError => e
79
+ if e.message.include?('No custom metadata access has been allocated for this zone')
80
+ skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7
81
+ else
82
+ raise
83
+ end
84
+ end
85
+ end
86
+
87
+ it 'can look up an existing custom hostname by the hostname or id' do
88
+ expect(zone.custom_hostnames.find_by_hostname(record.hostname).id).to eq record.id
89
+ expect(zone.custom_hostnames.find_by_id(record.id).id).to eq record.id
90
+ end
91
+
92
+ context 'with existing record' do
93
+
94
+ it 'returns the hostname when calling #to_s' do
95
+ expect(record.to_s).to eq domain
96
+ end
97
+
98
+ it 'can update metadata' do
99
+ metadata = { c: rand(1..10) }
100
+
101
+ expect(record.custom_metadata).to be_nil
102
+
103
+ begin
104
+ record.update_settings(metadata: metadata)
105
+
106
+ # Make sure the existing object is updated
107
+ expect(record.custom_metadata).to eq metadata
108
+
109
+ # Verify that the server has the changes
110
+ found_record = zone.custom_hostnames.find_by_id(record.id)
111
+
112
+ expect(found_record.custom_metadata).to eq metadata
113
+ expect(found_record.hostname).to eq domain
114
+ expect(found_record.custom_origin).to be_nil
115
+ rescue Cloudflare::RequestError => e
116
+ if e.message.include?('No custom metadata access has been allocated for this zone')
117
+ skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7
118
+ else
119
+ raise
120
+ end
121
+ end
122
+ end
123
+
124
+ it 'can update the custom origin' do
125
+ expect(record.custom_origin).to be_nil
126
+
127
+ begin
128
+ record.update_settings(origin: custom_origin)
129
+
130
+ # Make sure the existing object is updated
131
+ expect(record.custom_origin).to eq custom_origin
132
+
133
+ # Verify that the server has the changes
134
+ found_record = zone.custom_hostnames.find_by_id(record.id)
135
+
136
+ expect(found_record.custom_metadata).to be_nil
137
+ expect(found_record.hostname).to eq domain
138
+ expect(found_record.custom_origin).to eq custom_origin
139
+ rescue Cloudflare::RequestError => e
140
+ if e.message.include?('custom origin server has not been granted')
141
+ skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7
142
+ else
143
+ raise
144
+ end
145
+ end
146
+ end
147
+
148
+ it 'can update ssl information' do
149
+ expect(record.ssl.method).to eq 'http'
150
+
151
+ record.update_settings(ssl: { method: 'cname', type: 'dv' })
152
+
153
+ # Make sure the existing object is updated
154
+ expect(record.ssl.method).to eq 'cname'
155
+
156
+ # Verify that the server has the changes
157
+ found_record = zone.custom_hostnames.find_by_id(record.id)
158
+
159
+ expect(found_record.custom_metadata).to be_nil
160
+ expect(found_record.hostname).to eq domain
161
+ expect(found_record.custom_origin).to be_nil
162
+ expect(found_record.ssl.method).to eq 'cname'
163
+ end
164
+
165
+ context 'has an ssl section' do
166
+
167
+ it 'wraps it in an SSLAttributes object' do
168
+ expect(record.ssl).to be_kind_of Cloudflare::CustomHostname::SSLAttribute
169
+ end
170
+
171
+ it 'has some helpers for commonly used keys' do
172
+ # Make sure our values exist before we check to make sure that they are returned correctly
173
+ expect(record.value[:ssl].values_at(:method, :http_body, :http_url).compact).not_to be_empty
174
+ expect(record.ssl.method).to be record.value[:ssl][:method]
175
+ expect(record.ssl.http_body).to be record.value[:ssl][:http_body]
176
+ expect(record.ssl.http_url).to be record.value[:ssl][:http_url]
177
+ end
178
+
179
+ end
180
+
181
+ describe '#ssl_active?' do
182
+
183
+ it 'returns the result of calling ssl.active?' do
184
+ expected_value = double
185
+ expect(record.ssl).to receive(:active?).and_return(expected_value)
186
+ expect(record).not_to receive(:send_patch)
187
+ expect(record.ssl_active?).to be expected_value
188
+ end
189
+
190
+ it 'returns the result of calling ssl.active? without triggering an update if force_update is true and the ssl is not in the pending_validation state' do
191
+ expected_value = double
192
+ expect(record.ssl).to receive(:active?).and_return(expected_value)
193
+ expect(record.ssl.method).not_to be_nil
194
+ expect(record.ssl.type).not_to be_nil
195
+ expect(record.ssl.pending_validation?).to be false
196
+ expect(record).not_to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type })
197
+ expect(record.ssl_active?(true)).to be expected_value
198
+ end
199
+
200
+ it 'returns the result of calling ssl.active? after triggering an update if force_update is true and the ssl is in the pending_validation state' do
201
+ expected_value = double
202
+ expect(record.ssl).to receive(:active?).and_return(expected_value)
203
+ expect(record.ssl.method).not_to be_nil
204
+ expect(record.ssl.type).not_to be_nil
205
+ record.value[:ssl][:status] = 'pending_validation'
206
+ expect(record).to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type })
207
+ expect(record.ssl_active?(true)).to be expected_value
208
+ end
209
+
210
+ end
211
+
212
+ end
213
+ end
data/spec/cloudflare/dns_spec.rb CHANGED
@@ -6,21 +6,31 @@ RSpec.describe Cloudflare::DNS, order: :defined, timeout: 30 do
6
6
7
7
let(:subdomain) {"www#{ENV['TRAVIS_JOB_ID']}"}
8
8
9
- let(:record) {@record = zone.dns_records.create("A", subdomain, "1.2.3.4")}
10
-
11
9
after do
12
10
if defined? @record
13
11
expect(@record.delete).to be_success
14
12
end
15
13
end
16
14
17
- it "can create dns record" do
18
- expect(record.type).to be == "A"
19
- expect(record.name).to be_start_with subdomain
20
- expect(record.content).to be == "1.2.3.4"
15
+ context "new record" do
16
+ it "can create dns record" do
17
+ @record = zone.dns_records.create("A", subdomain, "1.2.3.4")
18
+ expect(@record.type).to be == "A"
19
+ expect(@record.name).to be_start_with subdomain
20
+ expect(@record.content).to be == "1.2.3.4"
21
+ end
22
+
23
+ it "can create dns record with proxied option" do
24
+ @record = zone.dns_records.create("A", subdomain, "1.2.3.4", proxied: true)
25
+ expect(@record.type).to be == "A"
26
+ expect(@record.name).to be_start_with subdomain
27
+ expect(@record.content).to be == "1.2.3.4"
28
+ expect(@record.proxied).to be_truthy
29
+ end
21
30
end
22
31
23
32
context "with existing record" do
33
+ let(:record) {@record = zone.dns_records.create("A", subdomain, "1.2.3.4")}
24
34
it "can update dns content" do
25
35
record.update_content("4.3.2.1")
26
36
expect(record.content).to be == "4.3.2.1"
@@ -28,5 +38,13 @@ RSpec.describe Cloudflare::DNS, order: :defined, timeout: 30 do
28
38
fetched_record = zone.dns_records.find_by_name(record.name)
29
39
expect(fetched_record.content).to be == record.content
30
40
end
41
+
42
+ it "can update dns content with proxied option" do
43
+ record.update_content("4.3.2.1", proxied: true)
44
+ expect(record.proxied).to be_truthy
45
+
46
+ fetched_record = zone.dns_records.find_by_name(record.name)
47
+ expect(fetched_record.proxied).to be_truthy
48
+ end
31
49
end
32
50
end
data/spec/cloudflare/kv/namespaces_spec.rb ADDED
@@ -0,0 +1,71 @@
1
+
2
+ RSpec.describe Cloudflare::KV::Namespaces, kv_spec: true, order: :defined, timeout: 30 do
3
+ include_context Cloudflare::Account
4
+
5
+ let(:namespace) { @namespace = account.kv_namespaces.create(namespace_title) }
6
+ let(:namespace_title) { "Test NS ##{rand(1..100)}" }
7
+
8
+ after do
9
+ if defined? @namespace
10
+ expect(@namespace.delete).to be_success
11
+ end
12
+ end
13
+
14
+ it 'can create a namespace' do
15
+ expect(namespace).to be_kind_of Cloudflare::KV::Namespace
16
+ expect(namespace.id).not_to be_nil
17
+ expect(namespace.title).to eq namespace_title
18
+ end
19
+
20
+ it 'can find a namespace by title' do
21
+ namespace # Call this so that the namespace gets created
22
+ expect(account.kv_namespaces.find_by_title(namespace_title).id).to eq namespace.id
23
+ end
24
+
25
+ it 'can rename the namespace' do
26
+ new_title = "#{namespace_title}-#{rand(1..100)}"
27
+ namespace.rename(new_title)
28
+ expect(namespace.title).to eq new_title
29
+ expect(account.kv_namespaces.find_by_title(new_title).id).to eq namespace.id
30
+ expect(account.kv_namespaces.find_by_title(namespace_title)).to be_nil
31
+ end
32
+
33
+ it 'can store a key/value, read it back' do
34
+ key = "key-#{rand(1..100)}"
35
+ value = rand(100..999)
36
+ namespace.write_value(key, value)
37
+ expect(account.kv_namespaces.find_by_id(namespace.id).read_value(key)).to eq value.to_s
38
+ end
39
+
40
+ it 'can read a previously stored key' do
41
+ key = "key-#{rand(1..100)}"
42
+ value = rand(100..999)
43
+ expect(account.kv_namespaces.find_by_id(namespace.id).write_value(key, value)).to be true
44
+ expect(namespace.read_value(key)).to eq value.to_s
45
+ end
46
+
47
+ it 'can delete keys' do
48
+ key = "key-#{rand(1..100)}"
49
+ value = rand(100..999)
50
+ expect(namespace.write_value(key, value)).to be true
51
+ expect(namespace.read_value(key)).to eq value.to_s
52
+ expect(namespace.delete_value(key)).to be true
53
+ expect do
54
+ account.kv_namespaces.find_by_id(namespace.id).read_value(key)
55
+ end.to raise_error(Cloudflare::RequestError)
56
+ end
57
+
58
+ it 'can get the keys that exist in the namespace' do
59
+ counter = 0
60
+ keys = Array.new(rand(1..9)) { "key-#{counter += 1}" } # Keep this single digits so ordering works
61
+ keys.each_with_index do |key, i|
62
+ namespace.write_value(key, i)
63
+ end
64
+
65
+ saved_keys = account.kv_namespaces.find_by_id(namespace.id).keys.to_a
66
+ expect(saved_keys.length).to eq keys.length
67
+ saved_keys.each_with_index do |key, i|
68
+ expect(key.name).to eq keys[i]
69
+ end
70
+ end
71
+ end
data/spec/cloudflare/zone_spec.rb CHANGED
@@ -1,23 +1,25 @@
1
1
2
2
RSpec.describe Cloudflare::Zones, order: :defined, timeout: 30 do
3
3
include_context Cloudflare::Zone
4
-
5
- it "can delete existing domain if exists" do
6
- if zone = zones.find_by_name(name)
7
- expect(zone.delete).to be_success
4
+
5
+ if ENV['CLOUDFLARE_TEST_ZONE_MANAGEMENT'] == 'true'
6
+ it "can delete existing domain if exists" do
7
+ if zone = zones.find_by_name(name)
8
+ expect(zone.delete).to be_success
9
+ end
10
+ end
11
+
12
+ it "can create a zone" do
13
+ zone = zones.create(name, account)
14
+ expect(zone.value).to include(:id)
8
15
end
9
16
end
10
-
17
+
11
- it "can create zone" do
12
- zone = zones.create(name, account)
13
- expect(zone.value).to include(:id)
14
- end
15
-
16
18
it "can list zones" do
17
19
matching_zones = zones.select{|zone| zone.name == name}
18
20
expect(matching_zones).to_not be_empty
19
21
end
20
-
22
+
21
23
it "can get zone by name" do
22
24
found_zone = zones.find_by_name(name)
23
25
expect(found_zone.name).to be == name
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ AUTH_EMAIL = ENV['CLOUDFLARE_EMAIL']
4
+ AUTH_KEY = ENV['CLOUDFLARE_KEY']
5
+
6
+ if AUTH_EMAIL.nil? || AUTH_EMAIL.empty? || AUTH_KEY.nil? || AUTH_KEY.empty?
7
+ puts 'Please make sure you have defined CLOUDFLARE_EMAIL and CLOUDFLARE_KEY in your environment'
8
+ puts 'You can also specify CLOUDFLARE_ZONE_NAME to test with your own zone and'
9
+ puts 'CLOUDFLARE_ACCOUNT_ID to use a specific account'
10
+ exit(1)
11
+ end
12
+
13
+ ACCOUNT_ID = ENV['CLOUDFLARE_ACCOUNT_ID']
14
+ NAMES = ['testing', 'horse', 'cat', 'dog', 'fish', 'dolphin', 'lion', 'tiger'].freeze
15
+ JOB_ID = ENV.fetch('TRAVIS_JOB_ID', 0).to_i
16
+ ZONE_NAME = ENV['CLOUDFLARE_ZONE_NAME'] || "#{NAMES[JOB_ID % NAMES.size]}.com"
1
17
2
18
require 'covered/rspec'
3
19
require 'async/rspec'
@@ -5,18 +21,30 @@ require 'async/rspec'
5
21
require 'cloudflare/rspec/connection'
6
22
require 'cloudflare/zones'
7
23
8
- RSpec.shared_context Cloudflare::Zone do
24
+ RSpec.shared_context Cloudflare::Account do
9
25
include_context Cloudflare::RSpec::Connection
10
-
11
- let(:job_id) {ENV.fetch('TRAVIS_JOB_ID', 0).to_i}
12
- let(:names) {['testing', 'horse', 'cat', 'dog', 'fish', 'dolphin', 'lion', 'tiger']}
13
- let(:name) {"#{names[job_id % names.size]}.com"}
14
-
15
- let(:account) {connection.accounts.first}
26
+
27
+ let(:account) do
28
+ if ACCOUNT_ID
29
+ connection.accounts.find_by_id(ACCOUNT_ID)
30
+ else
31
+ connection.accounts.first
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ RSpec.shared_context Cloudflare::Zone do
38
+ include_context Cloudflare::Account
39
+
40
+ let(:job_id) { JOB_ID }
41
+ let(:names) { NAMES.dup }
42
+ let(:name) { ZONE_NAME.dup }
43
+
16
44
let(:zones) {connection.zones}
17
-
45
+
18
46
let(:zone) {@zone = zones.find_by_name(name) || zones.create(name, account)}
19
-
47
+
20
48
# after do
21
49
# if defined? @zone
22
50
# @zone.delete
@@ -31,4 +59,27 @@ RSpec.configure do |config|
31
59
config.expect_with :rspec do |c|
32
60
c.syntax = :expect
33
61
end
62
+
63
+ disabled_specs = {}
64
+
65
+ # Check for features the current account has enabled
66
+ Cloudflare.connect(key: AUTH_KEY, email: AUTH_EMAIL) do |conn|
67
+ begin
68
+ account = if ACCOUNT_ID
69
+ conn.accounts.find_by_id(ACCOUNT_ID)
70
+ else
71
+ conn.accounts.first
72
+ end
73
+ account.kv_namespaces.to_a
74
+ rescue Cloudflare::RequestError => e
75
+ if e.message.include?('your account is not entitled')
76
+ puts 'Disabling KV specs due to no access'
77
+ disabled_specs[:kv_spec] = true
78
+ else
79
+ raise
80
+ end
81
+ end
82
+ end
83
+
84
+ config.filter_run_excluding disabled_specs unless disabled_specs.empty?
34
85
end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
--- !ruby/object:Gem::Specification
2
2
name: cloudflare
3
3
version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.2.0
5
5
platform: ruby
6
6
authors:
7
7
- Marcin Prokop
@@ -9,22 +9,22 @@ authors:
9
9
autorequire:
10
10
bindir: bin
11
11
cert_chain: []
12
- date: 2019-02-09 00:00:00.000000000 Z
12
+ date: 2019-11-11 00:00:00.000000000 Z
13
13
dependencies:
14
14
- !ruby/object:Gem::Dependency
15
15
name: async-rest
16
16
requirement: !ruby/object:Gem::Requirement
17
17
requirements:
18
- - - ">="
18
+ - - "~>"
19
19
- !ruby/object:Gem::Version
20
- version: '0'
20
+ version: 0.10.0
21
21
type: :runtime
22
22
prerelease: false
23
23
version_requirements: !ruby/object:Gem::Requirement
24
24
requirements:
25
- - - ">="
25
+ - - "~>"
26
26
- !ruby/object:Gem::Version
27
- version: '0'
27
+ version: 0.10.0
28
28
- !ruby/object:Gem::Dependency
29
29
name: async-rspec
30
30
requirement: !ruby/object:Gem::Requirement
@@ -113,8 +113,12 @@ files:
113
113
- lib/cloudflare.rb
114
114
- lib/cloudflare/accounts.rb
115
115
- lib/cloudflare/connection.rb
116
+ - lib/cloudflare/custom_hostname/ssl_attribute.rb
117
+ - lib/cloudflare/custom_hostname/ssl_attribute/settings.rb
118
+ - lib/cloudflare/custom_hostnames.rb
116
119
- lib/cloudflare/dns.rb
117
120
- lib/cloudflare/firewall.rb
121
+ - lib/cloudflare/kv/namespaces.rb
118
122
- lib/cloudflare/logs.rb
119
123
- lib/cloudflare/paginate.rb
120
124
- lib/cloudflare/representation.rb
@@ -122,8 +126,13 @@ files:
122
126
- lib/cloudflare/user.rb
123
127
- lib/cloudflare/version.rb
124
128
- lib/cloudflare/zones.rb
129
+ - spec/cloudflare/accounts_spec.rb
130
+ - spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb
131
+ - spec/cloudflare/custom_hostname/ssl_attribute_spec.rb
132
+ - spec/cloudflare/custom_hostnames_spec.rb
125
133
- spec/cloudflare/dns_spec.rb
126
134
- spec/cloudflare/firewall_spec.rb
135
+ - spec/cloudflare/kv/namespaces_spec.rb
127
136
- spec/cloudflare/zone_spec.rb
128
137
- spec/spec_helper.rb
129
138
homepage: https://github.com/b4k3r/cloudflare
@@ -145,12 +154,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
154
- !ruby/object:Gem::Version
146
155
version: '0'
147
156
requirements: []
148
- rubygems_version: 3.0.2
157
+ rubygems_version: 3.0.6
149
158
signing_key:
150
159
specification_version: 4
151
160
summary: A Ruby wrapper for the Cloudflare API.
152
161
test_files:
162
+ - spec/cloudflare/accounts_spec.rb
163
+ - spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb
164
+ - spec/cloudflare/custom_hostname/ssl_attribute_spec.rb
165
+ - spec/cloudflare/custom_hostnames_spec.rb
153
166
- spec/cloudflare/dns_spec.rb
154
167
- spec/cloudflare/firewall_spec.rb
168
+ - spec/cloudflare/kv/namespaces_spec.rb
155
169
- spec/cloudflare/zone_spec.rb
156
170
- spec/spec_helper.rb