gandirb 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/CHANGELOG +6 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +22 -24
  4. data/Rakefile +8 -9
  5. data/USAGE.rdoc +131 -0
  6. data/gandirb.gemspec +7 -9
  7. data/lib/gandi/connection.rb +65 -0
  8. data/lib/gandi/connector.rb +7 -0
  9. data/lib/gandi/contact.rb +175 -0
  10. data/lib/gandi/domain/contacts.rb +21 -0
  11. data/lib/gandi/domain/forward.rb +58 -0
  12. data/lib/gandi/domain/host.rb +63 -0
  13. data/lib/gandi/domain/mailbox.rb +72 -0
  14. data/lib/gandi/domain/nameservers.rb +17 -0
  15. data/lib/gandi/domain/status.rb +19 -0
  16. data/lib/gandi/domain/tld.rb +17 -0
  17. data/lib/gandi/domain/transferin.rb +13 -0
  18. data/lib/gandi/domain.rb +61 -128
  19. data/lib/gandi/errors.rb +10 -0
  20. data/lib/gandi/gandi_object_methods.rb +23 -0
  21. data/lib/gandi/operation.rb +45 -0
  22. data/lib/gandi/version.rb +1 -1
  23. data/lib/gandi.rb +57 -6
  24. data/spec/gandi/connection_spec.rb +39 -0
  25. data/spec/gandi/contact_spec.rb +173 -0
  26. data/spec/gandi/domain/forward_spec.rb +103 -0
  27. data/spec/gandi/domain/host_spec.rb +103 -0
  28. data/spec/gandi/domain/mailbox_spec.rb +117 -0
  29. data/spec/gandi/domain/tld_spec.rb +23 -0
  30. data/spec/gandi/domain_spec.rb +174 -0
  31. data/spec/gandi/errors_spec.rb +18 -0
  32. data/spec/gandi/gandi_spec.rb +57 -0
  33. data/spec/gandi/operation_spec.rb +73 -0
  34. data/spec/spec_helper.rb +5 -0
  35. data/spec/support/contact_macros.rb +18 -0
  36. data/spec/support/domain_macros.rb +103 -0
  37. data/spec/support/operation_macros.rb +22 -0
  38. metadata +58 -51
  39. data/lib/gandi/base.rb +0 -121
  40. data/lib/gandi/domain_modules/contact.rb +0 -36
  41. data/lib/gandi/domain_modules/host.rb +0 -29
  42. data/lib/gandi/domain_modules/mail.rb +0 -80
  43. data/lib/gandi/domain_modules/name_servers.rb +0 -28
  44. data/lib/gandi/domain_modules/operations.rb +0 -30
  45. data/lib/gandi/domain_modules/redirection.rb +0 -25
  46. data/test/gandi/base_test.rb +0 -188
  47. data/test/gandi/domain_test.rb +0 -301
  48. data/test/gandi_test.rb +0 -9
  49. data/test/test_helper.rb +0 -9
@@ -0,0 +1,72 @@
1
+ module Gandi
2
+ class Domain
3
+ class Mailbox
4
+ include Gandi::GandiObjectMethods
5
+
6
+ attr_reader :domain, :login
7
+
8
+ def initialize(domain, login, attributes = nil)
9
+ @domain = domain
10
+ @login = login
11
+ @attributes = attributes || info
12
+ end
13
+
14
+ #Delete a mailbox.
15
+ #Note: the mailbox is deleted instantly.
16
+ def delete
17
+ self.class.call('domain.mailbox.delete', @domain.fqdn, @login)
18
+ end
19
+
20
+ #Get mailbox information for a given login created for a given domain.
21
+ def info
22
+ self.class.call('domain.mailbox.info', @domain.fqdn, @login)
23
+ end
24
+
25
+ #Purge a mailbox.
26
+ #Create an operation that will delete this mailbox contents.
27
+ #Return a Gandi::Operation object.
28
+ def purge
29
+ operation_hash = self.class.call('domain.mailbox.purge', @domain.fqdn, @login)
30
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
31
+ end
32
+
33
+ #Update a mailbox.
34
+ #Returns a Gandi::Operation object.
35
+ def update(params)
36
+ operation_hash = self.class.call('domain.mailbox.update', @domain.fqdn, @login, params)
37
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
38
+ end
39
+
40
+ class << self
41
+ #Create a mailbox.
42
+ #Return a Mailbox object.
43
+ def create(domain, login, params)
44
+ mailbox = call('domain.mailbox.create', domain.fqdn, login, params)
45
+ self.new(domain, login, mailbox)
46
+ end
47
+
48
+ #Count mailboxes for a given domain.
49
+ #TODO: accept a fqdn string.
50
+ def count(domain, opts = {})
51
+ call('domain.mailbox.count', domain.fqdn, opts)
52
+ end
53
+
54
+ #List mailboxes for a given domain.
55
+ #Return an array of Mailbox objects or an array of hashes.
56
+ #Note that domain.mailbox.info will be called for each mailbox unless map_mailboxes is set to false (as the info hash returned by domain.mailbox.list does not contain the mailboxes attributes).
57
+ #This may result in a lot of API calls if you have many registered mailboxes for your domain, so using opts to restrict results is advised (or use a raw API call).
58
+ #TODO: accept a fqdn string.
59
+ def list(domain, opts = {}, map_mailboxes = true)
60
+ mailboxes = call('domain.mailbox.list', domain.fqdn, opts)
61
+
62
+ if map_mailboxes
63
+ mailboxes.map! do |mailbox|
64
+ self.new(domain, mailbox['login'])
65
+ end
66
+ end
67
+ return mailboxes
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,17 @@
1
+ module Gandi
2
+ class Domain
3
+ module Nameservers
4
+ #Returns the domains nameservers.
5
+ def nameservers
6
+ @attributes['nameservers']
7
+ end
8
+
9
+ #Define the nameservers for a given domain. Uses the API call domain.nameservers.set
10
+ #Returns a Gandi::Operation object.
11
+ def nameservers=(nameservers)
12
+ operation_hash = self.class.call('domain.nameservers.set', @fqdn, nameservers)
13
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module Gandi
2
+ class Domain
3
+ module Status
4
+ #Lock a domain, set the ‘clientTransferProhibited’ status flag.
5
+ #Returns a Gandi::Operation object.
6
+ def lock
7
+ operation_hash = self.class.call('domain.status.lock', @fqdn)
8
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
9
+ end
10
+
11
+ #Unlock a domain, unset the ‘clientTransferProhibited’ status flag.
12
+ #Returns a Gandi::Operation object.
13
+ def unlock
14
+ operation_hash = self.class.call('domain.status.unlock', @fqdn)
15
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Gandi
2
+ class Domain
3
+ class Tld
4
+ extend Gandi::Connector
5
+
6
+ #List all tld available in API.
7
+ def self.list
8
+ call('domain.tld.list')
9
+ end
10
+
11
+ #List all tld by region, available in API.
12
+ def self.region
13
+ call('domain.tld.region')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module Gandi
2
+ class Domain
3
+ module Transferin
4
+ #Transfer a domain. Uses the API call domain.transferin.proceed
5
+ #Returns a Gandi::Operation object.
6
+ #NOTE: this may be untestable in OT&E
7
+ def transferin(fqdn, params)
8
+ operation_hash = call('domain.transferin.proceed', fqdn, params)
9
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/gandi/domain.rb CHANGED
@@ -1,133 +1,66 @@
1
- require 'gandi/domain_modules/name_servers'
2
- require 'gandi/domain_modules/host'
3
- require 'gandi/domain_modules/redirection'
4
- require 'gandi/domain_modules/contact'
5
- require 'gandi/domain_modules/operations'
6
- require 'gandi/domain_modules/mail'
1
+ # encoding: utf-8
7
2
 
8
- module Gandi
9
- class Domain < Gandi::Base
10
- #Returns an array of domains for which the logged user is the reseller or a contact (owner, administrative, billing or technical).
11
- #TODO: memoize results
12
- def domain_list
13
- call('domain_list')
14
- end
15
-
16
- #Check a domain availability.
17
- #This implementation does not respect the original API specifications and allow for a single domain to be checked.
18
- #In this case the domain can be provided as a string and a boolean will be returned.
19
- def domain_available(domains)
20
- available_domains = call('domain_available', [domains].flatten)
21
- return (domains.is_a?(String)) ? available_domains.values.first : available_domains
22
- end
23
-
24
- #Add a lock status (cf. domain_info) to avoid any fraudulent transfer.
25
- #Return the operation attributed ID
26
- def domain_lock(domain)
27
- call('domain_lock', domain)
28
- end
29
-
30
- #Remove a lock status (cf. domain_info) to transfer the domain to another registrar.
31
- #Return the operation attributed ID
32
- def domain_unlock(domain)
33
- call('domain_unlock', domain)
34
- end
35
-
36
- #Retrieve the informations linked to this domain (contacts, nameservers, redirections, weblogs...).
37
- #This method only works on domains handled by Gandi.
38
- #Return a hash with string keys containing domain informations (see API documentation)
39
- #TODO: convert XMLRPC datetimes ?
40
- def domain_info(domain)
41
- call('domain_info', domain)
42
- end
43
-
44
- #Renew a domain for a number of years.
45
- #The total number of years cannot exceed 10.
46
- #Return the operation attributed ID
47
- def domain_renew(domain, period)
48
- raise ArgumentError.new("The total number of years cannot exceed 10") if period > 10
49
- call('domain_renew', domain, period)
50
- end
51
-
52
- #Register a domain with Gandi and associate it to a list of contacts.
53
- #Return the operation attributed ID
54
- def domain_create(domain, period, owner_handle, admin_handle, tech_handle, billing_handle, nameservers, lang = nil)
55
- args = [domain, period, owner_handle, admin_handle, tech_handle, billing_handle, nameservers, lang]
56
- args.pop if lang.nil?
57
- call('domain_create', *args)
58
- end
59
-
60
- #Get a domain out of its redemption period. See glossary, Restore.
61
- #This function is available for domains in .COM, .NET, .BE or .EU.
62
- #Return the operation attributed ID
63
- #TODO: Check for domain correctness
64
- def domain_restore(domain)
65
- call('domain_restore', domain)
66
- end
67
-
68
- #Delete a domain.
69
- #Return the operation attributed ID
70
- #This method is not available yet and will be present in v1.10 of the API
71
- def domain_del(domain)
72
- not_supported
73
- end
74
-
75
- #Check if a domain can be transferred from another registrar. See glossary, Transfer.
76
- def domain_transfer_in_available(domain)
77
- call('domain_transfer_in_available', domain)
78
- end
79
-
80
- alias_method :domain_transfer_in_available?, :domain_transfer_in_available
81
-
82
- #Transfer (see glossary, Transfer) a domain from another registrar to Gandi, or from a Gandi account to a Gandi reseller.
83
- #If the domain is already at Gandi (internal transfer), owner_handle, admin_handle, tech_handle and billing_handle are maintained.
84
- #Return the operation attributed ID
85
- #TODO: check for auth_code if TLD is .fr
86
- def domain_transfer_in(domain, owner_handle, admin_handle, tech_handle, billing_handle, nameservers, auth_code = nil)
87
- args = [domain, owner_handle, admin_handle, tech_handle, billing_handle, nameservers, auth_code]
88
- args.pop if auth_code.nil?
89
- call('domain_transfer_in', *args)
90
- end
91
-
92
- #Accept or deny a transfer of a domain from Gandi to another registrar. See glossary, Transfer.
93
- #This method is not available yet and will be present in v1.10 of the API
94
- def domain_transfer_out(opid, allow)
95
- not_supported
96
- end
97
-
98
- #Start the trade of a domain from another registrar to Gandi.
99
- #Return the operation attributed ID
100
- #This method is not available yet and will be present in v1.10 of the API
101
- def domain_trade(domain, owner_handle, admin_handle, tech_handle, billing_handle, afnic_titularkey = nil)
102
- not_supported
103
- end
104
-
105
- #Change the owner of a domain registered with Gandi and, optionally, the admin, tech and billing contacts. See glossary, Change of Ownership.
106
- #Return the operation attributed ID
107
- #This method is not available yet and will be present in v1.10 of the API
108
- def domain_change_owner(domain, new_owner, new_admin = nil, new_tech = nil, new_billing = nil)
109
- not_supported
110
- end
111
-
112
- #Change a given contact of a domain registered with Gandi.
113
- #Return the operation attributed ID
114
- def domain_change_contact(domain, type, new_contact)
115
- call('domain_change_contact', domain, type, new_contact)
116
- end
117
-
118
- #Misc methods
3
+ require 'gandi/domain/tld'
4
+ require 'gandi/domain/contacts'
5
+ require 'gandi/domain/status'
6
+ require 'gandi/domain/transferin'
7
+ require 'gandi/domain/nameservers'
8
+ require 'gandi/domain/host'
9
+ require 'gandi/domain/mailbox'
10
+ require 'gandi/domain/forward'
119
11
 
120
- #Return an array of active TLDs handled by the application.
121
- #TODO: memoization
122
- def tld_list
123
- call('tld_list')
12
+ module Gandi
13
+ class Domain
14
+ include Gandi::GandiObjectMethods
15
+ include Gandi::Domain::Contacts
16
+ include Gandi::Domain::Status
17
+ extend Gandi::Domain::Transferin
18
+ include Gandi::Domain::Nameservers
19
+
20
+ attr_reader :fqdn
21
+
22
+ def initialize(fqdn)
23
+ @fqdn = fqdn
24
+ @attributes = info
25
+ end
26
+
27
+ #get domain information.
28
+ def info
29
+ self.class.call('domain.info', @fqdn)
30
+ end
31
+
32
+ #Renew a domain.
33
+ def renew(params)
34
+ operation_hash = self.class.call('domain.renew', @fqdn, params)
35
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
36
+ end
37
+
38
+ class << self
39
+ #Check the availability of some domain names.
40
+ def available(fqdns)
41
+ call('domain.available', fqdns)
42
+ end
43
+
44
+ #Count domains visible by this account.
45
+ def count(filters = {})
46
+ call('domain.count', filters)
47
+ end
48
+
49
+ #List operations done by this account.
50
+ #Note that domain.info will be called for each domain (as the info hash returned by domain.list is missing some informations).
51
+ #This may result in a lot of API calls if you have many registered domains for your contact, so using opts to restrict results is advised (or use a raw API call).
52
+ def list(opts = {})
53
+ call('domain.list', opts).map do |domain|
54
+ self.new(domain['fqdn'])
55
+ end
56
+ end
57
+
58
+ #Create a domain with the given information.
59
+ #Returns a Gandi::Domain object.
60
+ def create(fqdn, params)
61
+ operation_hash = call('domain.create', fqdn, params)
62
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
63
+ end
124
64
  end
125
-
126
- include Gandi::DomainModules::NameServers
127
- include Gandi::DomainModules::Host
128
- include Gandi::DomainModules::Redirection
129
- include Gandi::DomainModules::Contact
130
- include Gandi::DomainModules::Operations
131
- include Gandi::DomainModules::Mail
132
65
  end
133
66
  end
@@ -0,0 +1,10 @@
1
+ module Gandi
2
+ class DataError < ArgumentError
3
+ end
4
+
5
+ class ServerError < RuntimeError
6
+ end
7
+
8
+ class NoMethodError < ::NoMethodError
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module Gandi
2
+ module GandiObjectMethods
3
+ def self.included(base)
4
+ base.extend Gandi::Connector
5
+ end
6
+
7
+ #Returns a hash (with string keys) with the object information attributes.
8
+ def to_hash
9
+ @attributes.dup
10
+ end
11
+
12
+ #Returns a string containing a human-readable representation of the object.
13
+ #TODO improve the output.
14
+ def inspect
15
+ @attributes.inspect
16
+ end
17
+
18
+ #Returns an attribute value.
19
+ def [](attribute)
20
+ @attributes[attribute.to_s]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ module Gandi
4
+ class Operation
5
+ include Gandi::GandiObjectMethods
6
+
7
+ attr_reader :id
8
+
9
+ def initialize(operation_id, information_attributes = nil)
10
+ @id = operation_id
11
+ @attributes = information_attributes || info
12
+ end
13
+
14
+ #Return some attributes of a operation visible by this account.
15
+ def info
16
+ self.class.call('operation.info', @id)
17
+ end
18
+
19
+ #Set the step of an operation to CANCEL.
20
+ def cancel
21
+ result = self.class.call('operation.cancel', @id)
22
+ @attributes['step'] = 'CANCEL' if result
23
+ return result
24
+ end
25
+
26
+ class << self
27
+ #Count operations visible by this account.
28
+ def count(opts = {})
29
+ call('operation.count', opts)
30
+ end
31
+
32
+ #List operations done by this account.
33
+ #The array of returned operations are mapped to Operation objects unless map_operations is set to false.
34
+ def list(opts = {}, map_operations = true)
35
+ operations = call('operation.list', opts)
36
+ if map_operations
37
+ operations.map! do |operation|
38
+ self.new(operation['id'], operation)
39
+ end
40
+ end
41
+ operations
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/gandi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Gandi
2
- VERSION = "1.1.1"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/gandi.rb CHANGED
@@ -1,8 +1,59 @@
1
+ require 'gandi/version'
2
+ require 'gandi/errors'
3
+ require 'gandi/connection'
4
+ require 'gandi/connector'
5
+ require 'gandi/gandi_object_methods'
6
+ require 'gandi/operation'
7
+ require 'gandi/contact'
8
+ require 'gandi/domain'
9
+
1
10
  module Gandi
2
- class DataError < ArgumentError ; end
3
- class ServerError < RuntimeError ; end
4
- end
11
+ #Production URL
12
+ URL = "https://rpc.gandi.net/xmlrpc/"
13
+
14
+ #OT&E URL
15
+ TEST_URL = "https://rpc.ote.gandi.net/xmlrpc/"
16
+
17
+ class << self
18
+ #The 24-character API key used for authentication.
19
+ attr_reader :apikey
20
+
21
+ #Sets the apikey for the Gandi connection.
22
+ #Changing it will reset the connection.
23
+ def apikey=(apikey)
24
+ @apikey = apikey
25
+ @connection = nil
26
+ end
27
+
28
+ #The URL for the Gandi connection.
29
+ def url
30
+ @url || Gandi::TEST_URL
31
+ end
32
+
33
+ #Sets the URL for the Gandi connection.
34
+ #Changing it will reset the connection.
35
+ def url=(url)
36
+ @url = url
37
+ @connection = nil
38
+ end
39
+
40
+ #A Gandi::Connection object set using the provided apikey and url.
41
+ #This object can be overriden for testing purposes.
42
+ #Note that the connection object is reset if the apikey or url is changed.
43
+ def connection
44
+ @connection ||= Gandi::Connection.new(apikey, url)
45
+ end
46
+
47
+ attr_writer :connection
5
48
 
6
- require 'gandi/base'
7
- require 'gandi/domain'
8
- require 'gandi/version'
49
+ #Calls a Gandi XML-RPC method, transparently providing the apikey.
50
+ def call(method, *arguments)
51
+ connection.call(method, *arguments)
52
+ end
53
+
54
+ #Returns the API version from Gandi.
55
+ def api_version
56
+ Gandi.call('version.info')['api_version']
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'gandi/connection'
3
+ require 'gandi/errors'
4
+
5
+ describe Gandi::Connection do
6
+ describe "#new" do
7
+ it "connects using apikey and url" do
8
+ gandi_connection = Gandi::Connection.new('apikey', 'http://fakedomain.tld/')
9
+ gandi_connection.instance_variable_get('@handler').should be_a(XMLRPC::Client)
10
+ gandi_connection.instance_variable_get('@handler').instance_variable_get('@host').should == 'fakedomain.tld'
11
+ end
12
+
13
+ it "fails when setting a nil apikey" do
14
+ lambda { Gandi::Connection.new(nil, nil) }.should raise_error(Gandi::DataError)
15
+ end
16
+ end
17
+
18
+ context "an instance" do
19
+ subject { Gandi::Connection.new('apikey', 'http://fakedomain.tld/') }
20
+
21
+ its(:url) { should == 'http://fakedomain.tld/' }
22
+ its(:apikey) { should == 'apikey' }
23
+
24
+ it "can call methods and transmit them to the handler" do
25
+ subject.instance_variable_get('@handler').should_receive(:call).once.with('method.name', 'apikey', 'arg1', 'arg2').and_return('result value')
26
+ subject.call('method.name', 'arg1', 'arg2').should == 'result value'
27
+ end
28
+
29
+ it "maps xml-rpc 5xxxxx errors to Gandi errors" do
30
+ subject.instance_variable_get('@handler').should_receive(:call).once.with('contact.list', 'apikey', 'AB123-GANDI').and_raise(XMLRPC::FaultException.new('516150', "Error on object : OBJECT_CONTACT (CAUSE_NORIGHT) [You aren't allowed to read that contact]"))
31
+ lambda { subject.call('contact.list', 'AB123-GANDI') }.should raise_error(Gandi::DataError)
32
+ end
33
+
34
+ it "maps xml-rpc errors with faultCode 1 to Gandi error" do
35
+ subject.instance_variable_get('@handler').should_receive(:call).once.with('unknownmethod', 'apikey').and_raise(XMLRPC::FaultException.new('1', 'Method "unknownmethod" is not supported'))
36
+ lambda { subject.call('unknownmethod') }.should raise_error(Gandi::NoMethodError)
37
+ end
38
+ end
39
+ end