gorgias-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.
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ class Configuration
5
+ # Gorgias account domain (e.g., "mycompany" for mycompany.gorgias.com)
6
+ attr_accessor :domain
7
+
8
+ # Agent email address used for Basic auth
9
+ attr_accessor :username
10
+
11
+ # API token / password used for Basic auth
12
+ attr_accessor :api_key
13
+
14
+ # HTTP read timeout in seconds
15
+ attr_accessor :timeout
16
+
17
+ # HTTP connection open timeout in seconds
18
+ attr_accessor :open_timeout
19
+
20
+ # Optional logger — must respond to #debug, #info, #warn, #error
21
+ attr_accessor :logger
22
+
23
+ def initialize
24
+ @timeout = 30
25
+ @open_timeout = 10
26
+ end
27
+
28
+ # Returns the base URL for the configured domain.
29
+ # @raise [ConfigurationError] if domain is blank
30
+ def base_url
31
+ raise ConfigurationError, "Gorgias domain is required" if domain.nil? || domain.strip.empty?
32
+
33
+ "https://#{domain.strip}.gorgias.com"
34
+ end
35
+
36
+ # Raises if any required field is missing.
37
+ def validate!
38
+ raise ConfigurationError, "domain is required" if domain.nil? || domain.strip.empty?
39
+ raise ConfigurationError, "username is required" if username.nil? || username.strip.empty?
40
+ raise ConfigurationError, "api_key is required" if api_key.nil? || api_key.strip.empty?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ # Base error class for all Gorgias errors
5
+ class Error < StandardError; end
6
+
7
+ # Raised when the client is misconfigured (missing domain, credentials, etc.)
8
+ class ConfigurationError < Error; end
9
+
10
+ # Base class for all API-level errors
11
+ class APIError < Error
12
+ attr_reader :http_status, :http_body, :http_headers, :error_message
13
+
14
+ def initialize(message = nil, http_status: nil, http_body: nil, http_headers: nil)
15
+ @http_status = http_status
16
+ @http_body = http_body
17
+ @http_headers = http_headers
18
+ @error_message = extract_message(http_body)
19
+ super(message || @error_message || "API error (HTTP #{http_status})")
20
+ end
21
+
22
+ private
23
+
24
+ def extract_message(body)
25
+ return unless body.is_a?(Hash)
26
+
27
+ body["error"] || body["message"] || body.dig("errors", 0, "message")
28
+ end
29
+ end
30
+
31
+ # 400 Bad Request
32
+ class BadRequestError < APIError; end
33
+
34
+ # 401 Unauthorized — invalid credentials
35
+ class AuthenticationError < APIError; end
36
+
37
+ # 403 Forbidden — valid credentials but insufficient permissions
38
+ class AuthorizationError < APIError; end
39
+
40
+ # 404 Not Found
41
+ class NotFoundError < APIError; end
42
+
43
+ # 409 Conflict
44
+ class ConflictError < APIError; end
45
+
46
+ # 422 Unprocessable Entity — validation errors
47
+ class UnprocessableEntityError < APIError
48
+ attr_reader :errors
49
+
50
+ def initialize(message = nil, http_status: nil, http_body: nil, http_headers: nil)
51
+ super
52
+ @errors = http_body.is_a?(Hash) ? (http_body["errors"] || []) : []
53
+ end
54
+ end
55
+
56
+ # 429 Too Many Requests
57
+ class RateLimitError < APIError
58
+ attr_reader :retry_after
59
+
60
+ def initialize(message = nil, http_status: nil, http_body: nil, http_headers: nil)
61
+ super
62
+ @retry_after = http_headers&.[]("retry-after")&.to_i
63
+ end
64
+ end
65
+
66
+ # 5xx Server Errors
67
+ class ServerError < APIError; end
68
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ # Wraps a Gorgias API response hash providing convenient attribute-style
5
+ # access, nested object wrapping, and predicate helpers.
6
+ #
7
+ # @example
8
+ # ticket = Gorgias::Object.new({ "id" => 1, "status" => "open", "customer" => { "email" => "a@b.com" } })
9
+ # ticket.id # => 1
10
+ # ticket.status # => "open"
11
+ # ticket.customer # => #<Gorgias::Object ...>
12
+ # ticket.customer.email # => "a@b.com"
13
+ # ticket[:id] # => 1
14
+ # ticket.to_h # => { "id" => 1, ... }
15
+ class Object
16
+ attr_reader :data
17
+
18
+ def initialize(data = {})
19
+ @data = stringify_keys(data)
20
+ end
21
+
22
+ # Hash-style key access.
23
+ def [](key)
24
+ val = @data[key.to_s]
25
+ coerce(val)
26
+ end
27
+
28
+ def []=(key, value)
29
+ @data[key.to_s] = value
30
+ end
31
+
32
+ # Checks if the key is present.
33
+ def key?(key)
34
+ @data.key?(key.to_s)
35
+ end
36
+
37
+ alias_method :has_key?, :key?
38
+
39
+ # Returns all keys as symbols.
40
+ def keys
41
+ @data.keys.map(&:to_sym)
42
+ end
43
+
44
+ # Returns a plain Ruby Hash with nested objects recursively converted.
45
+ def to_h
46
+ deep_to_h(@data)
47
+ end
48
+
49
+ alias_method :to_hash, :to_h
50
+
51
+ def to_s
52
+ inspect
53
+ end
54
+
55
+ def inspect
56
+ attrs = @data.map { |k, v| "#{k}=#{v.inspect}" }.join(", ")
57
+ "#<#{self.class.name} #{attrs}>"
58
+ end
59
+
60
+ def ==(other)
61
+ case other
62
+ when Gorgias::Object then @data == other.data
63
+ when Hash then @data == stringify_keys(other)
64
+ else false
65
+ end
66
+ end
67
+
68
+ def respond_to_missing?(name, include_private = false)
69
+ key = attribute_key(name)
70
+ @data.key?(key) || super
71
+ end
72
+
73
+ def method_missing(name, *args, &block)
74
+ key = attribute_key(name)
75
+
76
+ if @data.key?(key)
77
+ val = @data[key]
78
+ name.to_s.end_with?("?") ? !!val : coerce(val)
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ # Strip trailing "?" for predicate methods
87
+ def attribute_key(name)
88
+ name.to_s.delete_suffix("?")
89
+ end
90
+
91
+ def coerce(val)
92
+ case val
93
+ when ::Hash then self.class.new(val)
94
+ when ::Array then val.map { |e| e.is_a?(::Hash) ? self.class.new(e) : e }
95
+ else val
96
+ end
97
+ end
98
+
99
+ def stringify_keys(hash)
100
+ hash.transform_keys(&:to_s)
101
+ rescue NoMethodError
102
+ hash
103
+ end
104
+
105
+ def deep_to_h(obj)
106
+ case obj
107
+ when ::Hash then obj.transform_values { |v| deep_to_h(v) }
108
+ when ::Array then obj.map { |v| deep_to_h(v) }
109
+ else obj
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Account < Base
6
+ # Retrieve the account associated with the configured domain.
7
+ #
8
+ # @return [Gorgias::Object]
9
+ def retrieve
10
+ get("/api/account")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Base
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ private
11
+
12
+ def get(path, params = {})
13
+ @client.get(path, params)
14
+ end
15
+
16
+ def post(path, body = {}, headers: {})
17
+ @client.post(path, body, headers: headers)
18
+ end
19
+
20
+ def put(path, body = {})
21
+ @client.put(path, body)
22
+ end
23
+
24
+ def delete(path, body = nil)
25
+ @client.delete(path, body)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Customers < Base
6
+ # Create a new customer.
7
+ #
8
+ # @param params [Hash] Customer attributes:
9
+ # - :name [String] Full name
10
+ # - :email [String] Primary email address
11
+ # - :external_id [String] ID in a foreign system
12
+ # - :language [String] ISO 639-1 language code
13
+ # - :timezone [String] IANA timezone name
14
+ # - :channels [Array<Hash>] Communication channels
15
+ # - :data [Hash] Custom data fields
16
+ # @return [Gorgias::Object] The created customer
17
+ def create(**params)
18
+ post("/api/customers", params)
19
+ end
20
+
21
+ # List customers (paginated, ordered alphabetically by name).
22
+ #
23
+ # @param params [Hash] Filter/pagination options:
24
+ # - :email [String]
25
+ # - :name [String]
26
+ # - :external_id [String]
27
+ # - :language [String]
28
+ # - :timezone [String]
29
+ # - :view_id [Integer]
30
+ # - :cursor [String] Pagination cursor
31
+ # - :limit [Integer] Max objects per page (default 30)
32
+ # @return [Array<Gorgias::Object>]
33
+ def list(**params)
34
+ get("/api/customers", params)
35
+ end
36
+
37
+ # Retrieve a single customer by ID.
38
+ #
39
+ # @param id [Integer, String] Customer ID
40
+ # @return [Gorgias::Object]
41
+ def retrieve(id)
42
+ get("/api/customers/#{id}/")
43
+ end
44
+
45
+ # Update a customer.
46
+ #
47
+ # @param id [Integer, String] Customer ID
48
+ # @param params [Hash] Attributes to update
49
+ # @return [Gorgias::Object]
50
+ def update(id, **params)
51
+ put("/api/customers/#{id}/", params)
52
+ end
53
+
54
+ # Delete a single customer.
55
+ #
56
+ # @param id [Integer, String] Customer ID
57
+ # @return [true]
58
+ def delete(id)
59
+ super("/api/customers/#{id}/")
60
+ end
61
+
62
+ # Bulk-delete multiple customers by ID.
63
+ #
64
+ # @param ids [Array<Integer>] Customer IDs to delete
65
+ # @return [true]
66
+ def delete_many(ids)
67
+ @client.delete("/api/customers", { ids: ids })
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Events < Base
6
+ # List events (paginated).
7
+ #
8
+ # @param params [Hash] Filter/pagination options:
9
+ # - :cursor [String]
10
+ # - :limit [Integer]
11
+ # @return [Array<Gorgias::Object>]
12
+ def list(**params)
13
+ get("/api/events", params)
14
+ end
15
+
16
+ # Retrieve a single event by ID.
17
+ #
18
+ # @param id [Integer, String] Event ID
19
+ # @return [Gorgias::Object]
20
+ def retrieve(id)
21
+ get("/api/events/#{id}/")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Files < Base
6
+ # Upload one or more files.
7
+ #
8
+ # Each key in +files+ becomes the label (filename) of the uploaded file,
9
+ # and the value should be a readable IO object or a file path string.
10
+ #
11
+ # @example
12
+ # client.files.upload("receipt.png" => File.open("/path/to/receipt.png"))
13
+ #
14
+ # @param files [Hash{String => IO, String}] label => file content
15
+ # @return [Array<Gorgias::Object>] Uploaded file objects
16
+ def upload(files)
17
+ payload = files.transform_values do |file|
18
+ next file if file.respond_to?(:read)
19
+
20
+ ::File.open(file, "rb")
21
+ end
22
+
23
+ post("/api/upload", payload, headers: { "Content-Type" => "multipart/form-data" })
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Integrations < Base
6
+ # Create a new integration.
7
+ #
8
+ # @param params [Hash] Integration attributes
9
+ # @return [Gorgias::Object]
10
+ def create(**params)
11
+ post("/api/integrations", params)
12
+ end
13
+
14
+ # List integrations.
15
+ #
16
+ # @param params [Hash] Filter/pagination options
17
+ # @return [Array<Gorgias::Object>]
18
+ def list(**params)
19
+ get("/api/integrations", params)
20
+ end
21
+
22
+ # Retrieve a single integration by ID.
23
+ #
24
+ # @param id [Integer, String]
25
+ # @return [Gorgias::Object]
26
+ def retrieve(id)
27
+ get("/api/integrations/#{id}/")
28
+ end
29
+
30
+ # Update an integration.
31
+ #
32
+ # @param id [Integer, String]
33
+ # @param params [Hash] Attributes to update
34
+ # @return [Gorgias::Object]
35
+ def update(id, **params)
36
+ put("/api/integrations/#{id}/", params)
37
+ end
38
+
39
+ # Delete an integration.
40
+ #
41
+ # @param id [Integer, String]
42
+ # @return [true]
43
+ def delete(id)
44
+ super("/api/integrations/#{id}/")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Jobs < Base
6
+ # Create a new job.
7
+ #
8
+ # @param params [Hash] Job attributes
9
+ # @return [Gorgias::Object]
10
+ def create(**params)
11
+ post("/api/jobs", params)
12
+ end
13
+
14
+ # List jobs.
15
+ #
16
+ # @param params [Hash] Filter/pagination options
17
+ # @return [Array<Gorgias::Object>]
18
+ def list(**params)
19
+ get("/api/jobs", params)
20
+ end
21
+
22
+ # Retrieve a single job by ID.
23
+ #
24
+ # @param id [Integer, String]
25
+ # @return [Gorgias::Object]
26
+ def retrieve(id)
27
+ get("/api/jobs/#{id}/")
28
+ end
29
+
30
+ # Update a job.
31
+ #
32
+ # @param id [Integer, String]
33
+ # @param params [Hash] Attributes to update
34
+ # @return [Gorgias::Object]
35
+ def update(id, **params)
36
+ put("/api/jobs/#{id}/", params)
37
+ end
38
+
39
+ # Cancel a job.
40
+ #
41
+ # @param id [Integer, String]
42
+ # @return [true]
43
+ def cancel(id)
44
+ delete("/api/jobs/#{id}/")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Macros < Base
6
+ # Create a new macro.
7
+ #
8
+ # @param params [Hash] Macro attributes (name, actions, etc.)
9
+ # @return [Gorgias::Object]
10
+ def create(**params)
11
+ post("/api/macros", params)
12
+ end
13
+
14
+ # List macros.
15
+ #
16
+ # @param params [Hash] Filter/pagination options
17
+ # @return [Array<Gorgias::Object>]
18
+ def list(**params)
19
+ get("/api/macros", params)
20
+ end
21
+
22
+ # Retrieve a single macro by ID.
23
+ #
24
+ # @param id [Integer, String]
25
+ # @return [Gorgias::Object]
26
+ def retrieve(id)
27
+ get("/api/macros/#{id}/")
28
+ end
29
+
30
+ # Update a macro.
31
+ #
32
+ # @param id [Integer, String]
33
+ # @param params [Hash] Attributes to update
34
+ # @return [Gorgias::Object]
35
+ def update(id, **params)
36
+ put("/api/macros/#{id}/", params)
37
+ end
38
+
39
+ # Delete a macro.
40
+ #
41
+ # @param id [Integer, String]
42
+ # @return [true]
43
+ def delete(id)
44
+ super("/api/macros/#{id}/")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Rules < Base
6
+ # Create a new rule.
7
+ #
8
+ # @param params [Hash] Rule attributes
9
+ # @return [Gorgias::Object]
10
+ def create(**params)
11
+ post("/api/rules", params)
12
+ end
13
+
14
+ # List rules.
15
+ #
16
+ # @param params [Hash] Filter/pagination options
17
+ # @return [Array<Gorgias::Object>]
18
+ def list(**params)
19
+ get("/api/rules", params)
20
+ end
21
+
22
+ # Retrieve a single rule by ID.
23
+ #
24
+ # @param id [Integer, String]
25
+ # @return [Gorgias::Object]
26
+ def retrieve(id)
27
+ get("/api/rules/#{id}/")
28
+ end
29
+
30
+ # Update a rule.
31
+ #
32
+ # @param id [Integer, String]
33
+ # @param params [Hash] Attributes to update
34
+ # @return [Gorgias::Object]
35
+ def update(id, **params)
36
+ put("/api/rules/#{id}/", params)
37
+ end
38
+
39
+ # Delete a rule.
40
+ #
41
+ # @param id [Integer, String]
42
+ # @return [true]
43
+ def delete(id)
44
+ super("/api/rules/#{id}/")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class SatisfactionSurveys < Base
6
+ # Create a satisfaction survey.
7
+ #
8
+ # @param params [Hash] Survey attributes (ticket_id, etc.)
9
+ # @return [Gorgias::Object]
10
+ def create(**params)
11
+ post("/api/satisfaction-surveys", params)
12
+ end
13
+
14
+ # Retrieve a satisfaction survey by ID.
15
+ #
16
+ # @param id [Integer, String]
17
+ # @return [Gorgias::Object]
18
+ def retrieve(id)
19
+ get("/api/satisfaction-surveys/#{id}/")
20
+ end
21
+
22
+ # Update a satisfaction survey.
23
+ #
24
+ # @param id [Integer, String]
25
+ # @param params [Hash] Attributes to update
26
+ # @return [Gorgias::Object]
27
+ def update(id, **params)
28
+ put("/api/satisfaction-surveys/#{id}/", params)
29
+ end
30
+
31
+ # Accept (submit a response to) a satisfaction survey.
32
+ #
33
+ # @param id [Integer, String]
34
+ # @param params [Hash] Response payload (score, body_text, etc.)
35
+ # @return [Gorgias::Object]
36
+ def accept(id, **params)
37
+ post("/api/satisfaction-surveys/#{id}/accept", params)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Search < Base
6
+ # Search customers.
7
+ #
8
+ # @param params [Hash] Search parameters (query, filters, etc.)
9
+ # @return [Array<Gorgias::Object>]
10
+ def customers(**params)
11
+ post("/api/search/customers", params)
12
+ end
13
+
14
+ # Search tickets.
15
+ #
16
+ # @param params [Hash] Search parameters
17
+ # @return [Array<Gorgias::Object>]
18
+ def tickets(**params)
19
+ post("/api/search/tickets", params)
20
+ end
21
+
22
+ # Search ticket messages.
23
+ #
24
+ # @param params [Hash] Search parameters
25
+ # @return [Array<Gorgias::Object>]
26
+ def ticket_messages(**params)
27
+ post("/api/search/ticket-messages", params)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gorgias
4
+ module Resources
5
+ class Statistics < Base
6
+ # Get customer statistics.
7
+ #
8
+ # @param params [Hash] Query parameters (start_datetime, end_datetime, etc.)
9
+ # @return [Gorgias::Object]
10
+ def customers(**params)
11
+ post("/api/statistics/customers", params)
12
+ end
13
+
14
+ # Get ticket statistics.
15
+ #
16
+ # @param params [Hash] Query parameters (start_datetime, end_datetime, etc.)
17
+ # @return [Gorgias::Object]
18
+ def tickets(**params)
19
+ post("/api/statistics/tickets", params)
20
+ end
21
+
22
+ # Get customer satisfaction statistics.
23
+ #
24
+ # @param params [Hash] Query parameters (start_datetime, end_datetime, etc.)
25
+ # @return [Gorgias::Object]
26
+ def customer_satisfaction(**params)
27
+ post("/api/statistics/customer-satisfaction", params)
28
+ end
29
+ end
30
+ end
31
+ end