Module: Ghostcrawl

Defined in:
lib/ghostcrawl.rb,
lib/ghostcrawl/client.rb,
lib/ghostcrawl/errors.rb,
lib/ghostcrawl/version.rb,
lib/ghostcrawl/error_codes.rb,
lib/ghostcrawl/client.rb

Overview

Mirrors ghostcrawl/errors/codes.json (the single source of truth). Do not hand-diverge the set.

Two channels:

PROBLEM — OUR failure  -> non-2xx application/problem+json
RESULT  — TARGET failure -> HTTP 200 + result_error{code,retryable}

Defined Under Namespace

Modules: ErrorCodes, KiotaParseNodeFix, KiotaWriterFix, ResponseHelper Classes: APIError, AdditionalDataBody, AuthenticationError, Client, CrawlRunsClient, DatasetsClient, GhostcrawlError, InvalidRequestError, KVClient, PaymentRequiredError, ProfilesClient, RateLimitError, RecordingsClient, SchedulesClient, ScrapeError, SessionsClient, StaticTokenProvider, WebhooksClient

Constant Summary collapse

VERSION =
"2.2.3"
DEFAULT_BASE_URL =
"https://api.ghostcrawl.io"

Class Method Summary collapse

Class Method Details

.extract_status(err) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



118
119
120
121
122
# File 'lib/ghostcrawl/errors.rb', line 118

def self.extract_status(err)
  # The Faraday adapter appends ":<code>" to its ApiError message.
  m = err.message.to_s.match(/:(\d{3})\b/)
  m ? m[1].to_i : 0
end

.friendly_message(status, err) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/ghostcrawl/errors.rb', line 125

def self.friendly_message(status, err)
  return "validation error (HTTP 422)" if err.class.name.to_s.include?("HTTPValidationError")

  case status
  when 401, 403 then "authentication failed — check your API key (HTTP #{status})"
  when 402      then "payment required — usage or spend limit reached (HTTP 402)"
  when 400, 422 then "invalid request parameters (HTTP #{status})"
  when 429      then "rate limit reached — retry after a short delay (HTTP 429)"
  when 500..599 then "server error (HTTP #{status})"
  else err.message.to_s
  end
end

.new(token: nil, base_url: nil) ⇒ Client

Convenience constructor.

Parameters:

  • token (String, nil) (defaults to: nil)

    API key

  • base_url (String, nil) (defaults to: nil)

    optional base URL override

Returns:



14
15
16
# File 'lib/ghostcrawl.rb', line 14

def self.new(token: nil, base_url: nil)
  Client.new(token: token, base_url: base_url)
end

.parse_problem_body(body) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Best-effort parse of a problem+json body/message string. Returns the parsed Hash when the text is a JSON object, or an empty Hash otherwise — never raises, so callers can read +["code"]+/+["instance"]+ unconditionally.



108
109
110
111
112
113
114
115
# File 'lib/ghostcrawl/errors.rb', line 108

def self.parse_problem_body(body)
  return {} if body.nil?

  parsed = JSON.parse(body.to_s)
  parsed.is_a?(Hash) ? parsed : {}
rescue StandardError
  {}
end

.raise_translated(err) ⇒ GhostcrawlError

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Translates a low-level transport exception (raised by the Kiota Faraday adapter) into a typed GhostcrawlError. The adapter raises a bare MicrosoftKiotaAbstractions::ApiError whose message embeds the HTTP status code (e.g. "...status code...:402"), and a typed HTTPValidationError for 422 responses. Neither subclasses GhostcrawlError, so the documented rescue Ghostcrawl::GhostcrawlError contract would otherwise never fire.

Parameters:

  • err (StandardError)

    the original transport exception

Returns:

Raises:

  • (klass)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/ghostcrawl/errors.rb', line 71

def self.raise_translated(err)
  # A typed HTTPValidationError is the adapter's 422 mapping — it carries no
  # ":<code>" in its message, so key off the class name.
  validation = err.class.name.to_s.include?("HTTPValidationError")
  status = validation ? 422 : extract_status(err)
  body = err.message

  # The problem+json body is frequently dropped by the Kiota runtime before it
  # raises (no spec error-mapping for the status). Try to read a canonical
  # +code+ and +instance+ (request id) from whatever message/body text exists;
  # otherwise fall back to a status -> code map so the caller still gets a
  # canonical, documented code + retryability keyed off the status line.
  problem = parse_problem_body(body)
  code = problem["code"] || ErrorCodes.code_for_status(status)
  request_id = problem["instance"]
  retryable = ErrorCodes::RETRYABLE.fetch(code, false)

  # Keep the existing status -> class selection for back-compat; just enrich
  # the raised instance with the canonical code/retryable/request_id.
  klass =
    case status
    when 401, 403 then AuthenticationError
    when 402      then PaymentRequiredError
    when 400, 422 then InvalidRequestError
    when 429      then RateLimitError
    when 500..599 then APIError
    else GhostcrawlError
    end
  raise klass.new(friendly_message(status, err), status_code: status, body: body,
                                                 code: code, retryable: retryable,
                                                 request_id: request_id)
end