rodoo 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.
- checksums.yaml +7 -0
- data/.env.example +2 -0
- data/.rubocop.yml +32 -0
- data/CHANGELOG.md +5 -0
- data/CLAUDE.md +25 -0
- data/LICENSE.txt +21 -0
- data/Odoo_API.md +424 -0
- data/README.md +230 -0
- data/Rakefile +12 -0
- data/lib/rodoo/configuration.rb +42 -0
- data/lib/rodoo/connection.rb +128 -0
- data/lib/rodoo/domain_builder.rb +62 -0
- data/lib/rodoo/errors.rb +31 -0
- data/lib/rodoo/model.rb +276 -0
- data/lib/rodoo/models/accounting_entry.rb +53 -0
- data/lib/rodoo/models/accounting_entry_line.rb +24 -0
- data/lib/rodoo/models/analytic_account.rb +7 -0
- data/lib/rodoo/models/analytic_plan.rb +7 -0
- data/lib/rodoo/models/contact.rb +18 -0
- data/lib/rodoo/models/customer_credit_note.rb +15 -0
- data/lib/rodoo/models/customer_invoice.rb +15 -0
- data/lib/rodoo/models/journal_entry.rb +15 -0
- data/lib/rodoo/models/product.rb +18 -0
- data/lib/rodoo/models/project.rb +18 -0
- data/lib/rodoo/models/provider_credit_note.rb +15 -0
- data/lib/rodoo/models/provider_invoice.rb +15 -0
- data/lib/rodoo/version.rb +5 -0
- data/lib/rodoo.rb +45 -0
- metadata +72 -0
data/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Rodoo
|
|
2
|
+
|
|
3
|
+
A Ruby gem wrapping Odoo's JSON-RPC 2.0 API (Odoo v19+) with an Active Record-style interface.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Ruby 3.0+
|
|
8
|
+
- Odoo v19 or higher (uses the `/json/2/` API endpoint)
|
|
9
|
+
- Odoo API key for authentication
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add to your Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "rodoo"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then run:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bundle install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or install directly:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
gem install rodoo
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
### Using environment variables
|
|
34
|
+
|
|
35
|
+
Rodoo automatically reads `ODOO_URL` and `ODOO_API_KEY` from the environment:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
export ODOO_URL="https://your-instance.odoo.com"
|
|
39
|
+
export ODOO_API_KEY="your-api-key"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Explicit configuration
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
Rodoo.configure do |config|
|
|
46
|
+
config.url = "https://your-instance.odoo.com"
|
|
47
|
+
config.api_key = "your-api-key"
|
|
48
|
+
config.timeout = 30 # Request timeout in seconds (default: 30)
|
|
49
|
+
config.open_timeout = 10 # Connection timeout in seconds (default: 10)
|
|
50
|
+
config.logger = Logger.new($stdout)
|
|
51
|
+
config.log_level = :debug # :info or :debug (default: :info)
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
### Finding records
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Find by ID
|
|
61
|
+
contact = Rodoo::Contact.find(42)
|
|
62
|
+
contact.name # => "Acme Corp"
|
|
63
|
+
contact.email # => "contact@acme.com"
|
|
64
|
+
|
|
65
|
+
# Find by attributes
|
|
66
|
+
contact = Rodoo::Contact.find_by(email: "john@example.com")
|
|
67
|
+
contact = Rodoo::Contact.find_by(name: "Acme Corp", is_company: true)
|
|
68
|
+
|
|
69
|
+
# Find by string condition
|
|
70
|
+
contact = Rodoo::Contact.find_by("credit_limit > 1000")
|
|
71
|
+
|
|
72
|
+
# Find by raw domain
|
|
73
|
+
contact = Rodoo::Contact.find_by([["name", "ilike", "%acme%"]])
|
|
74
|
+
|
|
75
|
+
# Find by attributes (raises NotFoundError if not found)
|
|
76
|
+
contact = Rodoo::Contact.find_by!(email: "john@example.com")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Querying records
|
|
80
|
+
|
|
81
|
+
Rodoo supports multiple query syntaxes for convenience:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# Keyword arguments (equality)
|
|
85
|
+
companies = Rodoo::Contact.where(is_company: true)
|
|
86
|
+
active_companies = Rodoo::Contact.where(is_company: true, active: true)
|
|
87
|
+
|
|
88
|
+
# String conditions (parsed automatically)
|
|
89
|
+
high_credit = Rodoo::Contact.where("credit_limit > 1000")
|
|
90
|
+
|
|
91
|
+
# Multiple string conditions
|
|
92
|
+
filtered = Rodoo::Contact.where(["credit_limit > 1000", "active = true"])
|
|
93
|
+
|
|
94
|
+
# Raw Odoo domain syntax (for complex queries)
|
|
95
|
+
# See: https://www.odoo.com/documentation/19.0/developer/reference/backend/orm.html#reference-orm-domains
|
|
96
|
+
contacts = Rodoo::Contact.where([["name", "ilike", "%acme%"]])
|
|
97
|
+
|
|
98
|
+
# With pagination
|
|
99
|
+
contacts = Rodoo::Contact.where(is_company: true, limit: 10, offset: 20)
|
|
100
|
+
|
|
101
|
+
# Select specific fields
|
|
102
|
+
contacts = Rodoo::Contact.where(active: true, fields: ["name", "email"])
|
|
103
|
+
|
|
104
|
+
# Fetch all (with optional limit)
|
|
105
|
+
all_contacts = Rodoo::Contact.all(limit: 100)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Supported operators in string conditions: `=`, `!=`, `<>`, `<`, `>`, `<=`, `>=`, `like`, `ilike`, `=like`, `=ilike`
|
|
109
|
+
|
|
110
|
+
### Creating records
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# Create and persist immediately
|
|
114
|
+
contact = Rodoo::Contact.create(
|
|
115
|
+
name: "New Contact",
|
|
116
|
+
email: "new@example.com",
|
|
117
|
+
is_company: false
|
|
118
|
+
)
|
|
119
|
+
contact.id # => 123
|
|
120
|
+
|
|
121
|
+
# Build and save later
|
|
122
|
+
contact = Rodoo::Contact.new(name: "Draft Contact")
|
|
123
|
+
contact.email = "draft@example.com"
|
|
124
|
+
contact.save
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Updating records
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
contact = Rodoo::Contact.find(42)
|
|
131
|
+
|
|
132
|
+
# Update specific attributes
|
|
133
|
+
contact.update(email: "updated@example.com", phone: "+1234567890")
|
|
134
|
+
|
|
135
|
+
# Or modify and save
|
|
136
|
+
contact.email = "another@example.com"
|
|
137
|
+
contact.save
|
|
138
|
+
|
|
139
|
+
# Reload from Odoo
|
|
140
|
+
contact.reload
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Deleting records
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
contact = Rodoo::Contact.find(42)
|
|
147
|
+
contact.destroy
|
|
148
|
+
|
|
149
|
+
contact.destroyed? # => true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Available models
|
|
153
|
+
|
|
154
|
+
Rodoo includes pre-built models for common Odoo objects:
|
|
155
|
+
|
|
156
|
+
| Class | Odoo Model |
|
|
157
|
+
|-------|------------|
|
|
158
|
+
| `Rodoo::Contact` | `res.partner` |
|
|
159
|
+
| `Rodoo::Project` | `project.project` |
|
|
160
|
+
| `Rodoo::AnalyticAccount` | `account.analytic.account` |
|
|
161
|
+
| `Rodoo::AnalyticPlan` | `account.analytic.plan` |
|
|
162
|
+
| `Rodoo::AccountingEntry` | `account.move` (all types) |
|
|
163
|
+
| `Rodoo::CustomerInvoice` | `account.move` (move_type: out_invoice) |
|
|
164
|
+
| `Rodoo::ProviderInvoice` | `account.move` (move_type: in_invoice) |
|
|
165
|
+
| `Rodoo::CustomerCreditNote` | `account.move` (move_type: out_refund) |
|
|
166
|
+
| `Rodoo::ProviderCreditNote` | `account.move` (move_type: in_refund) |
|
|
167
|
+
| `Rodoo::JournalEntry` | `account.move` (move_type: entry) |
|
|
168
|
+
|
|
169
|
+
### Custom models
|
|
170
|
+
|
|
171
|
+
Create your own model by inheriting from `Rodoo::Model`:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
class Product < Rodoo::Model
|
|
175
|
+
model_name "product.product"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Use it like any other model
|
|
179
|
+
product = Product.find(1)
|
|
180
|
+
products = Product.where(type: "consu", limit: 10)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Error handling
|
|
184
|
+
|
|
185
|
+
Rodoo provides a structured exception hierarchy:
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
begin
|
|
189
|
+
contact = Rodoo::Contact.find(999999)
|
|
190
|
+
rescue Rodoo::NotFoundError => e
|
|
191
|
+
puts "Contact not found: #{e.message}"
|
|
192
|
+
rescue Rodoo::AuthenticationError => e
|
|
193
|
+
puts "Invalid credentials"
|
|
194
|
+
rescue Rodoo::AccessDeniedError => e
|
|
195
|
+
puts "Permission denied"
|
|
196
|
+
rescue Rodoo::ValidationError => e
|
|
197
|
+
puts "Validation failed: #{e.message}"
|
|
198
|
+
rescue Rodoo::TimeoutError => e
|
|
199
|
+
puts "Request timed out"
|
|
200
|
+
rescue Rodoo::ConnectionError => e
|
|
201
|
+
puts "Connection failed: #{e.message}"
|
|
202
|
+
rescue Rodoo::APIError => e
|
|
203
|
+
puts "API error: #{e.message} (code: #{e.code})"
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Development
|
|
208
|
+
|
|
209
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
rake test # Run tests
|
|
213
|
+
rake rubocop # Run linter
|
|
214
|
+
rake # Run both
|
|
215
|
+
bin/console # Interactive REPL
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
To install the gem locally:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
bundle exec rake install
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Contributing
|
|
225
|
+
|
|
226
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dekuple/rodoo.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rodoo
|
|
4
|
+
class Configuration
|
|
5
|
+
VALID_OPTIONS = %i[
|
|
6
|
+
url
|
|
7
|
+
api_key
|
|
8
|
+
timeout
|
|
9
|
+
open_timeout
|
|
10
|
+
logger
|
|
11
|
+
log_level
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
attr_accessor(*VALID_OPTIONS)
|
|
15
|
+
|
|
16
|
+
DEFAULT_TIMEOUT = 30
|
|
17
|
+
DEFAULT_OPEN_TIMEOUT = 10
|
|
18
|
+
DEFAULT_LOG_LEVEL = :info
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@url = ENV.fetch("ODOO_URL", nil)
|
|
22
|
+
@api_key = ENV.fetch("ODOO_API_KEY", nil)
|
|
23
|
+
@timeout = DEFAULT_TIMEOUT
|
|
24
|
+
@open_timeout = DEFAULT_OPEN_TIMEOUT
|
|
25
|
+
@log_level = DEFAULT_LOG_LEVEL
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def validate!
|
|
29
|
+
raise ConfigurationError, "url is required" if url.nil? || url.empty?
|
|
30
|
+
raise ConfigurationError, "api_key or username/password is required" unless api_key
|
|
31
|
+
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns a hash of all options (useful for debugging)
|
|
36
|
+
def to_h
|
|
37
|
+
VALID_OPTIONS.each_with_object({}) do |key, hash|
|
|
38
|
+
hash[key] = send(key)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module Rodoo
|
|
8
|
+
class Connection
|
|
9
|
+
attr_reader :config
|
|
10
|
+
|
|
11
|
+
def initialize(config)
|
|
12
|
+
@config = config
|
|
13
|
+
@uri = URI.parse(config.url)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def execute(model, method, params = {})
|
|
17
|
+
path = "/json/2/#{model}/#{method}"
|
|
18
|
+
response = post(path, params)
|
|
19
|
+
handle_response(response)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def post(path, payload)
|
|
25
|
+
http = build_http_client
|
|
26
|
+
request = build_request(path, payload)
|
|
27
|
+
log_request(path, payload)
|
|
28
|
+
response = http.request(request)
|
|
29
|
+
log_response(response)
|
|
30
|
+
response
|
|
31
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
32
|
+
raise TimeoutError.new("Request timed out", original_error: e)
|
|
33
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET => e
|
|
34
|
+
raise ConnectionError.new("Failed to connect to #{config.url}", original_error: e)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_http_client
|
|
38
|
+
Net::HTTP.new(@uri.host, @uri.port).tap do |http|
|
|
39
|
+
http.use_ssl = @uri.scheme == "https"
|
|
40
|
+
http.read_timeout = config.timeout
|
|
41
|
+
http.open_timeout = config.open_timeout
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def build_request(path, payload)
|
|
46
|
+
Net::HTTP::Post.new(path).tap do |request|
|
|
47
|
+
request["Content-Type"] = "application/json"
|
|
48
|
+
request["Authorization"] = "Bearer #{config.api_key}" if config.api_key
|
|
49
|
+
request.body = payload.to_json
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def handle_response(response)
|
|
54
|
+
case response
|
|
55
|
+
when Net::HTTPSuccess
|
|
56
|
+
parse_response(response.body)
|
|
57
|
+
when Net::HTTPUnauthorized
|
|
58
|
+
raise AuthenticationError.new("Invalid credentials", code: response.code)
|
|
59
|
+
when Net::HTTPForbidden
|
|
60
|
+
raise AccessDeniedError.new("Access denied", code: response.code)
|
|
61
|
+
when Net::HTTPNotFound
|
|
62
|
+
raise NotFoundError.new("Endpoint not found", code: response.code)
|
|
63
|
+
else
|
|
64
|
+
raise_api_error(response)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def raise_api_error(response)
|
|
69
|
+
error_data = parse_error_body(response.body)
|
|
70
|
+
unless error_data
|
|
71
|
+
raise APIError.new("HTTP #{response.code}: #{response.message}", code: response.code)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
error_class = map_odoo_exception(error_data[:name])
|
|
75
|
+
message = error_data[:message] || "HTTP #{response.code}: #{response.message}"
|
|
76
|
+
raise error_class.new(message, code: response.code, data: error_data)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def parse_error_body(body)
|
|
80
|
+
return nil if body.nil? || body.empty?
|
|
81
|
+
|
|
82
|
+
JSON.parse(body, symbolize_names: true)
|
|
83
|
+
rescue JSON::ParserError
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def map_odoo_exception(exception_name)
|
|
88
|
+
case exception_name
|
|
89
|
+
when /ValidationError/, /UserError/
|
|
90
|
+
ValidationError
|
|
91
|
+
when /AccessError/, /AccessDenied/
|
|
92
|
+
AccessDeniedError
|
|
93
|
+
when /MissingError/
|
|
94
|
+
NotFoundError
|
|
95
|
+
else
|
|
96
|
+
APIError
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def parse_response(body)
|
|
101
|
+
return nil if body.nil? || body.empty?
|
|
102
|
+
|
|
103
|
+
JSON.parse(body, symbolize_names: true)
|
|
104
|
+
rescue JSON::ParserError => e
|
|
105
|
+
raise Error.new("Invalid JSON response", original_error: e)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def log_request(path, payload)
|
|
109
|
+
return unless config.logger
|
|
110
|
+
|
|
111
|
+
if config.log_level == :debug
|
|
112
|
+
config.logger.debug("[Rodoo] POST #{path} #{payload.to_json}")
|
|
113
|
+
else
|
|
114
|
+
config.logger.info("[Rodoo] POST #{path}")
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def log_response(response)
|
|
119
|
+
return unless config.logger
|
|
120
|
+
|
|
121
|
+
if config.log_level == :debug
|
|
122
|
+
config.logger.debug("[Rodoo] Response #{response.code}: #{response.body}")
|
|
123
|
+
else
|
|
124
|
+
config.logger.info("[Rodoo] Response #{response.code}")
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rodoo
|
|
4
|
+
# Converts various condition formats into Odoo domain arrays.
|
|
5
|
+
#
|
|
6
|
+
# Supports:
|
|
7
|
+
# - Hash: equality conditions `{ name: "John" }` → `[["name", "=", "John"]]`
|
|
8
|
+
# - String: parsed condition `"age > 18"` → `[["age", ">", 18]]`
|
|
9
|
+
# - Array of strings: `["age > 18", "active = true"]`
|
|
10
|
+
# - Array of arrays: raw Odoo domain (passthrough)
|
|
11
|
+
#
|
|
12
|
+
module DomainBuilder
|
|
13
|
+
CONDITION_PATTERN = /\A(\w+)\s*(=|!=|<>|<=|>=|<|>|like|ilike|=like|=ilike)\s*(.+)\z/i
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
def build(conditions, attrs = {})
|
|
18
|
+
return hash_to_domain(attrs) if attrs.any?
|
|
19
|
+
return [] if conditions.nil?
|
|
20
|
+
|
|
21
|
+
case conditions
|
|
22
|
+
when String then [parse_string_condition(conditions)]
|
|
23
|
+
when Hash then hash_to_domain(conditions)
|
|
24
|
+
when Array then array_to_domain(conditions)
|
|
25
|
+
else raise ArgumentError, "Invalid conditions: #{conditions.class}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def hash_to_domain(hash)
|
|
30
|
+
hash.map { |k, v| [k.to_s, "=", v] }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def array_to_domain(arr)
|
|
34
|
+
return [] if arr.empty?
|
|
35
|
+
return arr.map { |s| parse_string_condition(s) } if arr.first.is_a?(String)
|
|
36
|
+
|
|
37
|
+
arr
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def parse_string_condition(str)
|
|
41
|
+
match = str.strip.match(CONDITION_PATTERN)
|
|
42
|
+
raise ArgumentError, "Invalid condition: '#{str}'" unless match
|
|
43
|
+
|
|
44
|
+
field = match[1]
|
|
45
|
+
operator = match[2] == "<>" ? "!=" : match[2].downcase
|
|
46
|
+
value = parse_value(match[3].strip)
|
|
47
|
+
|
|
48
|
+
[field, operator, value]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def parse_value(str)
|
|
52
|
+
case str
|
|
53
|
+
when /\A["'](.*)["']\z/ then ::Regexp.last_match(1)
|
|
54
|
+
when /\Atrue\z/i then true
|
|
55
|
+
when /\Afalse\z/i then false
|
|
56
|
+
when /\A-?\d+\z/ then str.to_i
|
|
57
|
+
when /\A-?\d+\.\d+\z/ then str.to_f
|
|
58
|
+
else str
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/rodoo/errors.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rodoo
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
attr_reader :original_error
|
|
6
|
+
|
|
7
|
+
def initialize(message = nil, original_error: nil)
|
|
8
|
+
@original_error = original_error
|
|
9
|
+
super(message)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ConfigurationError < Error; end
|
|
14
|
+
class ConnectionError < Error; end
|
|
15
|
+
class TimeoutError < ConnectionError; end
|
|
16
|
+
|
|
17
|
+
class APIError < Error
|
|
18
|
+
attr_reader :code, :data
|
|
19
|
+
|
|
20
|
+
def initialize(message = nil, code: nil, data: nil, **kwargs)
|
|
21
|
+
@code = code
|
|
22
|
+
@data = data
|
|
23
|
+
super(message, **kwargs)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class AuthenticationError < APIError; end
|
|
28
|
+
class NotFoundError < APIError; end
|
|
29
|
+
class ValidationError < APIError; end
|
|
30
|
+
class AccessDeniedError < APIError; end
|
|
31
|
+
end
|