glare 0.3.0 → 0.4.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
  SHA1:
3
- metadata.gz: e8578b25f97445a55d9f6c54dbdf8c5dc261fac5
4
- data.tar.gz: eac8475ba924ae0aaaefccfa4cd356fe654be477
3
+ metadata.gz: a704d9f5af8df8ee5a03f7e2c73ecdcca75eabc2
4
+ data.tar.gz: a4e53971ad770c1239f06bddb568b366e4cfb736
5
5
  SHA512:
6
- metadata.gz: 915652c5f9414bc54ecd1ed031973c3c8466bbf5412f1806a312171057f4e1e3c22ab4c4ce2fdee48bcaa4e42fd40d432f611c760d9274bf27970069ee36f378
7
- data.tar.gz: 322574e10153826cf4442a4f8946986439ddc1ac2c4b2d888f35452255c49f4147c24457b69e3b29d30949aee1943b519e9fa82be69220219859bda523c8f8e2
6
+ metadata.gz: d69f25042bf3de9da4028ba361ad1ab7deaf82f651c1bbf671c59d71fb8931cc831d73bb3f55b3f580ad08d9db58969e408bfcda2d41065160fee1d64d495d04
7
+ data.tar.gz: eae5c856c6dd64a9f4ae4e7e21f0fdea8aa082beff6a4baf73d7036c2338780353f42ccb27767fc82696a4fc69f2051fff523b4fc4aaab5425186ac55a24050d
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  .envrc
11
11
  /spec/examples.txt
12
+ /.ruby-version
@@ -6,6 +6,7 @@ require 'glare/domain'
6
6
  require 'glare/api_response'
7
7
  require 'glare/dns_record'
8
8
  require 'glare/cf_dns_records'
9
+ require 'glare/errors'
9
10
 
10
11
  module Glare
11
12
  class << self
@@ -1,3 +1,5 @@
1
+ require 'glare/errors'
2
+
1
3
  module Glare
2
4
  class ApiResponse
3
5
  def initialize(response)
@@ -8,11 +10,23 @@ module Glare
8
10
  content['result']
9
11
  end
10
12
 
13
+ def valid!
14
+ raise Glare::Errors::ApiError.new(errors) unless success?
15
+ self
16
+ end
17
+
11
18
  private
12
19
 
20
+ def success?
21
+ content['success']
22
+ end
23
+
13
24
  def content
14
25
  @response.content
15
26
  end
27
+
28
+ def errors
29
+ content['errors'].map { |e| e['message'] }.join(',')
30
+ end
16
31
  end
17
- private_constant :ApiResponse
18
32
  end
@@ -0,0 +1,29 @@
1
+ module Glare
2
+ class CfDnsRecord
3
+ include Comparable
4
+
5
+ def initialize(id:, name:, type:, content:)
6
+ @id = id
7
+ @name = name
8
+ @type = type
9
+ @content = content
10
+ end
11
+
12
+ def <=>(cf_dns_record)
13
+ @type <=> cf_dns_record.type &&
14
+ @name <=> cf_dns_record.name &&
15
+ @content <=> cf_dns_record.content
16
+ end
17
+
18
+ def to_h
19
+ {
20
+ type: @type,
21
+ name: @name,
22
+ content: @content
23
+ }
24
+ end
25
+
26
+ attr_reader :id, :name, :type
27
+ attr_accessor :content
28
+ end
29
+ end
@@ -1,19 +1,11 @@
1
- module Glare
2
- class CfDnsRecord
3
- def initialize(id:, name:, type:, content:)
4
- @id = id
5
- @name = name
6
- @type = type
7
- @content = content
8
- end
9
- attr_reader :id, :name, :type, :content
10
- end
1
+ require 'glare/cf_dns_record'
2
+ require 'glare/cf_dns_records/updater'
11
3
 
4
+ module Glare
12
5
  class CfDnsRecords
13
6
  class << self
14
- def from_result(api_result)
15
- response = ApiResponse.new(api_result)
16
- result = response.result
7
+ def from_result(api_response)
8
+ result = api_response.result
17
9
 
18
10
  records = result.map do |item|
19
11
  CfDnsRecord.new(
@@ -36,14 +28,12 @@ module Glare
36
28
  @records = records
37
29
  end
38
30
 
39
- def to_update(desired_records)
40
- @records.reject do |record|
41
- desired_records.any? { |r| r.content == record.content }
42
- end
31
+ def calculate(desired_records)
32
+ Updater.new(@records.dup, desired_records.dup).calculate
43
33
  end
44
34
 
45
- def count
46
- @records.count
35
+ def dup
36
+ CfDnsRecords.new(@records.dup)
47
37
  end
48
38
 
49
39
  def contents
@@ -54,15 +44,16 @@ module Glare
54
44
  @records.each { |record| yield(record) }
55
45
  end
56
46
 
57
- def to_delete(target_number)
58
- records_to_delete = count - target_number
59
- return CfDnsRecords.empty if records_to_delete < 0
47
+ def map
48
+ @records.map { |record| yield(record) }
49
+ end
60
50
 
61
- @records.last(records_to_delete)
51
+ def any?
52
+ @records.any? { |record| yield(record) }
62
53
  end
63
54
 
64
- def to_create(desired_records)
65
- desired_records.drop(count)
55
+ def delete_if
56
+ @records.delete_if { |record| yield(record) }
66
57
  end
67
58
  end
68
59
  end
@@ -0,0 +1,107 @@
1
+ module Glare
2
+ class CfDnsRecords
3
+ class Updater
4
+ class Operations
5
+ def initialize
6
+ @updates = []
7
+ @insertions = []
8
+ @deletions = []
9
+ @count = 0
10
+ end
11
+
12
+ attr_reader :updates, :insertions, :deletions, :count
13
+
14
+ def add_updates(updates)
15
+ @count += updates.count
16
+ @updates += updates
17
+ end
18
+
19
+ def add_insertions(insertions)
20
+ @count += insertions.count
21
+ @insertions += insertions
22
+ end
23
+
24
+ def add_deletions(deletions)
25
+ @count += deletions.count
26
+ @deletions += deletions
27
+ end
28
+ end
29
+
30
+ class Operation
31
+ include Comparable
32
+
33
+ def initialize(record, action)
34
+ @record = record.dup
35
+ @action = action
36
+ end
37
+
38
+ def <=>(operation)
39
+ @record <=> operation.record &&
40
+ @action <=> operation.action
41
+ end
42
+
43
+ attr_reader :action, :record
44
+ end
45
+
46
+ def initialize(current_records, new_contents)
47
+ @current_records = current_records.dup
48
+ @new_contents = new_contents.dup
49
+ end
50
+
51
+ def calculate
52
+ drop_same_records
53
+
54
+ operations = Operations.new
55
+ operations.add_updates(updated_records)
56
+ operations.add_insertions(new_records)
57
+ operations.add_deletions(deleted_records)
58
+
59
+ operations
60
+ end
61
+
62
+ private
63
+
64
+ def drop_same_records
65
+ new_contents = @new_contents.dup
66
+ current_records = @current_records.dup
67
+
68
+ @new_contents.delete_if do |new_content|
69
+ current_records.any? { |x| x == new_content }
70
+ end
71
+
72
+ @current_records.delete_if do |current_record|
73
+ new_contents.any? { |x| x == current_record }
74
+ end
75
+ end
76
+
77
+ def updated_records
78
+ operations = []
79
+
80
+ @current_records.delete_if do |record|
81
+ if new_record = @new_contents.shift
82
+ final_record = record.dup
83
+ final_record.content = new_record.content
84
+ operations << Operation.new(final_record, :update)
85
+ true
86
+ else
87
+ false
88
+ end
89
+ end
90
+
91
+ operations
92
+ end
93
+
94
+ def new_records
95
+ @new_contents.map do |new_record|
96
+ Operation.new(new_record, :add)
97
+ end
98
+ end
99
+
100
+ def deleted_records
101
+ @current_records.map do |record|
102
+ Operation.new(record, :delete)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -1,4 +1,5 @@
1
1
  require 'jsonclient'
2
+ require 'glare/api_response'
2
3
 
3
4
  module Glare
4
5
  class Client
@@ -14,19 +15,19 @@ module Glare
14
15
  end
15
16
 
16
17
  def get(query, params)
17
- @http.get(BASE_URL + query, params, @headers)
18
+ ApiResponse.new(@http.get(BASE_URL + query, params, @headers)).valid!
18
19
  end
19
20
 
20
21
  def post(query, data)
21
- @http.post(BASE_URL + query, data, @headers)
22
+ ApiResponse.new(@http.post(BASE_URL + query, data, @headers)).valid!
22
23
  end
23
24
 
24
25
  def put(query, data)
25
- @http.put(BASE_URL + query, data, @headers)
26
+ ApiResponse.new(@http.put(BASE_URL + query, data, @headers)).valid!
26
27
  end
27
28
 
28
29
  def delete(query, params=nil)
29
- @http.delete(BASE_URL + query, params, @headers)
30
+ ApiResponse.new(@http.delete(BASE_URL + query, params, @headers)).valid!
30
31
  end
31
32
  end
32
33
  end
@@ -1,5 +1,7 @@
1
1
  module Glare
2
2
  class DnsRecord
3
+ include Comparable
4
+
3
5
  def initialize(name:, type:, content:)
4
6
  @name = name
5
7
  @type = type
@@ -13,7 +15,13 @@ module Glare
13
15
  content: @content
14
16
  }
15
17
  end
16
- attr_reader :content, :type
18
+
19
+ def <=>(dns_record)
20
+ @type <=> dns_record.type &&
21
+ @name <=> dns_record.name &&
22
+ @content <=> dns_record.content
23
+ end
24
+
25
+ attr_reader :content, :type, :name
17
26
  end
18
- private_constant :DnsRecord
19
27
  end
@@ -1,11 +1,11 @@
1
1
  require 'glare/domain/cf_zones'
2
+ require 'glare/errors'
2
3
 
3
4
  module Glare
4
5
  class Domain
5
6
  class CfZones
6
7
  def self.from_result(api_response)
7
- response = ApiResponse.new(api_response)
8
- result = response.result
8
+ result = api_response.result
9
9
 
10
10
  zones = result.map do |item|
11
11
  CfZone.new(
@@ -21,8 +21,9 @@ module Glare
21
21
  @zones = zones
22
22
  end
23
23
 
24
- def first
25
- @zones.first
24
+ def first_id
25
+ raise ::Glare::Errors::NotExistingZoneError.new if @zones.empty?
26
+ @zones.first.id
26
27
  end
27
28
  end
28
29
 
@@ -26,34 +26,27 @@ module Glare
26
26
  end
27
27
 
28
28
  def update(zone_id, dns_records, existing_records)
29
- update_current_records(zone_id, dns_records, existing_records)
30
- delete_uneeded_records(zone_id, dns_records, existing_records)
31
- create_new_records(zone_id, dns_records, existing_records)
29
+ operations = existing_records.calculate(dns_records)
30
+ update_current_records(zone_id, operations.updates)
31
+ delete_uneeded_records(zone_id, operations.deletions)
32
+ create_new_records(zone_id, operations.insertions)
32
33
  end
33
34
 
34
- def update_current_records(zone_id, dns_records, existing_records)
35
- records_to_update = existing_records.to_update(dns_records)
36
- updates = records_to_update.zip(dns_records)
37
- updates.each do |existing_record, dns_record|
38
- @client.put("/zones/#{zone_id}/dns_records/#{existing_record.id}", dns_record.to_h)
35
+ def update_current_records(zone_id, updates)
36
+ updates.each do |update|
37
+ @client.put("/zones/#{zone_id}/dns_records/#{update.record.id}", update.record.to_h)
39
38
  end
40
39
  end
41
40
 
42
- def delete_uneeded_records(zone_id, dns_records, existing_records)
43
- records_to_delete = existing_records.to_delete(dns_records.count)
44
- records_to_delete.each do |record|
45
- @client.delete("/zones/#{zone_id}/dns_records/#{record.id}")
41
+ def delete_uneeded_records(zone_id, deletions)
42
+ deletions.each do |deletion|
43
+ @client.delete("/zones/#{zone_id}/dns_records/#{deletion.record.id}")
46
44
  end
47
45
  end
48
46
 
49
- def create_new_records(zone_id, dns_records, existing_records)
50
- records_to_create = existing_records.to_create(dns_records)
51
- create(zone_id, records_to_create)
52
- end
53
-
54
- def create(zone_id, dns_records)
55
- dns_records.each do |dns_record|
56
- @client.post("/zones/#{zone_id}/dns_records", dns_record.to_h)
47
+ def create_new_records(zone_id, insertions)
48
+ insertions.each do |insertion|
49
+ @client.post("/zones/#{zone_id}/dns_records", insertion.record.to_h)
57
50
  end
58
51
  end
59
52
  end
@@ -16,7 +16,7 @@ module Glare
16
16
  def id
17
17
  return @id if @id
18
18
  zone_search = @client.get('/zones', name: registered_domain)
19
- @id = CfZones.from_result(zone_search).first.id
19
+ @id = CfZones.from_result(zone_search).first_id
20
20
  end
21
21
 
22
22
  private
@@ -0,0 +1,6 @@
1
+ module Glare
2
+ module Errors
3
+ class NotExistingZoneError < StandardError; end
4
+ class ApiError < StandardError; end
5
+ end
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Glare
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -0,0 +1,17 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "code": 1004,
5
+ "error_chain": [
6
+ {
7
+ "code": 9007,
8
+ "message": "Content for CNAME record is invalid"
9
+ }
10
+ ],
11
+ "message": "DNS Validation Error"
12
+ }
13
+ ],
14
+ "messages": [],
15
+ "result": null,
16
+ "success": false
17
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "errors": [],
3
+ "messages": [],
4
+ "result": [
5
+ {
6
+ "content": "another_destination.com",
7
+ "created_on": "2016-05-12T11:53:49.233342Z",
8
+ "id": "b3142498230989gsd0f88h80998908fc",
9
+ "locked": false,
10
+ "meta": {
11
+ "auto_added": false
12
+ },
13
+ "modified_on": "2016-05-12T11:53:49.233342Z",
14
+ "name": "wadus.example.com",
15
+ "proxiable": true,
16
+ "proxied": false,
17
+ "ttl": 1,
18
+ "type": "CNAME",
19
+ "zone_id": "9de4eb694c380d79845d35cd939cc7a7",
20
+ "zone_name": "example.com"
21
+ },
22
+ {
23
+ "content": "destination.com",
24
+ "created_on": "2016-05-12T11:53:49.233342Z",
25
+ "id": "a1f984afe5544840505494298f54c33e",
26
+ "locked": false,
27
+ "meta": {
28
+ "auto_added": false
29
+ },
30
+ "modified_on": "2016-05-12T11:53:49.233342Z",
31
+ "name": "wadus.example.com",
32
+ "proxiable": true,
33
+ "proxied": false,
34
+ "ttl": 1,
35
+ "type": "CNAME",
36
+ "zone_id": "9de4eb694c380d79845d35cd939cc7a7",
37
+ "zone_name": "example.com"
38
+ }
39
+ ],
40
+ "result_info": {
41
+ "count": 2,
42
+ "page": 1,
43
+ "per_page": 20,
44
+ "total_count": 1,
45
+ "total_pages": 1
46
+ },
47
+ "success": true
48
+ }
@@ -5,13 +5,26 @@ RSpec.describe 'Resolve domain' do
5
5
  let(:domain) { 'cname.flywire.cc' }
6
6
  let(:destination) { ['peertransfer.me'] }
7
7
  let(:type) { 'CNAME' }
8
- before do
9
- register_domain(domain, destination)
10
- end
11
8
 
12
9
  it 'resolves to right destination' do
10
+ register_domain(domain, destination)
11
+
13
12
  expect(resolve(domain)).to eq(destination)
14
13
  end
14
+
15
+ it 'raises an exception if domain does not exist in account' do
16
+ register_domain(domain, destination)
17
+
18
+ expect do
19
+ resolve('error.ojete.cc')
20
+ end.to raise_error(Glare::Errors::NotExistingZoneError)
21
+ end
22
+
23
+ it 'raises an exception if api returns error' do
24
+ expect do
25
+ register_domain('error.flywire.cc', '1.1.1.1')
26
+ end.to raise_error(Glare::Errors::ApiError)
27
+ end
15
28
  end
16
29
 
17
30
  context 'when a domain contains more than one destination' do
@@ -95,3 +95,9 @@ RSpec.configure do |config|
95
95
  # as the one that triggered the failure.
96
96
  Kernel.srand config.seed
97
97
  end
98
+
99
+ def load_fixture(fixture)
100
+ fixture_dir = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures'))
101
+ json = IO.read(File.join(fixture_dir, "#{fixture}.json"))
102
+ ::HTTP::Message.new_response(JSON.parse(json))
103
+ end
@@ -0,0 +1,30 @@
1
+ require 'glare/api_response'
2
+ require 'httpclient/http'
3
+
4
+ RSpec.describe Glare::ApiResponse do
5
+ let(:error_response) { load_fixture('error_response') }
6
+ let(:empty_response) { load_fixture('empty_result') }
7
+
8
+ context 'when api returns success response' do
9
+ it 'returns api reponse' do
10
+ expect do
11
+ Glare::ApiResponse.new(empty_response).valid!
12
+ end.not_to raise_error
13
+ end
14
+ end
15
+
16
+ context 'when api returns error response' do
17
+ it 'raises an exception if api result is not success' do
18
+ expect do
19
+ Glare::ApiResponse.new(error_response).valid!
20
+ end.to raise_error(Glare::Errors::ApiError)
21
+ end
22
+
23
+ it 'shows error messages' do
24
+ expect do
25
+ Glare::ApiResponse.new(error_response).valid!
26
+ end.to raise_error(Glare::Errors::ApiError).
27
+ with_message('DNS Validation Error')
28
+ end
29
+ end
30
+ end
@@ -8,9 +8,14 @@ RSpec.describe Glare do
8
8
  allow(Glare::Client).to receive(:new).and_return(client)
9
9
  end
10
10
  let(:client) { spy(Glare::Client) }
11
- let(:zone_list) { load_fixture('list_zone') }
12
- let(:empty_result) { load_fixture('empty_result') }
13
- let(:wadus_records) { load_fixture('wadus_records') }
11
+ let(:zone_list) { Glare::ApiResponse.new(load_fixture('list_zone')) }
12
+ let(:empty_result) { Glare::ApiResponse.new(load_fixture('empty_result')) }
13
+ let(:wadus_records) do
14
+ [
15
+ Glare::ApiResponse.new(load_fixture('wadus_records')),
16
+ Glare::ApiResponse.new(load_fixture('wadus_records_reverse_order'))
17
+ ].sample
18
+ end
14
19
 
15
20
  describe '.resolve' do
16
21
  it 'resolves a fqdn' do
@@ -24,7 +29,7 @@ RSpec.describe Glare do
24
29
  ).and_return(wadus_records)
25
30
 
26
31
  destination = Glare.resolve('wadus.example.com', 'CNAME')
27
- expect(destination).to eq(['destination.com', 'another_destination.com'])
32
+ expect(destination).to match_array(['destination.com', 'another_destination.com'])
28
33
  end
29
34
  end
30
35
 
@@ -41,13 +46,13 @@ RSpec.describe Glare do
41
46
  end
42
47
 
43
48
  it 'uses default credentials' do
44
- Glare.register('example.com', :a_destination, 'CNAME')
49
+ Glare.register('example.com', 'a_destination', 'CNAME')
45
50
 
46
51
  expect(Glare::Client).to have_received(:new).with('an_email', 'an_auth_key')
47
52
  end
48
53
 
49
54
  it 'uses the registration endpoint' do
50
- Glare.register('example.com', :a_destination, 'CNAME')
55
+ Glare.register('example.com', 'a_destination', 'CNAME')
51
56
 
52
57
  expect(client).to have_received(:post) do |*args|
53
58
  expect(args.first).to match(%r{/zones/.*/dns_records})
@@ -55,7 +60,7 @@ RSpec.describe Glare do
55
60
  end
56
61
 
57
62
  it 'retrieves zone id for a given domain name' do
58
- Glare.register('example.com', :a_destination, 'CNAME')
63
+ Glare.register('example.com', 'a_destination', 'CNAME')
59
64
 
60
65
  expect(client).to have_received(:get).
61
66
  with('/zones', name: 'example.com')
@@ -66,7 +71,7 @@ RSpec.describe Glare do
66
71
  end
67
72
 
68
73
  it 'retrieves record to check if exists' do
69
- Glare.register('example.com', :a_destination, 'CNAME')
74
+ Glare.register('example.com', 'a_destination', 'CNAME')
70
75
 
71
76
  expect(client).to have_received(:get).with(
72
77
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records',
@@ -80,14 +85,14 @@ RSpec.describe Glare do
80
85
  name: 'not-exist.example.com', type: 'CNAME'
81
86
  ).and_return(empty_result)
82
87
 
83
- Glare.register('not-exist.example.com', :a_destination, 'CNAME')
88
+ Glare.register('not-exist.example.com', 'a_destination', 'CNAME')
84
89
 
85
90
  expect(client).not_to have_received(:put).
86
91
  with('/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records', any_args)
87
92
 
88
93
  expect(client).to have_received(:post).with(
89
94
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records',
90
- type: 'CNAME', name: 'not-exist.example.com', content: :a_destination
95
+ type: 'CNAME', name: 'not-exist.example.com', content: 'a_destination'
91
96
  )
92
97
  end
93
98
 
@@ -97,19 +102,19 @@ RSpec.describe Glare do
97
102
  name: 'not-exist.example.com', type: 'CNAME'
98
103
  ).and_return(empty_result)
99
104
 
100
- Glare.register('not-exist.example.com', [:a_destination, :another_destination], 'CNAME')
105
+ Glare.register('not-exist.example.com', ['a_destination', 'another_destination'].shuffle, 'CNAME')
101
106
 
102
107
  expect(client).not_to have_received(:put).
103
108
  with('/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records', any_args)
104
109
 
105
110
  expect(client).to have_received(:post).with(
106
111
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records',
107
- type: 'CNAME', name: 'not-exist.example.com', content: :a_destination
112
+ type: 'CNAME', name: 'not-exist.example.com', content: 'a_destination'
108
113
  )
109
114
 
110
115
  expect(client).to have_received(:post).with(
111
116
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records',
112
- type: 'CNAME', name: 'not-exist.example.com', content: :another_destination
117
+ type: 'CNAME', name: 'not-exist.example.com', content: 'another_destination'
113
118
  )
114
119
  end
115
120
 
@@ -122,35 +127,35 @@ RSpec.describe Glare do
122
127
  end
123
128
 
124
129
  context 'same number of records to update' do
125
- context 'records contents are different' do
126
- it 'sends registration data to update endpoint' do
127
- Glare.register('wadus.example.com', ['a_destination.com', 'yet_another_destination.com'], 'CNAME')
130
+ context 'records contents are the same' do
131
+ it 'does not send registration data to update endpoint' do
132
+ Glare.register('wadus.example.com', ['destination.com', 'another_destination.com'].shuffle, 'CNAME')
128
133
 
129
134
  expect(client).not_to have_received(:post).
130
135
  with('/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records', any_args)
131
136
 
132
- expect(client).to have_received(:put).with(
137
+ expect(client).not_to have_received(:put).with(
133
138
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records/a1f984afe5544840505494298f54c33e',
134
- type: 'CNAME', name: 'wadus.example.com', content: 'a_destination.com'
139
+ any_args
135
140
  )
136
141
 
137
- expect(client).to have_received(:put).with(
142
+ expect(client).not_to have_received(:put).with(
138
143
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records/b3142498230989gsd0f88h80998908fc',
139
- type: 'CNAME', name: 'wadus.example.com', content: 'yet_another_destination.com'
144
+ any_args
140
145
  )
141
146
  end
142
147
  end
143
148
 
144
- context 'records contents are the same' do
145
- it 'sends registration data to update endpoint' do
146
- Glare.register('wadus.example.com', ['destination.com', 'another_destination.com'], 'CNAME')
149
+ context 'some records contents are the same' do
150
+ it 'send registration data to update endpoint in different records' do
151
+ Glare.register('wadus.example.com', ['a_destination.com', 'another_destination.com'].shuffle, 'CNAME')
147
152
 
148
153
  expect(client).not_to have_received(:post).
149
154
  with('/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records', any_args)
150
155
 
151
- expect(client).not_to have_received(:put).with(
156
+ expect(client).to have_received(:put).with(
152
157
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records/a1f984afe5544840505494298f54c33e',
153
- any_args
158
+ type: 'CNAME', name: 'wadus.example.com', content: 'a_destination.com'
154
159
  )
155
160
 
156
161
  expect(client).not_to have_received(:put).with(
@@ -159,6 +164,26 @@ RSpec.describe Glare do
159
164
  )
160
165
  end
161
166
  end
167
+
168
+ context 'all records contents are different' do
169
+ it 'sends registration data to update endpoint' do
170
+ Glare.register('wadus.example.com', ['a_destination.com', 'yet_another_destination.com'].shuffle, 'CNAME')
171
+
172
+ expect(client).not_to have_received(:post).
173
+ with('/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records', any_args)
174
+
175
+ expect(client).to have_received(:put).with(
176
+ any_args,
177
+ type: 'CNAME', name: 'wadus.example.com', content: 'a_destination.com'
178
+ )
179
+
180
+ expect(client).to have_received(:put).with(
181
+ any_args,
182
+ type: 'CNAME', name: 'wadus.example.com', content: 'yet_another_destination.com'
183
+ )
184
+ end
185
+ end
186
+
162
187
  end
163
188
 
164
189
  it 'updates different records and deletes extra ones' do
@@ -168,17 +193,15 @@ RSpec.describe Glare do
168
193
  with('/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records', any_args)
169
194
 
170
195
  expect(client).to have_received(:put).with(
171
- '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records/a1f984afe5544840505494298f54c33e',
172
- any_args
196
+ any_args,
197
+ { type: 'CNAME', name: 'wadus.example.com', content: 'a_destination.com' }
173
198
  )
174
199
 
175
- expect(client).to have_received(:delete).with(
176
- '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records/b3142498230989gsd0f88h80998908fc'
177
- )
200
+ expect(client).to have_received(:delete).once
178
201
  end
179
202
 
180
203
  it 'updates different records and creates new ones' do
181
- Glare.register('wadus.example.com', ['destination.com', 'another_destination.com', 'a_third_destination.com'], 'CNAME')
204
+ Glare.register('wadus.example.com', ['destination.com', 'another_destination.com', 'a_third_destination.com'].shuffle, 'CNAME')
182
205
 
183
206
  expect(client).not_to have_received(:put).with(
184
207
  '/zones/9de4eb694c380d79845d35cd939cc7a7/dns_records/a1f984afe5544840505494298f54c33e',
@@ -220,10 +243,4 @@ RSpec.describe Glare do
220
243
  )
221
244
  end
222
245
  end
223
-
224
- def load_fixture(fixture)
225
- fixture_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures'))
226
- json = IO.read(File.join(fixture_dir, "#{fixture}.json"))
227
- ::HTTP::Message.new_response(JSON.parse(json))
228
- end
229
246
  end
@@ -0,0 +1,111 @@
1
+ require 'glare/cf_dns_records'
2
+ require 'glare/domain/record'
3
+ require 'glare/dns_record'
4
+
5
+ RSpec.describe Glare::CfDnsRecords::Updater do
6
+ it 'can detects new records to add' do
7
+ current_records = Glare::CfDnsRecords.empty
8
+ new_record = dns_record(content: '1.2.3.4')
9
+ new_records = [new_record]
10
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
11
+ operation = Glare::CfDnsRecords::Updater::Operation.new(new_record, :add)
12
+ expect(operations.insertions).to match_array([operation])
13
+ end
14
+
15
+ it 'can detects new records to keep' do
16
+ current_record = existing_record(content: '1.2.3.4')
17
+ current_records = Glare::CfDnsRecords.new([current_record])
18
+ new_record = dns_record(content: '1.2.3.4')
19
+ new_records = [new_record]
20
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
21
+ expect(operations.count).to be_zero
22
+ end
23
+
24
+ it 'can detects new records to add when there are some records' do
25
+ current_record = existing_record(content: '1.2.3.4')
26
+ current_records = Glare::CfDnsRecords.new([current_record])
27
+
28
+ new_record = dns_record(content: '1.2.3.5')
29
+ existing_record = dns_record(content: '1.2.3.4')
30
+ new_records = [new_record, existing_record]
31
+
32
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
33
+ operation = Glare::CfDnsRecords::Updater::Operation.new(new_record, :add)
34
+
35
+ expect(operations.insertions).to eq([operation])
36
+ end
37
+
38
+ it 'can detects new records to update when there are some records' do
39
+ current_record = existing_record(content: '1.2.3.4')
40
+ current_records = Glare::CfDnsRecords.new([current_record])
41
+
42
+ new_record = dns_record(content: '1.2.3.5')
43
+ update_record = dns_record(content: '1.2.3.6')
44
+ new_records = [update_record, new_record].shuffle
45
+
46
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
47
+ add_operation = Glare::CfDnsRecords::Updater::Operation.new(new_record, :add)
48
+
49
+ current_record.content = '1.2.3.6'
50
+ update_operation = Glare::CfDnsRecords::Updater::Operation.new(current_record, :update)
51
+
52
+ expect(operations.insertions).to eq([add_operation])
53
+ expect(operations.updates).to eq([update_operation])
54
+ end
55
+
56
+ it 'can detects new records to delete' do
57
+ current_record = existing_record(content: '1.2.3.4')
58
+ current_records = Glare::CfDnsRecords.new([current_record])
59
+
60
+ new_records = []
61
+
62
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
63
+ operation = Glare::CfDnsRecords::Updater::Operation.new(current_record, :delete)
64
+
65
+ expect(operations.deletions).to match_array([operation])
66
+ end
67
+
68
+ it 'can detects new records to delete and update' do
69
+ current_record = existing_record(content: '1.2.3.4')
70
+ current_record2 = existing_record(content: '1.2.3.6')
71
+ current_record3 = existing_record(content: '1.2.3.5')
72
+ current_records = Glare::CfDnsRecords.new([current_record2, current_record, current_record3])
73
+
74
+ new_record = dns_record(content: '1.2.3.8')
75
+ update_record = dns_record(content: '1.2.3.4')
76
+ new_records = [new_record, update_record].shuffle
77
+
78
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
79
+
80
+ current_record2.content = '1.2.3.8'
81
+ update_operation = Glare::CfDnsRecords::Updater::Operation.new(current_record2, :update)
82
+ delete_operation = Glare::CfDnsRecords::Updater::Operation.new(current_record3, :delete)
83
+
84
+ expect(operations.updates).to eq([update_operation])
85
+ expect(operations.deletions).to eq([delete_operation])
86
+ end
87
+
88
+ it 'can detects new records to delete and update' do
89
+ current_record = existing_record(content: '1.2.3.4')
90
+ current_record2 = existing_record(content: '1.2.3.6')
91
+ current_record3 = existing_record(content: '1.2.3.5')
92
+ current_records = Glare::CfDnsRecords.new([current_record2, current_record, current_record3].shuffle)
93
+
94
+ new_record = dns_record(content: '1.2.3.6')
95
+ new_record2 = dns_record(content: '1.2.3.4')
96
+ new_record3 = dns_record(content: '1.2.3.5')
97
+ new_records = [new_record, new_record2, new_record3].shuffle
98
+
99
+ operations = Glare::CfDnsRecords::Updater.new(current_records, new_records).calculate
100
+
101
+ expect(operations.count).to be_zero
102
+ end
103
+
104
+ def existing_record(id: 1_234, name: 'name', type: 'A', content:)
105
+ Glare::CfDnsRecord.new(id: id, name: name, type: type, content: content)
106
+ end
107
+
108
+ def dns_record(name: 'name', type: 'A', content:)
109
+ Glare::DnsRecord.new(name: name, type: type, content: content)
110
+ end
111
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glare
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Luis Salas
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-10-31 00:00:00.000000000 Z
12
+ date: 2016-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -97,7 +97,9 @@ files:
97
97
  - glare.gemspec
98
98
  - lib/glare.rb
99
99
  - lib/glare/api_response.rb
100
+ - lib/glare/cf_dns_record.rb
100
101
  - lib/glare/cf_dns_records.rb
102
+ - lib/glare/cf_dns_records/updater.rb
101
103
  - lib/glare/client.rb
102
104
  - lib/glare/credentials.rb
103
105
  - lib/glare/dns_record.rb
@@ -105,14 +107,19 @@ files:
105
107
  - lib/glare/domain/cf_zones.rb
106
108
  - lib/glare/domain/record.rb
107
109
  - lib/glare/domain/zone.rb
110
+ - lib/glare/errors.rb
108
111
  - lib/glare/version.rb
109
112
  - spec/delete_domain_spec.rb
110
113
  - spec/fixtures/empty_result.json
114
+ - spec/fixtures/error_response.json
111
115
  - spec/fixtures/list_zone.json
112
116
  - spec/fixtures/wadus_records.json
117
+ - spec/fixtures/wadus_records_reverse_order.json
113
118
  - spec/resolve_domain_spec.rb
114
119
  - spec/spec_helper.rb
120
+ - spec/units/api_response_spec.rb
115
121
  - spec/units/glare_spec.rb
122
+ - spec/units/operations_spec.rb
116
123
  homepage: https://github.com/peertransfer/glare
117
124
  licenses:
118
125
  - MIT
@@ -138,10 +145,14 @@ signing_key:
138
145
  specification_version: 4
139
146
  summary: API client for CloudFlare v4 API
140
147
  test_files:
148
+ - spec/units/api_response_spec.rb
141
149
  - spec/units/glare_spec.rb
150
+ - spec/units/operations_spec.rb
142
151
  - spec/delete_domain_spec.rb
143
152
  - spec/resolve_domain_spec.rb
144
153
  - spec/spec_helper.rb
145
154
  - spec/fixtures/wadus_records.json
146
155
  - spec/fixtures/empty_result.json
147
156
  - spec/fixtures/list_zone.json
157
+ - spec/fixtures/error_response.json
158
+ - spec/fixtures/wadus_records_reverse_order.json