cloudflare 4.0.1 → 4.1.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: 93312512447e91ad4a4e49d249d0474ce69304fbaed539a49cee2277afdc35f7
4
- data.tar.gz: 40c655960faf06672e9b0720cb9e24bfa2b47d09e87bc6e24f23b7def429f014
3
+ metadata.gz: 8f5bd083595954567afddacb3dccb6ad1dbe614d3d654d6ec5edfb7967d99e19
4
+ data.tar.gz: f1ac3f29decb6108455805f5a24d68cda0ab7a142cd781b0df991ed11014835f
5
5
  SHA512:
6
- metadata.gz: 212b1e7f5ec0c274676c6458a940eff14c9df4d2e43d990e1a03833bfa2ece184cb85e002a9eafe879ac6e8001e7750d23ebb07efb10bd8f1c02ae31686c5766
7
- data.tar.gz: c8e62b92827dc64b0833f821f07b526164a1da58cd408dfbe9b597d5c64a38d51ab805359655049a4dc98ce3e2e520f330ddf58f98039cfcb6e04dfdbeea5c2a
6
+ metadata.gz: 6113d2ffeb66def38c41b35ed739a24f2d8b568596e0fc5623e55c8b49dd17954235e37c0288ccef0aaf0a370c54c8bec5a4321cb5b2bfdc5f461a438184300d
7
+ data.tar.gz: bc5c727f94c5078d0a506d9cf6006b7d46582802d24a3089dbc3a8000be48e28a14d072f7a7a6125f835a14d567cd51f1424f2142dea44e992dafcfaff7d0d29
@@ -21,4 +21,5 @@ matrix:
21
21
 
22
22
  env:
23
23
  global:
24
- secure: c5yG7N1r9nYuw47Y90jGeoHNvkL59AAC55dtPAhKP+gjXWW8hKbntj3oj+lVkxEqzRpzEQgYZzUElrP+6mJnb20ldclZg03L56243tMtVEcpGOx/MFhnIBkx3kKu1H6ydKKMxieHxjsLQ3vnpcIZ3p0skTQjYbjdu607tjbyg7s=
24
+ - CLOUDFLARE_TEST_ZONE_MANAGEMENT=true
25
+ - secure: c5yG7N1r9nYuw47Y90jGeoHNvkL59AAC55dtPAhKP+gjXWW8hKbntj3oj+lVkxEqzRpzEQgYZzUElrP+6mJnb20ldclZg03L56243tMtVEcpGOx/MFhnIBkx3kKu1H6ydKKMxieHxjsLQ3vnpcIZ3p0skTQjYbjdu607tjbyg7s=
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
 
@@ -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
+ KV::Namespaces.new(@resource.with(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])
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))
42
+
43
+ def representation
44
+ Account
49
45
  end
50
46
  end
51
47
  end
@@ -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
@@ -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
@@ -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
@@ -40,22 +40,22 @@ module Cloudflare
40
40
  name: @record[:name],
41
41
  content: content
42
42
  )
43
-
43
+
44
44
  @value = response.result
45
45
  end
46
-
46
+
47
47
  def type
48
48
  value[:type]
49
49
  end
50
-
50
+
51
51
  def name
52
52
  value[:name]
53
53
  end
54
-
54
+
55
55
  def content
56
56
  value[:content]
57
57
  end
58
-
58
+
59
59
  def to_s
60
60
  "#{@record[:name]} #{@record[:type]} #{@record[:content]}"
61
61
  end
@@ -63,29 +63,20 @@ module Cloudflare
63
63
 
64
64
  class Records < Representation
65
65
  include Paginate
66
-
66
+
67
67
  def representation
68
68
  Record
69
69
  end
70
-
70
+
71
71
  TTL_AUTO = 1
72
72
 
73
73
  def create(type, name, content, **options)
74
- 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)
74
+ represent_message(self.post(type: type, name: name, content: content, **options))
80
75
  end
81
-
76
+
82
77
  def find_by_name(name)
83
78
  each(name: name).first
84
79
  end
85
-
86
- def find_by_id(id)
87
- Record.new(@resource.with(path: id))
88
- end
89
80
  end
90
81
  end
91
82
  end
@@ -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]
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))
64
+
65
+ represent_message(message)
73
66
  end
74
-
67
+
75
68
  def each_by_value(value, &block)
76
69
  each(configuration_value: value, &block)
77
70
  end
@@ -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
+ Keys.new(@resource.with(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
+ Representation.new(@resource.with(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
@@ -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
-
25
- def empty?
26
- self.value.empty?
27
- end
28
-
29
- def representation
30
- Representation
31
- end
32
-
24
+
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])
43
-
44
- yield representation.new(resource, metadata: zones.metadata, value: attributes)
34
+ yield represent(zones.metadata, attributes)
45
35
  end
46
-
36
+
47
37
  page += 1
48
-
38
+
49
39
  # Was this the last page?
50
40
  break if zones.value.count < 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
@@ -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
@@ -22,5 +22,5 @@
22
22
  # THE SOFTWARE.
23
23
 
24
24
  module Cloudflare
25
- VERSION = '4.0.1'
25
+ VERSION = '4.1.0'
26
26
  end
@@ -25,12 +25,17 @@
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
+ CustomHostnames.new(@resource.with(path: 'custom_hostnames'))
37
+ end
38
+
34
39
  def dns_records
35
40
  DNS::Records.new(@resource.with(path: 'dns_records'))
36
41
  end
@@ -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
+ Zone.new(@resource.with(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)
72
-
73
- id = message.result[:id]
74
- resource = @resource.with(path: id)
75
-
76
- return representation.new(resource, metadata: message.headers, value: message.result)
76
+ represent_message(self.post(name: name, account: account.to_hash, jump_start: jump_start))
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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,213 @@
1
+
2
+ RSpec.describe 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
@@ -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
@@ -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
-
11
- it "can create zone" do
12
- zone = zones.create(name, account)
13
- expect(zone.value).to include(:id)
14
- end
15
-
17
+
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
@@ -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 permitted')
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.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Prokop
@@ -9,7 +9,7 @@ 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-04-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: async-rest
@@ -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.3
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