nzbn-ruby 0.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +239 -0
  6. data/Rakefile +8 -0
  7. data/lib/nzbn/api/addresses.rb +61 -0
  8. data/lib/nzbn/api/company_details.rb +32 -0
  9. data/lib/nzbn/api/email_addresses.rb +35 -0
  10. data/lib/nzbn/api/entities.rb +120 -0
  11. data/lib/nzbn/api/history.rb +81 -0
  12. data/lib/nzbn/api/industry_classifications.rb +35 -0
  13. data/lib/nzbn/api/organisation_parts.rb +92 -0
  14. data/lib/nzbn/api/phone_numbers.rb +35 -0
  15. data/lib/nzbn/api/privacy_settings.rb +33 -0
  16. data/lib/nzbn/api/roles.rb +53 -0
  17. data/lib/nzbn/api/trading_names.rb +35 -0
  18. data/lib/nzbn/api/watchlists.rb +120 -0
  19. data/lib/nzbn/api/websites.rb +35 -0
  20. data/lib/nzbn/client.rb +180 -0
  21. data/lib/nzbn/configuration.rb +28 -0
  22. data/lib/nzbn/error.rb +62 -0
  23. data/lib/nzbn/models/address.rb +12 -0
  24. data/lib/nzbn/models/base.rb +59 -0
  25. data/lib/nzbn/models/company.rb +20 -0
  26. data/lib/nzbn/models/email_address.rb +11 -0
  27. data/lib/nzbn/models/entity.rb +14 -0
  28. data/lib/nzbn/models/error_response.rb +19 -0
  29. data/lib/nzbn/models/full_entity.rb +50 -0
  30. data/lib/nzbn/models/industry_classification.rb +10 -0
  31. data/lib/nzbn/models/organisation_part.rb +14 -0
  32. data/lib/nzbn/models/phone_number.rb +16 -0
  33. data/lib/nzbn/models/privacy_settings.rb +26 -0
  34. data/lib/nzbn/models/role.rb +27 -0
  35. data/lib/nzbn/models/search_entity.rb +32 -0
  36. data/lib/nzbn/models/search_response.rb +50 -0
  37. data/lib/nzbn/models/trading_name.rb +10 -0
  38. data/lib/nzbn/models/watchlist.rb +14 -0
  39. data/lib/nzbn/models/website.rb +10 -0
  40. data/lib/nzbn/version.rb +5 -0
  41. data/lib/nzbn.rb +67 -0
  42. data/nzbn-ruby.gemspec +35 -0
  43. metadata +175 -0
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Organisation Parts API (OPN/GLN management)
6
+ class OrganisationParts
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # Search organisation parts
12
+ #
13
+ # @param nzbn [String] Parent NZBN to search within
14
+ # @param opn [String] OPN to search for
15
+ # @param name [String] Name filter
16
+ # @param function [String] Function filter (FUNCTION, PHYSICAL_LOCATION, DIGITAL_LOCATION)
17
+ # @param status [String] Status filter (ACTIVE, INACTIVE)
18
+ # @param page [Integer] Page number
19
+ # @param page_size [Integer] Results per page
20
+ # @return [Models::SearchResponse] Paginated results
21
+ #
22
+ def search(nzbn: nil, opn: nil, name: nil, function: nil, status: nil, page: nil, page_size: nil)
23
+ params = {}
24
+ params['nzbn'] = nzbn if nzbn
25
+ params['opn'] = opn if opn
26
+ params['name'] = name if name
27
+ params['function'] = function if function
28
+ params['status'] = status if status
29
+ params['page'] = page if page
30
+ params['page-size'] = page_size if page_size
31
+
32
+ response = @client.get('/entities/organisation-parts', params)
33
+ Models::SearchResponse.new(response, item_class: Models::OrganisationPart)
34
+ end
35
+
36
+ # List organisation parts for an entity
37
+ #
38
+ # @param nzbn [String] 13-digit NZBN
39
+ # @return [Array<Models::OrganisationPart>] List of organisation parts
40
+ #
41
+ def list(nzbn:)
42
+ response = @client.get("/entities/#{nzbn}/organisation-parts")
43
+ return [] unless response.is_a?(Array)
44
+
45
+ response.map { |op| Models::OrganisationPart.new(op) }
46
+ end
47
+
48
+ # Get an organisation part
49
+ #
50
+ # @param nzbn [String] 13-digit NZBN
51
+ # @param opn [String] Organisation part number
52
+ # @return [Models::OrganisationPart] Organisation part details
53
+ #
54
+ def get(nzbn:, opn:)
55
+ response = @client.get("/entities/#{nzbn}/organisation-parts/#{opn}")
56
+ Models::OrganisationPart.new(response)
57
+ end
58
+
59
+ # Create an organisation part
60
+ #
61
+ # @param nzbn [String] 13-digit NZBN
62
+ # @param organisation_part [Hash] Organisation part attributes
63
+ # @return [Models::OrganisationPart] Created organisation part
64
+ #
65
+ def create(nzbn:, organisation_part:)
66
+ response = @client.post("/entities/#{nzbn}/organisation-parts", organisation_part)
67
+ Models::OrganisationPart.new(response)
68
+ end
69
+
70
+ # Update an organisation part
71
+ #
72
+ # @param nzbn [String] 13-digit NZBN
73
+ # @param opn [String] Organisation part number
74
+ # @param organisation_part [Hash] Updated attributes
75
+ # @return [Models::OrganisationPart] Updated organisation part
76
+ #
77
+ def update(nzbn:, opn:, organisation_part:)
78
+ response = @client.put("/entities/#{nzbn}/organisation-parts/#{opn}", organisation_part)
79
+ Models::OrganisationPart.new(response)
80
+ end
81
+
82
+ # Get GLN allocation summary for an entity
83
+ #
84
+ # @param nzbn [String] 13-digit NZBN
85
+ # @return [Hash] Allocation summary
86
+ #
87
+ def gln_allocation(nzbn:)
88
+ @client.get("/entities/#{nzbn}/organisation-parts/allocation")
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Phone Numbers API
6
+ class PhoneNumbers
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # List phone numbers for an entity
12
+ #
13
+ # @param nzbn [String] 13-digit NZBN
14
+ # @return [Array<Models::PhoneNumber>] List of phone numbers
15
+ #
16
+ def list(nzbn:)
17
+ response = @client.get("/entities/#{nzbn}/phone-numbers")
18
+ return [] unless response.is_a?(Array)
19
+
20
+ response.map { |pn| Models::PhoneNumber.new(pn) }
21
+ end
22
+
23
+ # Add a phone number
24
+ #
25
+ # @param nzbn [String] 13-digit NZBN
26
+ # @param phone [Hash] Phone number attributes
27
+ # @return [Models::PhoneNumber] Created phone number
28
+ #
29
+ def create(nzbn:, phone:)
30
+ response = @client.post("/entities/#{nzbn}/phone-numbers", phone)
31
+ Models::PhoneNumber.new(response)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Privacy Settings API
6
+ class PrivacySettings
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # Get privacy settings for an entity
12
+ #
13
+ # @param nzbn [String] 13-digit NZBN
14
+ # @return [Models::PrivacySettings] Privacy settings
15
+ #
16
+ def get(nzbn:)
17
+ response = @client.get("/entities/#{nzbn}/privacy-settings")
18
+ Models::PrivacySettings.new(response)
19
+ end
20
+
21
+ # Update privacy settings
22
+ #
23
+ # @param nzbn [String] 13-digit NZBN
24
+ # @param settings [Hash] Privacy settings to update
25
+ # @return [Models::PrivacySettings] Updated settings
26
+ #
27
+ def update(nzbn:, settings:)
28
+ response = @client.put("/entities/#{nzbn}/privacy-settings", settings)
29
+ Models::PrivacySettings.new(response)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Roles API - Manage entity roles (directors, partners, etc.)
6
+ class Roles
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # List roles for an entity
12
+ #
13
+ # @param nzbn [String] 13-digit NZBN
14
+ # @return [Array<Models::Role>] List of roles
15
+ #
16
+ def list(nzbn:)
17
+ response = @client.get("/entities/#{nzbn}/roles")
18
+ return [] unless response.is_a?(Array)
19
+
20
+ response.map { |role| Models::Role.new(role) }
21
+ end
22
+
23
+ # Add a role to an entity
24
+ #
25
+ # @param nzbn [String] 13-digit NZBN
26
+ # @param role [Hash] Role attributes including roleType, rolePerson, etc.
27
+ # @return [Models::Role] Created role
28
+ #
29
+ # @example
30
+ # client.roles.create(nzbn: '9429041925676', role: {
31
+ # roleType: 'PARTNER_INDIVIDUAL',
32
+ # rolePerson: { firstName: 'John', lastName: 'Doe' },
33
+ # startDate: '2024-01-01'
34
+ # })
35
+ #
36
+ def create(nzbn:, role:)
37
+ response = @client.post("/entities/#{nzbn}/roles", role)
38
+ Models::Role.new(response)
39
+ end
40
+
41
+ # Update a role
42
+ #
43
+ # @param nzbn [String] 13-digit NZBN
44
+ # @param role [Hash] Updated role attributes
45
+ # @return [Models::Role] Updated role
46
+ #
47
+ def update(nzbn:, role:)
48
+ response = @client.put("/entities/#{nzbn}/roles", role)
49
+ Models::Role.new(response)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Trading Names API
6
+ class TradingNames
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # List trading names for an entity
12
+ #
13
+ # @param nzbn [String] 13-digit NZBN
14
+ # @return [Array<Models::TradingName>] List of trading names
15
+ #
16
+ def list(nzbn:)
17
+ response = @client.get("/entities/#{nzbn}/trading-names")
18
+ return [] unless response.is_a?(Array)
19
+
20
+ response.map { |tn| Models::TradingName.new(tn) }
21
+ end
22
+
23
+ # Add a trading name
24
+ #
25
+ # @param nzbn [String] 13-digit NZBN
26
+ # @param trading_name [Hash] Trading name attributes
27
+ # @return [Models::TradingName] Created trading name
28
+ #
29
+ def create(nzbn:, trading_name:)
30
+ response = @client.post("/entities/#{nzbn}/trading-names", trading_name)
31
+ Models::TradingName.new(response)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Watchlists API - Manage change notification watchlists
6
+ class Watchlists
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # List watchlists
12
+ #
13
+ # @param name [String] Filter by name (partial match)
14
+ # @param organisation_id [String] Filter by organisation ID
15
+ # @param page [Integer] Page number
16
+ # @param page_size [Integer] Results per page
17
+ # @param sort_by [String] Sort field
18
+ # @param sort_order [String] ASC or DESC
19
+ # @param change_event_types [String] Filter by change event types
20
+ # @return [Models::SearchResponse] Paginated watchlist results
21
+ #
22
+ def list(name: nil, organisation_id: nil, page: nil, page_size: nil,
23
+ sort_by: nil, sort_order: nil, change_event_types: nil)
24
+ params = {}
25
+ params['name'] = name if name
26
+ params['organisation-id'] = organisation_id if organisation_id
27
+ params['page'] = page if page
28
+ params['page-size'] = page_size if page_size
29
+ params['sort-by'] = sort_by if sort_by
30
+ params['sortorder'] = sort_order if sort_order
31
+ params['change-event-types'] = change_event_types if change_event_types
32
+
33
+ response = @client.get('/watchlists', params)
34
+ Models::SearchResponse.new(response, item_class: Models::Watchlist)
35
+ end
36
+
37
+ # Get a watchlist by ID
38
+ #
39
+ # @param watchlist_id [String] Watchlist ID
40
+ # @return [Models::Watchlist] Watchlist details
41
+ #
42
+ def get(watchlist_id:)
43
+ response = @client.get("/watchlists/#{watchlist_id}")
44
+ Models::Watchlist.new(response)
45
+ end
46
+
47
+ # Create a new watchlist
48
+ #
49
+ # @param watchlist [Hash] Watchlist attributes
50
+ # @return [Models::Watchlist] Created watchlist
51
+ #
52
+ # @example
53
+ # client.watchlists.create(watchlist: {
54
+ # name: 'My Watchlist',
55
+ # channel: 'EMAIL',
56
+ # frequency: 'DAILY',
57
+ # changeEventTypes: 'ALL',
58
+ # adminEmailAddress: 'admin@example.com'
59
+ # })
60
+ #
61
+ def create(watchlist:)
62
+ response = @client.post('/watchlists', watchlist)
63
+ Models::Watchlist.new(response)
64
+ end
65
+
66
+ # Update a watchlist
67
+ #
68
+ # @param watchlist_id [String] Watchlist ID
69
+ # @param watchlist [Hash] Updated watchlist attributes
70
+ # @return [Models::Watchlist] Updated watchlist
71
+ #
72
+ def update(watchlist_id:, watchlist:)
73
+ response = @client.put("/watchlists/#{watchlist_id}", watchlist)
74
+ Models::Watchlist.new(response)
75
+ end
76
+
77
+ # Delete a watchlist
78
+ #
79
+ # @param watchlist_id [String] Watchlist ID
80
+ # @return [Boolean] True if successful
81
+ #
82
+ def delete(watchlist_id:)
83
+ @client.delete("/watchlists/#{watchlist_id}")
84
+ true
85
+ end
86
+
87
+ # List NZBNs in a watchlist
88
+ #
89
+ # @param watchlist_id [String] Watchlist ID
90
+ # @return [Array<String>] List of NZBNs
91
+ #
92
+ def list_nzbns(watchlist_id:)
93
+ response = @client.get("/watchlists/#{watchlist_id}/nzbns")
94
+ response['nzbns'] || []
95
+ end
96
+
97
+ # Add NZBNs to a watchlist
98
+ #
99
+ # @param watchlist_id [String] Watchlist ID
100
+ # @param nzbns [Array<String>] NZBNs to add
101
+ # @return [Boolean] True if successful
102
+ #
103
+ def add_nzbns(watchlist_id:, nzbns:)
104
+ @client.post("/watchlists/#{watchlist_id}/nzbns", { nzbns: nzbns })
105
+ true
106
+ end
107
+
108
+ # Remove NZBNs from a watchlist
109
+ #
110
+ # @param watchlist_id [String] Watchlist ID
111
+ # @param nzbns [Array<String>] NZBNs to remove
112
+ # @return [Boolean] True if successful
113
+ #
114
+ def remove_nzbns(watchlist_id:, nzbns:)
115
+ @client.delete("/watchlists/#{watchlist_id}/nzbns", { nzbns: nzbns })
116
+ true
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Api
5
+ # Websites API
6
+ class Websites
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # List websites for an entity
12
+ #
13
+ # @param nzbn [String] 13-digit NZBN
14
+ # @return [Array<Models::Website>] List of websites
15
+ #
16
+ def list(nzbn:)
17
+ response = @client.get("/entities/#{nzbn}/websites")
18
+ return [] unless response.is_a?(Array)
19
+
20
+ response.map { |ws| Models::Website.new(ws) }
21
+ end
22
+
23
+ # Add a website
24
+ #
25
+ # @param nzbn [String] 13-digit NZBN
26
+ # @param website [Hash] Website attributes
27
+ # @return [Models::Website] Created website
28
+ #
29
+ def create(nzbn:, website:)
30
+ response = @client.post("/entities/#{nzbn}/websites", website)
31
+ Models::Website.new(response)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday/multipart'
5
+ require 'json'
6
+ require 'securerandom'
7
+
8
+ module Nzbn
9
+ # Main HTTP client for NZBN API
10
+ #
11
+ # @example
12
+ # client = Nzbn::Client.new
13
+ # client.entities.search(search_term: 'Company Name')
14
+ #
15
+ class Client
16
+ attr_reader :configuration
17
+
18
+ def initialize(api_key: nil, base_url: nil, timeout: nil)
19
+ @configuration = Nzbn.configuration&.dup || Configuration.new
20
+ @configuration.api_key = api_key if api_key
21
+ @configuration.base_url = base_url if base_url
22
+ @configuration.timeout = timeout if timeout
23
+
24
+ validate_configuration!
25
+ end
26
+
27
+ # API Resource Accessors
28
+
29
+ def entities
30
+ @entities ||= Api::Entities.new(self)
31
+ end
32
+
33
+ def addresses
34
+ @addresses ||= Api::Addresses.new(self)
35
+ end
36
+
37
+ def roles
38
+ @roles ||= Api::Roles.new(self)
39
+ end
40
+
41
+ def trading_names
42
+ @trading_names ||= Api::TradingNames.new(self)
43
+ end
44
+
45
+ def phone_numbers
46
+ @phone_numbers ||= Api::PhoneNumbers.new(self)
47
+ end
48
+
49
+ def email_addresses
50
+ @email_addresses ||= Api::EmailAddresses.new(self)
51
+ end
52
+
53
+ def websites
54
+ @websites ||= Api::Websites.new(self)
55
+ end
56
+
57
+ def industry_classifications
58
+ @industry_classifications ||= Api::IndustryClassifications.new(self)
59
+ end
60
+
61
+ def privacy_settings
62
+ @privacy_settings ||= Api::PrivacySettings.new(self)
63
+ end
64
+
65
+ def company_details
66
+ @company_details ||= Api::CompanyDetails.new(self)
67
+ end
68
+
69
+ def watchlists
70
+ @watchlists ||= Api::Watchlists.new(self)
71
+ end
72
+
73
+ def organisation_parts
74
+ @organisation_parts ||= Api::OrganisationParts.new(self)
75
+ end
76
+
77
+ def history
78
+ @history ||= Api::History.new(self)
79
+ end
80
+
81
+ # HTTP Methods
82
+
83
+ def get(path, params = {}, headers = {})
84
+ request(:get, path, params, headers)
85
+ end
86
+
87
+ def post(path, body = {}, headers = {})
88
+ request(:post, path, body, headers)
89
+ end
90
+
91
+ def put(path, body = {}, headers = {})
92
+ request(:put, path, body, headers)
93
+ end
94
+
95
+ def patch(path, body = {}, headers = {})
96
+ request(:patch, path, body, headers)
97
+ end
98
+
99
+ def delete(path, params = {}, headers = {})
100
+ request(:delete, path, params, headers)
101
+ end
102
+
103
+ private
104
+
105
+ def validate_configuration!
106
+ return if configuration.valid?
107
+
108
+ raise ConfigurationError, 'API key is required. Configure with Nzbn.configure or pass api_key to Client.new'
109
+ end
110
+
111
+ def connection
112
+ @connection ||= Faraday.new(url: configuration.base_url) do |conn|
113
+ conn.request :json
114
+ conn.response :json, content_type: /\bjson$/
115
+ conn.adapter Faraday.default_adapter
116
+
117
+ conn.options.timeout = configuration.timeout
118
+ conn.options.open_timeout = configuration.open_timeout
119
+
120
+ if configuration.logger
121
+ conn.response :logger, configuration.logger
122
+ end
123
+ end
124
+ end
125
+
126
+ def request(method, path, data, headers)
127
+ request_id = SecureRandom.uuid
128
+
129
+ merged_headers = default_headers(request_id).merge(headers)
130
+
131
+ response = connection.public_send(method) do |req|
132
+ req.url path
133
+ req.headers = merged_headers
134
+
135
+ case method
136
+ when :get, :delete
137
+ req.params = data unless data.empty?
138
+ else
139
+ req.body = data
140
+ end
141
+ end
142
+
143
+ handle_response(response)
144
+ rescue Faraday::TimeoutError => e
145
+ raise TimeoutError, "Request timed out: #{e.message}"
146
+ rescue Faraday::ConnectionFailed => e
147
+ raise ConnectionError, "Connection failed: #{e.message}"
148
+ end
149
+
150
+ def default_headers(request_id)
151
+ {
152
+ 'Ocp-Apim-Subscription-Key' => configuration.api_key,
153
+ 'api-business-govt-nz-Request-Id' => request_id,
154
+ 'Accept' => 'application/json',
155
+ 'Content-Type' => 'application/json'
156
+ }
157
+ end
158
+
159
+ def handle_response(response)
160
+ case response.status
161
+ when 200..299
162
+ response.body
163
+ when 400
164
+ raise ValidationError.new(response: response.body, status: response.status)
165
+ when 401
166
+ raise AuthenticationError.new('Authentication failed', response: response.body, status: response.status)
167
+ when 403
168
+ raise AuthorizationError.new('Authorization denied', response: response.body, status: response.status)
169
+ when 404
170
+ raise NotFoundError.new('Resource not found', response: response.body, status: response.status)
171
+ when 412
172
+ raise PreconditionFailedError.new('Precondition failed', response: response.body, status: response.status)
173
+ when 500..599
174
+ raise ServerError.new('Server error', response: response.body, status: response.status)
175
+ else
176
+ raise ApiError.new("Unexpected response: #{response.status}", response: response.body, status: response.status)
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ # Configuration class for NZBN client settings
5
+ #
6
+ # @example
7
+ # Nzbn.configure do |config|
8
+ # config.api_key = 'your-api-key'
9
+ # config.base_url = 'https://api.business.govt.nz/gateway/nzbn/v5'
10
+ # config.timeout = 30
11
+ # end
12
+ #
13
+ class Configuration
14
+ attr_accessor :api_key, :base_url, :timeout, :open_timeout, :logger
15
+
16
+ def initialize
17
+ @api_key = nil
18
+ @base_url = Nzbn::DEFAULT_BASE_URL
19
+ @timeout = 30
20
+ @open_timeout = 10
21
+ @logger = nil
22
+ end
23
+
24
+ def valid?
25
+ !api_key.nil? && !api_key.empty?
26
+ end
27
+ end
28
+ end
data/lib/nzbn/error.rb ADDED
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ # Base error class for NZBN client
5
+ class Error < StandardError
6
+ attr_reader :response, :code
7
+
8
+ def initialize(message = nil, response: nil, code: nil)
9
+ @response = response
10
+ @code = code
11
+ super(message)
12
+ end
13
+ end
14
+
15
+ # API error with status code and response body
16
+ class ApiError < Error
17
+ attr_reader :status, :error_code, :error_description, :errors
18
+
19
+ def initialize(message = nil, response: nil, status: nil)
20
+ @status = status
21
+ parse_response(response) if response
22
+ super(message || @error_description || 'API Error', response: response, code: @error_code)
23
+ end
24
+
25
+ private
26
+
27
+ def parse_response(response)
28
+ return unless response.is_a?(Hash)
29
+
30
+ @error_code = response['errorCode']
31
+ @error_description = response['errorDescription']
32
+ @errors = response['list'] || []
33
+ end
34
+ end
35
+
36
+ # 400 Bad Request - Validation errors
37
+ class ValidationError < ApiError; end
38
+
39
+ # 401 Unauthorized - Authentication required
40
+ class AuthenticationError < ApiError; end
41
+
42
+ # 403 Forbidden - Authorization denied
43
+ class AuthorizationError < ApiError; end
44
+
45
+ # 404 Not Found
46
+ class NotFoundError < ApiError; end
47
+
48
+ # 412 Precondition Failed - ETag mismatch
49
+ class PreconditionFailedError < ApiError; end
50
+
51
+ # 500 Internal Server Error
52
+ class ServerError < ApiError; end
53
+
54
+ # Connection/network errors
55
+ class ConnectionError < Error; end
56
+
57
+ # Timeout errors
58
+ class TimeoutError < Error; end
59
+
60
+ # Configuration errors
61
+ class ConfigurationError < Error; end
62
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nzbn
4
+ module Models
5
+ # Address model
6
+ class Address < Base
7
+ attr_accessor :unique_identifier, :start_date, :end_date,
8
+ :care_of, :address1, :address2, :address3, :address4,
9
+ :post_code, :country_code, :address_type, :paf_id, :description
10
+ end
11
+ end
12
+ end