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
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ == 2.0.0
2
+
3
+ * Full rewrite to support API version 2.0
4
+ * Tests are now using rspec
5
+ * Moved git repository to https://github.com/pickabee/gandi
6
+
1
7
  == 1.1.1
2
8
 
3
9
  * Missing gemfile in gem
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Pickabee
1
+ Copyright (c) 2012 Pickabee
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
data/README.rdoc CHANGED
@@ -1,36 +1,34 @@
1
- = Gandirb - Ruby library for using the Gandi XML-RPC API
1
+ = Gandi - Ruby library for using the Gandi XML-RPC API
2
2
 
3
- This is a ruby library for using the Gandi XML-RPC API.
4
- It currently only provides methods for using the domain and mail API, but is extensible enough to add hosting in the future.
3
+ A Ruby library for using the Gandi XML-RPC API.
4
+ At the moment support is planned for the Domain, Contact and Operation APIs only, but Hosting and Catalog APIs support may be added in the future.
5
+ Version 2.0.0 and upwards of this gem supports only the Gandi API version 2.0, use version 1.x of the gem if you need to the 1.0 API.
5
6
 
6
- == Howto
7
+ == HOWTO
7
8
 
8
- See http://wiki.gandi.net/fr/api-xml/docs/domain for the full documentation on the API.
9
- Note the session_id doesn't have to be explicitly provided when calling a method.
9
+ See http://rpc.ote.gandi.net/doc/2.0/index.html for the full documentation about the API.
10
+ Note that the apikey doesn't have to be explicitly provided when calling a method.
10
11
 
11
- It should also be noted that the ruby xml-rpc lib seems to have a bug with the ssl support, resulting in a timeout of about 15 seconds.
12
- After this time the current connection won't work and result in various exception.
13
- This lib takes this issue into account and provides a workaround by restarting the connection. See the comments for Gandi::Base#raw_call for more details.
12
+ Quick example:
13
+ Gandi.apikey = 'my 24-character API key'
14
+ Gandi.url # => "https://rpc.ote.gandi.net/xmlrpc/"
15
+ Gandi.url = Gandi::URL #Use the production URL instead of the default Operational Test and Evaluation URL
16
+ Gandi.call('version.info') #You do not have to provide the apikey for each call
17
+ contact = Gandi::Contact.new('FLN123-GANDI')
18
+ contact.country # => "FR"
14
19
 
15
- Quick example :
16
-
17
- require 'gandi'
18
- gandi_session = Gandi::Domain.login 'XXYYY-Gandi', 'mypasswd', Gandi::Domain::TEST_URL #or use Gandi::Domain::URL in production mode
19
- gandi_session.account_currency # => "EUR"
20
- gandi_session.domain_available ["mycoolwebsite.com"] # => {"mycoolwebsite.com"=>true}
21
- gandi_session.domain_available "mycoolwebsite.com" # => true #additional syntax
22
- gandi_session.domain_list # => ["mypersonalwebsite.com"]
23
-
24
- Detailed RDoc documentation for each method is available using rake rdoc.
20
+ You can read more about the library in USAGE.rdoc.
21
+ Detailed RDoc documentation for each method is available using `rake rdoc`.
25
22
 
26
23
  == TODO
27
24
 
28
25
  * More tests
29
- * Finish adding and cleaning up domain and mail methods
30
- * Better handling of failures and server exceptions, and stricter params checking
31
- * Refactor Domain and Base class and add a Session class to separate XML-RPC code and prepare adding hosting methods
26
+ * Refactor the Operation object creation from the other classes
27
+ * Add attributes getters to Domain/Operation/Mailbox/Host classes
28
+ * Refactor the Contact class to match the other classes behaviour (instances of the class should only match existing records)
29
+ * Make the Domain/Contact/etc. classes more ActiveModel-like
30
+ * Better handling of failures and server exceptions, and overall stricter params checking
32
31
 
33
32
  == Copyright
34
33
 
35
- Copyright (c) 2009 Pickabee. Released under the MIT licence, see LICENSE for details.
36
- Some parts inspired by http://github.com/jerome/gandi/tree
34
+ Copyright (c) 2012 Pickabee. Released under the MIT licence, see LICENSE for details.
data/Rakefile CHANGED
@@ -2,22 +2,21 @@ require 'bundler/setup'
2
2
  require 'rake'
3
3
  require 'bundler/gem_tasks'
4
4
 
5
- require 'rake/testtask'
6
- Rake::TestTask.new(:test) do |test|
7
- test.libs << 'lib' << 'test'
8
- test.pattern = 'test/**/*_test.rb'
9
- test.verbose = false
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.rspec_opts = ['-c', '-f progress', '-r ./spec/spec_helper.rb', '--backtrace']
8
+ t.pattern = 'spec/**/*_spec.rb'
10
9
  end
11
10
 
12
- task :default => :test
11
+ task :default => :spec
13
12
 
14
13
  require 'rdoc/task'
15
14
  Rake::RDocTask.new do |rdoc|
16
15
  rdoc.rdoc_dir = 'rdoc'
17
16
  rdoc.title = "Gandirb"
18
17
  rdoc.main = 'README.rdoc'
19
- rdoc.rdoc_files.include('README*', 'CHANGELOG', 'LICENSE')
18
+ rdoc.rdoc_files.include('README*', 'USAGE*', 'CHANGELOG', 'LICENSE')
20
19
  rdoc.rdoc_files.include('lib/**/*.rb')
21
- rdoc.options << "--line-numbers"
22
- rdoc.options << "--charset=UTF-8"
20
+ rdoc.options << '--line-numbers'
21
+ rdoc.options << '--charset=UTF-8'
23
22
  end
data/USAGE.rdoc ADDED
@@ -0,0 +1,131 @@
1
+ = Setup
2
+
3
+ == Installation
4
+
5
+ === Using bundler
6
+
7
+ Add the gandi gem requirement to your Gemfile :
8
+ gem "gandi", "~> 1.0"
9
+
10
+ Then install the gem with `bundle`.
11
+
12
+ === Using Rubygems (or any other Ruby package manager)
13
+
14
+ Install the gem manually (see your package manager documentation if you are using a Rubygems alternative) :
15
+ $ gem install gandi
16
+
17
+ You will have to require the gandi library manually in your application code :
18
+ require 'gandi'
19
+
20
+ === Configuration
21
+
22
+ Set your API key :
23
+ Gandi.apikey = 'my 24-character API key'
24
+ See https://www.gandi.net/admin/apixml/ to activate the API for your account and get your apikey.
25
+
26
+ By default, the library will use the OT&E system when calling the API.
27
+ See http://rpc.ote.gandi.net/doc/2.0/overview.html#ot-e-system to learn more about the OT&E system.
28
+ You can use the production system by changing the URL :
29
+ Gandi.url = Gandi::URL
30
+
31
+ = Description
32
+
33
+ The Gandi library can be used in two ways : either by calling the API directly, or by using Ruby classes and methods that transparently map to the corresponding API calls.
34
+
35
+ == API calls
36
+
37
+ You can call any API method directly by using Gandi.call :
38
+ Gandi.call('version.info') # => {"api_version"=>"2.0"}
39
+
40
+ The apikey will automatically be provided as the first argument of the called method.
41
+
42
+ == Ruby objects
43
+
44
+ The Gandi library aims to abstract the API calls by providing classes and methods matching Gandi "objects" :
45
+ * Gandi::Contact - a Gandi contact (identified by its handle)
46
+ * Gandi::Domain - a domain name (identified by its fqdn)
47
+ * Gandi::Domain::Forward - A domain forward (identified by its source email) (TODO)
48
+ * Gandi::Domain::Host - A domain host (glue record) (identified by its hostname)
49
+ * Gandi::Domain::Mailbox - a mailbox for a domain name (identified by its login) (TODO)
50
+ * Gandi::Operation - an operation on the Gandi system (identified by its unique id)
51
+
52
+ Be aware that Ruby classes do not specifically map to Gandi API namespaces.
53
+ For example, Gandi::Domain#nameservers will return an array of nameservers fetched with a previous call to 'domain.info', while Gandi::Domain#nameservers=(nameservers) will call 'domain.nameservers.set'
54
+
55
+ Here is a (partial) reference of Gandi namespaces and their matching Ruby classes and methods :
56
+ * contact
57
+ * can_associate(contact, domain) => Gandi::Contact.can_associate(contact, domain)
58
+ * can_associate_domain(handle, domain) => Gandi::Contact#can_associate_domain(domain)
59
+ * create(contact) => Gandi::Contact.create(contact)
60
+ * info(handle='') => Gandi::Contact#info, Gandi::Contact.info(handle)
61
+ * list(opts=nil) => Gandi::Contact.list(opts = {})
62
+ * update(handle, contact) => Gandi::Contact#update(contact), Gandi::Contact.update(handle, contact)
63
+ * domain
64
+ * available(fqdns) => Gandi::Domain.available(fqdns)
65
+ * count(filters=nil) => Gandi::Domain.count(filters = {})
66
+ * create(fqdn, params) => Gandi::Domain.create(fqdn, params)
67
+ * info(fqdn) => Gandi::Domain#info
68
+ * list(opts=nil) => Gandi::Domain.list(opts = {})
69
+ * renew(fqdn, params) => Gandi::Domain#renew(params)
70
+ * domain.contacts
71
+ * set(fqdn, contacts) => Gandi::Domain#contacts=(contacts)
72
+ * domain.status
73
+ * lock(fqdn) => Gandi::Domain#lock
74
+ * unlock(fqdn) => Gandi::Domain#unlock
75
+ * domain.host
76
+ * count(fqdn[, opts=nil])
77
+ * create(hostname, ips)
78
+ * delete(hostname)
79
+ * info(hostname)
80
+ * list(fqdn[, opts=nil])
81
+ * update(hostname, ips)
82
+ * domain.mailbox
83
+ * count(domain[, opts=nil])
84
+ * create(domain, login, params)
85
+ * delete(domain, login)
86
+ * info(domain, login)
87
+ * list(domain[, opts=nil])
88
+ * purge(domain, login)
89
+ * update(domain, login, params)
90
+ * domain.mailbox.alias
91
+ * set(domain, login, aliases)
92
+ * domain.mailbox.responder
93
+ * activate(domain, login, params)
94
+ * deactivate(domain, login[, params=nil])
95
+ * domain.forward
96
+ * count(domain[, opts=nil])
97
+ * create(domain, source, params
98
+ * delete(domain, source)
99
+ * list(domain[, opts=nil])
100
+ * update(domain, source, params)
101
+ * domain.packmail
102
+ * domain.tld
103
+ * list => Gandi::Domain::Tld.list
104
+ * region => Gandi::Domain::Tld.region
105
+ * domain.transferin
106
+ * proceed(fqdn, params) => Gandi::Domain#transferin(params)
107
+ * domain.nameservers
108
+ * set(fqdn, nameservers) => Gandi::Domain#nameservers=(nameservers)
109
+ * operation
110
+ * cancel(apikey, operation) => Gandi::Operation#cancel
111
+ * count(apikey[, opts=nil]) => Gandi::Operation.count(opts = {})
112
+ * info(apikey, operation) => Gandi::Operation#info
113
+ * list(apikey[, opts=nil]) => Gandi::Operation.list(opts = {})
114
+
115
+ For more information on a Ruby class/method see the matching RDoc.
116
+
117
+ Note that API calls under the hosting and catalog are not currently implemented.
118
+ No support is planned for thoses API calls (but pull requests are accepted).
119
+
120
+ = Use in testing
121
+
122
+ While using the OT&E system is safe as it will not result in any real billing, integration and unit tests for your application should be repeatable and isolated.
123
+ You can use mocking (and/or stubbing) to avoid calling the XML-RPC methods on the Gandi server and cover all the possible cases.
124
+
125
+ Example using rspec-mocks:
126
+ connection_mock = double()
127
+ connection_mock.should_receive(:call).with('version.info').and_return({"api_version"=>"2.0"})
128
+ Gandi.connection = connection_mock
129
+ Gandi.call('version.info') # => {"api_version"=>"2.0"}
130
+
131
+ Be aware that changing the apikey or URL will reset the connection variable (so you will have to set up your mock again).
data/gandirb.gemspec CHANGED
@@ -9,25 +9,23 @@ Gem::Specification.new do |s|
9
9
 
10
10
  s.authors = ["Pickabee"]
11
11
  s.email = ""
12
- s.homepage = "http://github.com/pickabee/gandirb"
12
+ s.homepage = "http://github.com/pickabee/gandi"
13
13
  s.summary = %q{Ruby library for using the Gandi XML-RPC API}
14
14
  s.description = <<-EOL
15
- This is a ruby library for using the Gandi XML-RPC API.
16
- It currently only provides methods for using the domain and mail API, but is extensible enough to add hosting in the future.
15
+ A Ruby library for using the Gandi XML-RPC API.
16
+ At the moment support is planned for the Domain, Contact and Operation APIs only, but Hosting and Catalog APIs support may be added in the future.
17
17
  EOL
18
18
  s.rubyforge_project = "gandirb"
19
19
 
20
20
  s.date = Date.today.strftime('%Y-%m-%d')
21
21
 
22
- s.files = Dir["CHANGELOG", "LICENSE", "README.rdoc", "Gemfile", "Rakefile", "gandirb.gemspec", "{lib}/**/*.rb"]
23
- s.test_files = Dir["{test}/**/*.rb"]
22
+ s.files = Dir["CHANGELOG", "LICENSE", "README.rdoc", "USAGE.rdoc", "Gemfile", "Rakefile", "gandirb.gemspec", "{lib}/**/*.rb"]
23
+ s.test_files = Dir["{spec}/**/*.rb"]
24
24
  s.rdoc_options = ["--line-numbers", "--charset=UTF-8", "--title", "Gandirb", "--main", "README.rdoc"]
25
- s.extra_rdoc_files = %w[CHANGELOG LICENSE]
25
+ s.extra_rdoc_files = %w[USAGE.rdoc CHANGELOG LICENSE]
26
26
  s.require_paths = ["lib"]
27
27
 
28
- s.add_dependency 'activesupport', '>= 2.0'
29
28
  s.add_development_dependency 'rake'
30
29
  s.add_development_dependency 'rdoc'
31
- s.add_development_dependency 'shoulda', '< 3'
32
- s.add_development_dependency 'mocha'
30
+ s.add_development_dependency 'rspec', '>= 2.8'
33
31
  end
@@ -0,0 +1,65 @@
1
+ require 'xmlrpc/client'
2
+ require 'openssl'
3
+
4
+ module Gandi
5
+ class Connection
6
+ SSL_VERIFY_MODE = OpenSSL::SSL::VERIFY_NONE
7
+
8
+ #The 24-character API key used for authentication.
9
+ attr_reader :apikey
10
+
11
+ #URL used for connecting to the Gandi API.
12
+ attr_reader :url
13
+
14
+ def initialize(apikey, url)
15
+ raise DataError, "You must set the apikey before calling any method" unless apikey
16
+ @apikey = apikey
17
+ @url = url
18
+ connect
19
+ end
20
+
21
+ #Calls a RPC method, automatically setting the apikey as the first argument.
22
+ def call(method, *arguments)
23
+ raw_call(method.to_s, @apikey, *arguments)
24
+ end
25
+
26
+ private
27
+
28
+ #Handles RPC calls and exceptions.
29
+ def raw_call(*args)
30
+ begin
31
+ @handler.call(*args)
32
+ rescue StandardError => e
33
+ case e
34
+ when XMLRPC::FaultException
35
+ case e.faultCode.to_s.chars.first
36
+ when '1'
37
+ raise Gandi::NoMethodError, e.faultString
38
+ when '5'
39
+ raise Gandi::DataError, e.faultString
40
+ else
41
+ raise Gandi::ServerError, e.faultString
42
+ end
43
+ else
44
+ raise e
45
+ end
46
+ end
47
+ end
48
+
49
+ #Instanciates the XMLRPC handler.
50
+ def connect
51
+ @handler = XMLRPC::Client.new_from_uri(@url)
52
+ #Get rid of SSL warnings "peer certificate won't be verified in this SSL session"
53
+ #See http://developer.amazonwebservices.com/connect/thread.jspa?threadID=37139
54
+ #and http://blog.chmouel.com/2008/03/21/ruby-xmlrpc-over-a-self-certified-ssl-with-a-warning/
55
+ #and http://stackoverflow.com/questions/4748633/how-can-i-make-rubys-xmlrpc-client-ignore-ssl-certificate-errors for ruby 1.9 (which does not set @ssl_context before a request)
56
+ if @handler.instance_variable_get('@http').use_ssl?
57
+ if @handler.instance_variable_get('@http').instance_variable_get("@ssl_context") #Ruby 1.8.7
58
+ @handler.instance_variable_get('@http').instance_variable_get("@ssl_context").verify_mode = SSL_VERIFY_MODE
59
+ else
60
+ @handler.instance_variable_get('@http').instance_variable_set(:@verify_mode, SSL_VERIFY_MODE) #Ruby 1.9
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,7 @@
1
+ module Gandi
2
+ module Connector
3
+ def call(method, *arguments) #:nodoc:
4
+ Gandi.call(method, *arguments)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,175 @@
1
+ # encoding: utf-8
2
+
3
+ module Gandi
4
+ class Contact
5
+ include Gandi::GandiObjectMethods
6
+
7
+ #Contact types mapping
8
+ TYPES = {
9
+ 0 => 'person',
10
+ 1 => 'company',
11
+ 2 => 'association',
12
+ 3 => 'organization',
13
+ 4 => 'reseller'
14
+ }
15
+
16
+ #Security questions mapping
17
+ SECURITY_QUESTION_NUM_VALUES = {
18
+ 1 => "What is the name of your favorite city?",
19
+ 2 => "What is your mother’s maiden name?",
20
+ 3 => "What is your favorite food?",
21
+ 4 => "What year were you born in?",
22
+ 5 => "What is your cell phone number?",
23
+ 6 => "In what year was your Gandi account created?"
24
+ }
25
+
26
+ #Settable contact attributes (ie. not the id or handle)
27
+ WRITABLE_ATTRIBUTES = :type, :orgname, :given, :family, :streetaddr, :city, :state, :zip, :country, :phone, :fax, :mobile,
28
+ :tva_number, :siren, :marque, :lang, :newsletter, :obfuscated, :whois_obfuscated, :resell, :shippingaddress,
29
+ :extra_parameters
30
+
31
+ #Additional attributes used when creating an account
32
+ CREATE_PARAMETERS_ATTRIBUTES = :password, :email,
33
+ :jo_announce_page, :jo_announce_number, :jo_declaration_date, :jo_publication_date,
34
+ :security_question_num, :security_question_answer
35
+
36
+ #Attributes returned when calling contact.info or creating/updating a contact
37
+ INFORMATION_ATTRIBUTES = WRITABLE_ATTRIBUTES + [:id]
38
+
39
+ attr_reader :handle
40
+
41
+ #A new instance of Contact.
42
+ #Takes a Gandi handle and/or a contact hash with string keys.
43
+ #When providing only a handle the contact info will be fetched from the API.
44
+ def initialize(handle = nil, contact = nil)
45
+ @handle = handle
46
+ @attributes = {}
47
+ self.attributes=(contact) if (@handle || contact)
48
+ end
49
+
50
+ #Test if a contact (defined by its handle) can create that domain.
51
+ #Takes a domain hash.
52
+ #TODO allow giving a string for the domain and converting it transparently, or a domain object.
53
+ #FIXME is checking multiple domains at once possible ? (it may be according to the Gandi documentation).
54
+ #FIXME OT&E seems to fail instead of returning an error hash (Error on object : OBJECT_APPLICATION (CAUSE_UNKNOWN) [Internal Server Error]).
55
+ def can_associate_domain(domain)
56
+ return false unless persisted?
57
+ self.class.call('contact.can_associate_domain', @handle, domain)
58
+ end
59
+
60
+ #TODO make this method return a boolean
61
+ alias_method :can_associate_domain?, :can_associate_domain
62
+
63
+ #Give all information on the given contact.
64
+ #This should not be directly used as initializing the class already fetches the contact info.
65
+ def info
66
+ raise DataError, "Cannot get informations for a new contact" unless persisted?
67
+ self.class.call('contact.info', @handle)
68
+ end
69
+
70
+ #Check the same way as check and then update.
71
+ #FIXME What is check ? Typo for create ?
72
+ def update(contact)
73
+ raise DataError, "Cannot update a new contact, use Gandi::Contact.create instead" unless persisted?
74
+ self.attributes = self.class.call('contact.update', @handle, contact)
75
+ end
76
+
77
+ (WRITABLE_ATTRIBUTES - [:type]).each do |attr|
78
+ define_method(attr) do
79
+ @attributes[attr.to_s]
80
+ end
81
+
82
+ define_method("#{attr}=") do |value|
83
+ @attributes[attr.to_s] = value
84
+ end
85
+ end
86
+
87
+ #Returns the contact unique identifier.
88
+ def id
89
+ @attributes['id']
90
+ end
91
+
92
+ #Returns the contact type string.
93
+ def type
94
+ TYPES[@attributes['type']]
95
+ end
96
+
97
+ #Sets the contact type (provided with a type string or id).
98
+ def type=(type_string_or_id)
99
+ @attributes['type'] = TYPES.invert[type_string_or_id] || type_string_or_id
100
+ end
101
+
102
+ #Sets a contact attribute value.
103
+ def []=(attribute, value)
104
+ raise DataError, "Attribute #{attribute} is read-only" unless WRITABLE_ATTRIBUTES.include?(attribute.to_sym)
105
+ @attributes[attribute.to_s] = value
106
+ end
107
+
108
+ #Returns a string containing the handle of the contact.
109
+ def to_s
110
+ @handle || ''
111
+ end
112
+
113
+ #Returns true if the contact exists on Gandi databases.
114
+ def persisted?
115
+ !!@handle
116
+ end
117
+
118
+ class << self
119
+ #Should have a all optional contact dict then look if he is a person or a company and depending on that call create_person or create_company.
120
+ #Returns a contact object.
121
+ #TODO filter params.
122
+ def create(contact)
123
+ contact = call('contact.create', contact)
124
+ self.new(contact['handle'], contact)
125
+ end
126
+
127
+ #Give all information on the contact linked to the apikey currently used.
128
+ #Returns a hash.
129
+ def info
130
+ call('contact.info')
131
+ end
132
+
133
+ #Test if a contact (full contact description) can be associated to the domains.
134
+ #Takes a contact hash and a domain hash.
135
+ #TODO allow giving a string for the domain and converting it transparently, or a domain object.
136
+ #FIXME is checking multiple domains at once possible ? (it may be according to the Gandi documentation).
137
+ #FIXME OT&E seems to fail instead of returning an error hash (Error on object : OBJECT_APPLICATION (CAUSE_UNKNOWN) [Internal Server Error]).
138
+ def can_associate(contact, domain)
139
+ call('contact.can_associate', contact, domain)
140
+ end
141
+
142
+ #TODO make this method return a boolean
143
+ alias_method :can_associate?, :can_associate
144
+
145
+ #List all contacts linked to the connected user (it will only return contacts when the apikey belong to a reseller).
146
+ #The array of returned contacts are mapped to contact objects, set map_contacts to false to get contact info hashes.
147
+ def list(opts = {}, map_contacts = true)
148
+ contacts = call('contact.list', opts)
149
+ if map_contacts
150
+ contacts.map! do |contact|
151
+ self.new(contact['handle'], contact)
152
+ end
153
+ end
154
+ contacts
155
+ end
156
+
157
+ #Check the same way as check and then update.
158
+ #Returns a contact object.
159
+ #TODO filter params.
160
+ def update(handle, contact)
161
+ contact = call('contact.update', handle, contact)
162
+ self.new(contact['handle'], contact)
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def attributes=(contact = nil)
169
+ contact_attributes = contact || info
170
+ @attributes = contact_attributes.reject {|k,v| !INFORMATION_ATTRIBUTES.include?(k.to_sym) }
171
+ @attributes['handle'] = @handle || contact_attributes['handle']
172
+ return @attributes
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,21 @@
1
+ module Gandi
2
+ class Domain
3
+ module Contacts
4
+ #Returns an hash of contacts, keys being the type of contact and values Gandi::Contact objects.
5
+ #TODO make the contact mapping optional.
6
+ def contacts
7
+ @attributes['contacts'].inject({}) { |h, contact| h[contact.first] = Gandi::Contact.new(contact.last['handle']); h }
8
+ end
9
+
10
+ #Change domain’s contact. Uses the API call domain.contacts.set
11
+ #Returns a Gandi::Operation object.
12
+ #NOTE: you cannot change the owner or reseller contacts.
13
+ #FIXME: is it possible to return the operation object and not the contacts hash passed as a param?
14
+ #TODO accept Gandi::Contact objects as values for the hash.
15
+ def contacts=(contacts)
16
+ operation_hash = self.class.call('domain.contacts.set', @fqdn, contacts)
17
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ module Gandi
2
+ class Domain
3
+ class Forward
4
+ include Gandi::GandiObjectMethods
5
+
6
+ attr_reader :domain, :source
7
+
8
+ def initialize(domain, source, attributes = nil)
9
+ @domain = domain
10
+ @source = source
11
+ @attributes = attributes || self.class.call('domain.forward.list', @domain.fqdn, {'source' => @source}).first
12
+ raise DataError, "Forward does not exist" unless @attributes
13
+ end
14
+
15
+ #Return the destinations emails for this forward.
16
+ def destinations
17
+ @attributes['destinations']
18
+ end
19
+
20
+ #Delete a forward.
21
+ #Return true.
22
+ def delete
23
+ self.class.call('domain.forward.delete', @domain.fqdn, @source)
24
+ end
25
+
26
+ #Update a forward.
27
+ #Return self.
28
+ def update(params)
29
+ @attributes = self.class.call('domain.forward.update', @domain.fqdn, @source, params)
30
+ return self
31
+ end
32
+
33
+ class << self
34
+ #Create a new forward.
35
+ #Return a Forward object.
36
+ def create(domain, source, params)
37
+ forward = call('domain.forward.create', domain.fqdn, source, params)
38
+ self.new(domain, source, forward)
39
+ end
40
+
41
+ #Count forwards for a domain.
42
+ #TODO: accept a fqdn string.
43
+ def count(domain, opts = {})
44
+ call('domain.forward.count', domain.fqdn, opts)
45
+ end
46
+
47
+ #List forwards for specified domain.
48
+ #Return an array of Forward objects.
49
+ #TODO: accept a fqdn string.
50
+ def list(domain, opts = {})
51
+ call('domain.forward.list', domain.fqdn, opts).map do |forward|
52
+ self.new(domain, forward['source'], forward)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,63 @@
1
+ module Gandi
2
+ class Domain
3
+ class Host
4
+ include Gandi::GandiObjectMethods
5
+
6
+ #The hostname of the Host
7
+ attr_reader :hostname
8
+
9
+ def initialize(hostname, attributes = nil)
10
+ @hostname = hostname
11
+ @attributes = attributes || info
12
+ end
13
+
14
+ #Delete a host.
15
+ #Returns a Gandi::Operation object.
16
+ def delete
17
+ operation_hash = self.class.call('domain.host.delete', @hostname)
18
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
19
+ end
20
+
21
+ #Display host information for a given domain.
22
+ def info
23
+ self.class.call('domain.host.info', @hostname)
24
+ end
25
+
26
+ #Return the host IP adresses.
27
+ def ips
28
+ @attributes['ips']
29
+ end
30
+
31
+ #Update a host.
32
+ #Return a Gandi::Operation object.
33
+ def update(ips)
34
+ operation_hash = self.class.call('domain.host.update', @hostname, ips)
35
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
36
+ end
37
+
38
+ class << self
39
+ #Create a host.
40
+ #Returns a Gandi::Operation object.
41
+ def create(hostname, ips)
42
+ operation_hash = call('domain.host.create', hostname, ips)
43
+ Gandi::Operation.new(operation_hash['id'], operation_hash)
44
+ end
45
+
46
+ #Count the glue records / hosts of a domain.
47
+ #TODO: accept a Domain object.
48
+ def count(fqdn, opts = {})
49
+ call('domain.host.count', fqdn, opts)
50
+ end
51
+
52
+ #List the glue records / hosts for a given domain.
53
+ #Return an array of Host objects.
54
+ #TODO: accept a Domain object.
55
+ def list(fqdn, opts = {})
56
+ call('domain.host.list', fqdn, opts).map do |host|
57
+ self.new(host['name'], host)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end